Reputation: 3318
This is the code of an NBack test. When I execute the code as it is, it works. The sequence that appears on the screen is
self.sequence = np.array([0,0,0,1,1,1,6,6,6,0,1,2,3,4,5,6,7,8,0,1])
But I have a function that generates sequences that I want to use instead of that array. It is ZBack.py
When I call my ZBack.py self.sequence = generateZBackSequence(20, 5)
instead of the array, the program does not work.
It outputs this error:
Exception in thread Thread-14:
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "C:\ProgramData\Anaconda3\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "C:/Users/Python_Scripts/N-back.py", line 198, in targetTask
for i in range(self.sequence.shape[0]):
AttributeError: 'NBack' object has no attribute 'sequence'
What am I doing wrong? Why does it tell me the attribute 'sequence' does not exist? I have already checked indentation. My ZBack.py does return an array too.
import tkinter as tk
from tkinter import Tk
from threading import Thread
from keylogger.KeyLogger import KeyLogger
from time import sleep
from nBackTools.NBackTools import *
from nBackTools.ZBack import *
from random import randint
import numpy as np
import csv
def _create_circle(self, x, y, r, **kwargs):
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
class NBack:
def __init__(self, master):
##Title of the window
self.master = master
master.title("N-Back")
##It measures the screen size (width x height + x + y)
##The opened window will be based on the screen size
master.geometry("{0}x{1}-0+0".format(master.winfo_screenwidth(), master.winfo_screenheight()))
self.canvas = tk.Canvas(master, width=master.winfo_screenwidth(), height=master.winfo_screenheight(), \
borderwidth=0, highlightthickness=0, bg="grey")
self.canvasWidth = master.winfo_screenwidth()
self.canvasHeight = master.winfo_screenheight()
self.createLines()
self.createText()
self.canvas.grid()
self.t = Thread(target=self.targetTask,)
self.t.daemon = True
self.t.start()
## Positions of the circles ("stims")
## - -
## 0 - 1 - 2
## ------------
## 3 - 4 - 5
## ------------
## 6 - 7 - 8
## - -
self.positions = np.array([[(self.canvasWidth/2)-130, (self.canvasHeight/2)-130],\
[(self.canvasWidth/2), (self.canvasHeight/2)-130],\
[(self.canvasWidth/2)+130, (self.canvasHeight/2)-130],\
[(self.canvasWidth/2)-130, (self.canvasHeight/2)],\
[(self.canvasWidth/2), (self.canvasHeight/2)],\
[(self.canvasWidth/2)+130, (self.canvasHeight/2)],\
[(self.canvasWidth/2)-130, (self.canvasHeight/2)+130], \
[(self.canvasWidth/2), (self.canvasHeight/2)+130], \
[(self.canvasWidth/2)+130, (self.canvasHeight/2)+130]])
###############################################
###############################################
#THIS IS THE PROBLEM!!!
#self.sequence = generateZBackSequence(20, 5)
self.sequence = np.array([0,0,0,1,1,1,6,6,6,0,1,2,3,4,5,6,7,8,0,1])
###############################################
###############################################
"""
ADDING LINES TO THE SCREEN
"""
def createLines(self, linewidth = 5):
#Vertical 1
self.canvas.create_line((self.canvasWidth/2)-65, (self.canvasHeight/2)-65-130, \
(self.canvasWidth/2)-65, (self.canvasHeight/2)+65+130, width= linewidth )
#Vertical 2
self.canvas.create_line((self.canvasWidth/2)+65, (self.canvasHeight/2)-65-130, \
(self.canvasWidth/2)+65, (self.canvasHeight/2)+65+130, width= linewidth )
#Horizontal1
self.canvas.create_line((self.canvasWidth/2)-65-130, (self.canvasHeight/2)-65, \
(self.canvasWidth/2)+65+130, (self.canvasHeight/2)-65, width= linewidth )
#Horizontal2
self.canvas.create_line((self.canvasWidth/2)-65-130, (self.canvasHeight/2)+65, \
(self.canvasWidth/2)+65+130, (self.canvasHeight/2)+65, width= linewidth )
"""
ADDING TEXT TO THE SCREEN
"""
def createText(self):
self.canvas.create_text((self.canvasWidth/4), (self.canvasHeight - self.canvasHeight/4), text="A=YES", font=( None, 20))
self.canvas.create_text((self.canvasWidth/4), (self.canvasHeight - self.canvasHeight/4)+50, text="S=NO", font=( None, 20))
"""
CIRCLES APPEAR AND DISAPPEAR
"""
def targetTask(self):
thisnb = randint(75,900*2)
targetFile = open(str(thisnb)+"_targets.csv",'w', newline='')
writer = csv.writer(targetFile)
##Loops from 0 to the Number of circles that will appear (default=20)
for i in range(self.sequence.shape[0]):
posX = self.positions[self.sequence[i]][0]
posY = self.positions[self.sequence[i]][1]
print(": (%d,%d)" % (posX,posY))
##Records the coordenates in an excel file
writer.writerow([posX,posY])
## How many seconds each circle will appear
circle = self.canvas.create_circle(posX, posY, 55, fill="black", outline="#000", width=1)
sleep(1)
## Delete circle
## How many seconds circle dissapears
self.canvas.delete(circle)
sleep(2)
sleep(1)
print('All done')
targetFile.close()
self.master.destroy()
root = Tk()
my_gui = NBack(root)
root.mainloop()
This is the ZBack.py:
import numpy as np
import random
def generateZBackSequence(nbTrial, nbOfYes):
##all possible positions of the "stims"
allStims = [0,1,2,3,4,5,6,7,8]
##sequence is an array of -1s and has the size of nbTrial (default=20)
sequence = np.zeros((nbTrial,))-1
##security: in case the required "yeses" are too many
if nbOfYes > nbTrial:
print('generateZBackSequence: invalid nb of Yeses')
return None
##Pick randomly 1 stim and repeat it (default=5)
yeses = np.repeat(random.sample(allStims, 1),nbOfYes)
print("yeses:", yeses, '\n')
##Making the first value in the sequence = value in yeses
sequence[0]=yeses[0]
#print("sequence:", sequence, '\n')
##Contains the "stims" that were not randomly sampled
newStims = [x for x in allStims if x not in yeses]
#print("newStims:", newStims, '\n')
"""
STEP 1 - impose the yeses
"""
##nbOfYes-1, because there is already a "yeses" in sequence[0]
for i in range(nbOfYes-1):
idxValid = False
while idxValid==False:
##select index randomly, from 1 to 19
idx = random.randint(1, nbTrial-1)
if sequence[idx]==-1:
idxValid = True
print("idx:", idx, '\n')
##insert yeses in the random index
sequence[idx] = yeses[i]
#print("sequence_idx:", sequence)
"""
STEP 2 - fill in the sequence
"""
##Makes a "for" from 0 to 19
for i in range(sequence.shape[0]):
if sequence[i]==-1:
stimValid = False
##Search for -1s
while stimValid==False:
##Takes a random sample "stim" which is different from 'yeses'
stimID = random.sample(newStims, 1)
##stimID is a list of 1 element, sequence is an array of 20 elements
sequence[i]=stimID[0]
stimValid = True
else:
stimValid = True
return sequence
def _test():
print(generateZBackSequence(20,5))
print("")
print(np.arange(0,20,1))
pass
if __name__ == "__main__":
_test()
Upvotes: 0
Views: 50
Reputation: 722
It seems you are calling the method targetTask()
within NBack
's constructor, in the lines below
self.t = Thread(target=self.targetTask,)
self.t.daemon = True
self.t.start()
before it has the chance to define the sequence
attribute.
It would be fixed by defining sequence
and anything else you need before you call targetTask()
.
The constructor would look like this:
def __init__(self, master):
##Title of the window
self.master = master
master.title("N-Back")
##It measures the screen size (width x height + x + y)
##The opened window will be based on the screen size
master.geometry("{0}x{1}-0+0".format(master.winfo_screenwidth(), master.winfo_screenheight()))
self.canvas = tk.Canvas(master, width=master.winfo_screenwidth(), height=master.winfo_screenheight(), \
borderwidth=0, highlightthickness=0, bg="grey")
self.canvasWidth = master.winfo_screenwidth()
self.canvasHeight = master.winfo_screenheight()
self.createLines()
self.createText()
self.canvas.grid()
# Notice we define self.sequence before calling self.targetTask
self.sequence = generateZBackSequence(20, 5)
## Positions of the circles ("stims")
## - -
## 0 - 1 - 2
## ------------
## 3 - 4 - 5
## ------------
## 6 - 7 - 8
## - -
self.positions = np.array([[(self.canvasWidth/2)-130, (self.canvasHeight/2)-130],\
[(self.canvasWidth/2), (self.canvasHeight/2)-130],\
[(self.canvasWidth/2)+130, (self.canvasHeight/2)-130],\
[(self.canvasWidth/2)-130, (self.canvasHeight/2)],\
[(self.canvasWidth/2), (self.canvasHeight/2)],\
[(self.canvasWidth/2)+130, (self.canvasHeight/2)],\
[(self.canvasWidth/2)-130, (self.canvasHeight/2)+130], \
[(self.canvasWidth/2), (self.canvasHeight/2)+130], \
[(self.canvasWidth/2)+130, (self.canvasHeight/2)+130]])
self.t = Thread(target=self.targetTask,)
self.t.daemon = True
self.t.start()
Upvotes: 1