0

I am using Tkinter to build a very simple trouchscreen-based experiment on the raspberry pie. I want to display a stimulus on the screen, constantly (so the list line of my code is win.mainloop(). At every click/touch of the stimulus, the stimulus should change position. I generate the stimulus positions with basic trigonometry by doing:

distance = 0.2 
angle_range = list(range(0, 360, 45))
xc = (math.sin(angle_range[0] * 0.0174) * distance) + 0.5
yc = (math.cos(angle_range[0] * 0.0174) * distance) + 0.5

I then use xc and yc to position the stimulus on the screen (in this case it will be treated as a button by tkinter):

photo = PhotoImage(file="FaceStimulus.png")
Stimulus = Button(win, command=changePosition)
Stimulus.config(image=photo)
Stimulus.place(relx=xc, rely=yc)

And finally the function associated with the button

def changePosition():
    print("Stimulus pressed")
    angle_range = random.shuffle(angle_range)

In the function changePosition I ask for a shuffle of the positions list, which doesn't actually happen. The stimulus keeps always its initial position.

I am probably missing some very basic logic of python programming, sorry if this is a trivial question. I looked a bit online before writing here and I find some methods, but every time I try to implement them I run in the same logical error: how can I define over and over a variable from a function?

Ethan Field
  • 4,646
  • 3
  • 22
  • 43
antcolony
  • 83
  • 1
  • 12
  • Are you looking to have the stimulus show up in any random position on the screen or are you looking to have it appear based on some predefined algorithm? @antscolony – Ethan Field Sep 14 '17 at 15:09
  • I would like the button to show up in any random position calculated with an angle extracted from the list (range(0, 360, 45)) – antcolony Sep 14 '17 at 15:18

2 Answers2

0

Once you declare xc = something, then xc is locked down and doesn’t change unless you go in and change it. Changing the list it’s based on does nothing. Try updating the variables within your function, not just the list.

This code probably doesn’t work, but hopefully helps you figure out what to do.

def changePosition():
    print("Stimulus pressed")
    random.shuffle(angle_range)
    xc = (math.sin(angle_range[0] * 0.0174) * distance) + 0.5
    yc = (math.cos(angle_range[0] * 0.0174) * distance) + 0.5
    Stimulus.place(relx=xc, rely=yc)
ddg
  • 1,090
  • 1
  • 6
  • 17
  • I understand your approach, thanks! However when I try to implement your code I am back to where I was before. I need a starting value of xc and yc otherwise at trial 1 (at the initialization) the stimulus does not show up basically. I could check if this is trial 1 by running a counter of the clicks on the stimulus so that every click starts a new trial. I will try that.. – antcolony Sep 14 '17 at 14:36
0

See my explained snippet below:

from tkinter import *
import math
import random

class App:
  def __init__(self, root):
     self.root = root
     self.distance = 0.2 
     self.angle_range = list(range(0, 360, 45))

     #this is a placeholder for your stimulus
     self.label = Label(self.root, text="Image Placeholder")

     self.command() #calls the command to generate the first position
     self.label.bind("<Button-1>", self.command) #creates a callback whenever the label is clicked with mousebutton 1
  def command(self, *args):
     #i'll explain this below the snippet
     var = random.choice(self.angle_range)
     self.xc = (math.sin(var * 0.0174) * self.distance) + 0.5
     self.yc = (math.cos(var * 0.0174) * self.distance) + 0.5
     self.label.place(relx=self.xc, rely=self.yc)

root = Tk()
App(root)
root.mainloop()

So it looks like the problem you were having before was that you were only ever calling the 0th element in your list. To remedy this I have added on self.angle_range[random.randrange(len(self.angle_range))].

Let's break this down.

self.angle_range[n] is your list, containing all of the elements we propagated earlier. So we want a random element from this list? Fine.

So we use random.choice(self.angle_range) which will grab a random element.

This returns a random element from the list self.angle_range, giving us a random coordinate based on the list we provided.

Ethan Field
  • 4,646
  • 3
  • 22
  • 43
  • This works as expected. Also very clear explanation. I noticed the way of coding differs significantly from my own (mine is very naive indeed). Would you know where I can find more information about this way of structuring the script? Thanks – antcolony Sep 14 '17 at 15:38
  • This is broken; you are choosing a potentially different random angle for computing the x and the y coordinates, so the point isn't constrained to be on the desired circle. `random.choice(self.angle_range)` would be a better way of choosing an element from a list, anyway. – jasonharper Sep 14 '17 at 15:39
  • This is `object-orientated programming`. It's one of the easiest ways to program anything complex in `tkinter` (Obviously this is opinion based). This question is probably better at explaining it that I am: https://stackoverflow.com/questions/17466561/best-way-to-structure-a-tkinter-application – Ethan Field Sep 14 '17 at 15:39
  • @jasonharper "I would like the button to show up in any random position calculated with an angle extracted from the list (range(0, 360, 45))" I'm picking a random element from the list as the user stated, why is this wrong? Amended to use the better random selection. – Ethan Field Sep 14 '17 at 15:42
  • You are picking a random angle from the list TWICE. With x and y being calculated separately, you generate a point that could be anywhere within a square area, rather than only on the boundary of a circle with the given radius. – jasonharper Sep 14 '17 at 15:56
  • @jasonharper This information wasn't given in the OP's question, amended. Also, does this not mean that XC and XY are the same value? What's the point in having two variables for it if they're identical? – Ethan Field Sep 14 '17 at 16:02