I am working on a simple project and I need a little help. I have created a program that draws circles on a canvas. The circles are blue, and the method flash() takes a random int count, and will light up a circle and change its color to yellow. The problem is I want the function to continue to execute every second or so, because I want to eventually be able to let the user of the program click onto the circle that is lit, and have the program respond with dialog stating Correct/Incorrect if the user got it right. As of right now I can only get it to light one circle and that is it. I tried using a thread and set it to every 2 seconds, but that didn't work or maybe I didn't do something correctly. Any help would be appreciated.

from graphics import *
import tkinter as tk
import threading
import random
class App():
    def __init__(self):
        self.win = GraphWin('Demo2', 400, 300) # give title and dimensions
        count = random.randint(1,9)


        self.flash(count)
        t = threading.Timer(2.0,flash)
        t.start()
    def flash(self,count):
        circle1 = Circle(Point(50,30), 25) # set center and radius
        circle2 = Circle(Point(110,30), 25)
        circle3 = Circle(Point(170,30),25)
        circle4 = Circle(Point(50,90),25)
        circle5 = Circle(Point(110,90),25)
        circle6 = Circle(Point(170,90),25)
        circle7 = Circle(Point(50,150),25)
        circle8 = Circle(Point(110,150),25)
        circle9 = Circle(Point(170,150),25)
        circle1.setFill("blue")
        circle1.draw(self.win)
        circle2.setFill("blue")
        circle2.draw(self.win)
        circle3.setFill("blue")
        circle3.draw(self.win)
        circle4.setFill("blue")
        circle4.draw(self.win)
        circle5.setFill("blue") 
        circle5.draw(self.win)
        circle6.setFill("blue")
        circle6.draw(self.win)
        circle7.setFill("blue")
        circle7.draw(self.win)
        circle8.setFill("blue")
        circle8.draw(self.win)
        circle9.setFill("blue")
        circle9.draw(self.win)
        if count==1:
            circle1.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 29 and mouseClick.y <= 31 and mouseClick.x >= 49 and mouseClick.x <= 51:
             print("Correct")
            else:
                print("Incorrect")
        elif count==2:
            circle2.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 29 and mouseClick.y <= 31 and mouseClick.x >= 109 and mouseClick.x <= 111:
                print("Correct")
            else:
                print("Incorrect")
        elif count==3:
            circle1.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 29 and mouseClick.y <= 31 and mouseClick.x >= 169 and mouseClick.x <= 171:
                print("Correct")
            else:
                print("Incorrect")

        elif count==4:
            circle4.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 89 and mouseClick.y <= 91 and mouseClick.x >= 49 and mouseClick.x <= 51:
                print("Correct")
            else:
                print("Incorrect")
        elif count==5:
            circle5.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 89 and mouseClick.y <= 91 and mouseClick.x >= 109 and mouseClick.x <= 111:
                print("Correct")
            else:
                print("Incorrect")
        elif count==6:
            circle6.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 89 and mouseClick.y <= 91 and mouseClick.x >= 169 and mouseClick.x <= 171:
                print("Correct")
            else:
                print("Incorrect")
        elif count==7:
            circle7.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 149 and mouseClick.y <= 151 and mouseClick.x >= 49 and mouseClick.x <= 51:
                print("Correct")
            else:
                print("Incorrect")
        elif count==8:
            circle8.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 149 and mouseClick.y <= 151 and mouseClick.x >= 109 and mouseClick.x <= 111:
                print("Correct")
            else:
                print("Incorrect")
        else:
            circle9.setFill("yellow")
            mouseClick = self.win.getMouse()
            if mouseClick.y >= 149 and mouseClick.y <= 151 and mouseClick.x >= 169 and mouseClick.x <= 171:
                print("Correct")
            else:
                print("Incorrect")


if __name__ == "__main__":
    app = App()
    app.mainloop()

Setting aside the timing issue, I would recommend storing the Circle data in a tuple, which should simplify the logic of the program significantly:

    def flash(self,count):
        diameter = 25
        centers = ((50,30),  (110,30),  (170,30),  (50,90), (110,90), 
                   (170,90),  (50,150), (110,150), (170,150))

        circles = list()
        for point in centers:
            c = Circle(Point(point), diameter)
            circles.append(c)
            c.setFill("blue")
            c.draw(self.win)

        circles[count].setFill("yellow")
        mouseClick = self.win.getMouse()
        leftX  = centers[count][0] - diameter
        rightX = centers[count][0] + diameter
        upperY = centers[count][1] - diameter
        lowerY = centers[count][1] + diameter
        if (upperY < mouseClick.y < lowerY) and (leftX < mouseClick.x < rightX):
             print("Correct")
        else:
             print("Incorrect")

I know this doesn't directly help your problem, but it should simplify the code overall.

Edited 2 Years Ago by Schol-R-LEA

Getting back to the main question, the problem is that a Timer runs exactly once, then exits when the runnable function has finished. What you actually seem to want is a Thread that loops on a time.sleep() call.

Edited 2 Years Ago by Schol-R-LEA

I have actually already tried the time.sleep() method already. It didn't work, and someone on another thread stated that for GUI applications, you don't wanna use the sleep method because it will do just that-put your program to sleep. Which means it won't respond to user interactions and events. Have you tried running my code already using this method and did it work?

I wasn't able to test your code at all, actually, as I don't know where the graphics package was from. I was able to find it eventually, but I haven't tested it with that, no.

Yea see I have used the suggestion that you posted, but it didn't work. I will try it again but I thought you had already ran the code since you made the suggestion...

I think most programmers will tell you that the Graphics module is more trouble that it is worth. Here is similar with Tkinter and using "after" for the timing. You can easily get events, like mouse clicks, in Tkinter also Click Here

import tkinter as tk
import random

class App():
    def __init__(self):
        self.root = tk.Tk()
        self.cv = tk.Canvas(self.root, width=600, height=300) 
        self.cv.grid()

        self.start_y=30
        self.num_runs = 1
        self.flash()

        self.root.mainloop()

    def flash(self,):
        start_y = self.start_y
        start_x = 50
        color = random.choice(["blue", "yellow", "orange", "green", "white"])
        for ctr in range(random.randint(1,9)):
            self.cv.create_oval(start_x, start_y, start_x+30, start_y+30,
                                fill=color)            
            start_x += 50
        self.start_y += 50

        if self.num_runs < 5:
            self.root.after(1000, self.flash)
            self.num_runs += 1
            print self.num_runs

if __name__ == "__main__":
    app = App()

Edited 2 Years Ago by woooee

This is the best I've been able to come up with; unfortunately, it does not exit correctly.

from graphics import *
import tkinter as tk
import threading
import random

class App():
    def __init__(self):
        self.win = GraphWin('Demo2', 400, 300) # give title and dimensions
        self.th = threading.Thread(target=self.FlashThread, daemon=False)

    def FlashThread(self):
        while not self.win.isClosed():
            count = random.randint(0, 8)
            t = threading.Timer(2.0, self.flash, [count])
            t.start()
            t.join()

    def flash(self, count):
        try:
            diameter = 25
            centers = ((50,30),  (110,30), (170,30),  (50,90), (110,90), 
                       (170,90), (50,150), (110,150), (170,150))

            circles = list()
            for point in centers:
                c = Circle(Point(point[0], point[1]), diameter)
                circles.append(c)
                c.setFill("blue")
                c.draw(self.win)

            circles[count].setFill("yellow")
            mouseClick = self.win.getMouse()
            leftX  = centers[count][0] - diameter
            rightX = centers[count][0] + diameter
            upperY = centers[count][1] - diameter
            lowerY = centers[count][1] + diameter
            if (upperY < mouseClick.y < lowerY) and (leftX < mouseClick.x < rightX):
                 print("Correct")
            else:
                 print("Incorrect")
        except:
            self.win.exit(0)


if __name__ == "__main__":
    try:
        app = App()
        app.th.start()
        app.win.mainloop()
        app.th.join()
    finally:
        app.th.close()
        app.close()
This question has already been answered. Start a new discussion instead.