"AttributeError" when using PsychoPy and threading

I’m trying to create an application for BCI experiments. To make sure things do not get delayed, I’m trying to implement all the steps in threads. My first simple PsychoPy program worked well, but as soon as I tried to start it from within a thread it crashes giving me the error “AttributeError: ‘NoneType’ object has no attribute ‘close’”:

pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "C:\Users\robotics\Documents\gitDocuments\SSVEP_EyeGaze_py\FlickeringBoxes-2020-11-24.py", line 29, in run
    FlickerStart(self.position)
  File "C:\Users\robotics\Documents\gitDocuments\SSVEP_EyeGaze_py\FlickeringBoxes-2020-11-24.py", line 33, in FlickerStart
    win = visual.Window([W,H], position, monitor = 'SSVEP Paradigm', color = 'black')
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\psychopy\visual\window.py", line 434, in __init__
    *args, **kwargs)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\psychopy\visual\backends\__init__.py", line 32, in getBackend
    return Backend(win, *args, **kwargs)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\psychopy\visual\backends\pygletbackend.py", line 145, in __init__
    validConfigs = thisScreen.get_matching_configs(config)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\pyglet\canvas\win32.py", line 68, in get_matching_configs
    configs = template.match(canvas)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\pyglet\gl\win32.py", line 59, in match
    return self._get_arb_pixel_format_matching_configs(canvas)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\pyglet\gl\win32.py", line 130, in _get_arb_pixel_format_matching_configs
    wglext_arb.wglChoosePixelFormatARB(canvas.hdc, attrs, None, nformats, pformats, nformats)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\pyglet\gl\lib_wgl.py", line 107, in __call__
    return self.func(*args, **kwargs)
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\pyglet\gl\lib.py", line 64, in MissingFunction
    raise MissingFunctionException(name, requires, suggestions)
pyglet.gl.lib.MissingFunctionException: wglChoosePixelFormatARB is not exported by the available OpenGL driver.  ARB_pixel_format is required for this functionality.

Exception ignored in: <bound method Window.__del__ of <psychopy.visual.window.Window object at 0x0000000002A1CFD0>>
Traceback (most recent call last):
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\psychopy\visual\window.py", line 577, in __del__
    self.close()
  File "C:\Users\robotics\AppData\Local\Continuum\anaconda3\envs\psychopy\lib\site-packages\psychopy\visual\window.py", line 2196, in close
    self.backend.close()  # moved here, dereferencing the window prevents
AttributeError: 'NoneType' object has no attribute 'close'
1.7421 	WARNING 	Monitor specification not found. Creating a temporary one...

Is there a proper way to implement visual.Window and threading? See below my code rough structure:

from psychopy import core, visual
import threading

class FlickThread(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)

	def run(self):
		FlickerStart()

def FlickerStart():

	#Create the Window
	win = visual.Window([850,700], [480,180], monitor = 'SSVEP Paradigm', color = 'black')
	win.flip()
	core.wait(1.0)

	message = visual.TextStim(win, text = 'Flickering routine\n\nReady?')
	message.draw()
	win.flip()
	core.wait(1.5)
	
	#Some flashing

	win.close()
	core.quit()

FlickTh = FlickThread()

if __name__ == '__main__':
	FlickTh.start()
	FlickTh.join()

Observation:
I tried updating the drives for my Graphics card, like suggested here but it didn’t seem to cause any effect.
Also, here are my computer specs:

Component Details Subscore Base score
Processor Intel® Core™ i5-2400 CPU @ 3.10GHz 7.4 5.1
Determined by lowest subscore
Memory (RAM) 8.00 GB 7.6
Graphics AMD Radeon HD 6450 5.1
Gaming graphics 4842 MB Total available graphics memory 6.3
Primary hard disk 733GB Free (931GB Total) 5.9
Graphics
Display adapter type AMD RADEON HD 6450
Total available graphics memory 4842 MB
Dedicated graphics memory 1024 MB
Dedicated system memory 0 MB
Shared system memory 3818 MB
Display adapter driver version 15.200.1062.1004
Primary monitor resolution 1920x1080
DirectX version DirectX 10

Hi everyone,

I had exactly the same issue and could not figure out what I was doing wrong. I was lucky enough that a colleague helped me.

I am posting here the solution in case anybody else is struggling :slight_smile:

This is a function to start a thread to move an image called img.png…


import time
from multiprocessing import Process, Value
from typing import Tuple 
from psychopy.visual import Window, ImageStim

def init_window() -Tuple[Window, ImageStim]:
    fname = "/home/img.png" #add any img.png to that path
    win = Window(
     [800, 600], monitor="testMonitor", units="deg", color=(-1, -1, -1)
    )
    img = ImageStim(win, image=fname, size=6, pos=[-7, 0])
    
    img.draw()
    win.flip()

    return win, img

def run_window(speed: Value,stop: Value,win: Window,img: ImageStim):
    while True:
       if stop.value == 1:
           break

       img.ori += speed.value
       img.draw()
       win.flip()


def main():
    win, img = init_window()
    speed = Value("i", 0) #to animate the png, and changing the speed value
    stop = Value("i", 2) #to stop the run_window and update the value of speed
   
    #start threading
    process = Process(target=run_window, args=(speed, stop, win, lwheel, rwheel))
    process.start()

    start = time.time()
    time.sleep(0.5) #just a test here keeping the speed value for 0.5 second
    while True:
        with speed.get_lock():
            speed.value += 1 #changing the speed value, incrementing of +1
        time.sleep(0.5) #just a test here keeping the NEW speed value for 0.5 second

        if time.time() - start >= 5:
            with stop.get_lock():
                stop.value = 1 #stopping the thread with that stop.value
            break #breaking the while loop
    process.join(2) #stop the thread
    if process.is_alive():
        process.kill() #to properly kill all the process

if __name__ == "__main__": 
    main()

Hello Matheus, Did you by any chance manage to figure out the issue? I am currently running on the same issue.