Aizzaac
Aizzaac

Reputation: 3318

How to make the program work when calling a function instead of using a numpy array?

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

Answers (1)

Pasa
Pasa

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

Related Questions