How to pause during a callback using python 3.6? - python

I am relatively new to python and I am programming a computer player for Othello. I am using the callback method to find the position of the mouse click on the board. I need to be able to pause halfway through the callback so that the player's move is shown followed by the computer's. However, for now, both moves happen together so the player cannot see the result of his/her move. When I tried the time.sleep() method it just delayed the execution of the whole callback.
This is a simplified version of my code:
from tkinter import *
import time
root = Tk()
root.configure(background="black")
canvas=Canvas(root, bg = "black", height = 708, width = 1280)
def callback(event):
if event.y < 350:
canvas.create_rectangle(500,234,780,334,fill="#004800",width=0)
time.sleep(2)
canvas.create_rectangle(500,374,780,474,fill="#004800",width=0)
else:
canvas.create_rectangle(500,374,780,474,fill="#004800",width=0)
time.sleep(2)
canvas.create_rectangle(500,234,780,334,fill="#004800",width=0)
canvas.bind("<Button-1>", callback)
canvas.pack(fill=BOTH, expand=1, pady=0, padx=0)
root.mainloop()

Both moves seem to happen simultaneously because the canvas content is updated only after time.sleep finishes. To see the first move separately, you need to force the canvas to update before the pause with root.update_idletasks().
By the way, you don't have to import the module time to pause, you can use root.after(2000) (the time is in ms) instead.
from tkinter import *
root = Tk()
root.configure(background="black")
canvas = Canvas(root, bg = "black", height = 708, width = 1280)
def callback(event):
if event.y < 350:
canvas.create_rectangle(500,234,780,334,fill="red",width=0)
root.update_idletasks()
root.after(2000)
canvas.create_rectangle(500,374,780,474,fill="#004800",width=0)
else:
canvas.create_rectangle(500,374,780,474,fill="red",width=0)
root.update_idletasks()
root.after(2000)
canvas.create_rectangle(500,234,780,334,fill="#004800",width=0)
canvas.bind("<Button-1>", callback)
canvas.pack(fill=BOTH, expand=1, pady=0, padx=0)
root.mainloop()

Related

Using Tkinter to draw and track a mouse pointer

im still learning to use Python and Tkinter.
I have created a bit of code which (i thought) should create a canvas with 2 dots on and afterwards continuously printers the position of the mouse curser
from tkinter import *
from win32 import win32gui
win = Tk()
def mouse_pos():
flags, hcursor, (x, y) = win32gui.GetCursorInfo()
return {"x": x, "y": y}
win.geometry("1500x900")
win.configure(background="black")
g_circle = Canvas(win, width=100, height=100, bg="black", bd=1, highlightthickness=0)
g_circle.place(x=100, y=100, in_=win)
g_circle.create_oval(50, 50, 100, 100, fill="green", offset="200,200", outline="white")
b_circle = Canvas(win, width=100, height=100, bg="black", bd=1, highlightthickness=0)
b_circle.place(x=1300, y=700, in_=win)
b_circle.create_oval(50, 50, 100, 100, fill="blue", outline="white")
while True:
print(mouse_pos())
win.mainloop()
I know there is an infinite loop but i am just testing it for now.
This issue is that when i run this code a TK window opens of the canvas with 2 circles and then a cmd displays an single value for x and y coordinate in text. The coordinates do not continue to update unless i close the TK window and i dont know why.
Ill post a screenshot in hopes it helps.
Any help is appreciated.
win.mainloop() will block the while loop until the main window is closed.
You can use .after() to replace the while loop:
...
def mouse_pos():
# no need to use external module to get the mouse position
#flags, hcursor, (x, y) = win32gui.GetCursorInfo()
x, y = win.winfo_pointerxy()
print({"x": x, "y": y})
# run mouse_pos() again 10 microseconds later
win.after(10, mouse_pos)
...
''' don't need the while loop
while True:
print(mouse_pos())
win.mainloop()
'''
# start the "after loop"
mouse_pos()
win.mainloop()

Changing the color of Tkinter canvas after set period of time

I'm trying to set a Tkinter canvas to red/green for one second, then back to white afterward. However, despite the fact that the code setting the canvas to red/green precedes the code reverting back to white, the window doesn't reflect the initial color change. I understand that by calling .after, the program freezes until the specified duration is over, but I don't understand why it doesn't change to red or green before freezing.
if is_correct:
self.canvas.config(bg="green")
else:
self.canvas.config(bg="red")
self.window.after(1000, self.canvas.config(bg="white"))
Refer to this simple program.
from tkinter import *
root=Tk()
def change_bg():
canvas.config(bg="red")
root.after(1000,lambda: canvas.config(bg="white"))
canvas=Canvas(root,bg="white")
canvas.pack()
root.after(1000,change_bg)
root.mainloop()
from tkinter import *
import time
def change_color():
can.config(bg="red")
can.update()
change_color2()
def change_color2():
time.sleep(1)
can.config(bg="white")
root = Tk()
root.geometry("500x500")
can = Canvas(root, bg="white", height=450, width=500)
can.pack()
Button(root, text="Change color for 1 sec", command=change_color).pack()
root.mainloop()
You can refer to this code

Tkinter opens second GUI first

I'm trying to build a login screen for practice and I'm having trouble with it. The general idea is that pressing the Login button closes the current window and opens another in a separate file. However, when I run the main file it opens the GUI window created in the second file. I'm not sure what would be causing it to do this.
import tkinter as tk
import loginEntry
HEIGHT = 200
WIDTH = 500
def login_function():
root.destroy()
loginEntry.NewScreen()
def register_function():
print("Register!")
root = tk.Tk()
root.title("Login Screen")
root.resizable(False, False)
canvas = tk.Canvas(root, height = HEIGHT, width = WIDTH)
canvas.pack()
frame = tk.Frame(root, bg='grey')
frame.place(relx=0.1, rely=0.25, relwidth=0.8, relheight=0.5)
login = tk.Button(frame, text="Login", command=login_function)
login.place(relx=0.05, rely=0.25, relwidth=0.425, relheight=0.5,)
register = tk.Button(frame, text="Register", command=register_function)
register.place(relx=0.525, rely=0.25, relwidth=0.425, relheight=0.5,)
introduction = tk.Label(root, text="Hello and welcome to DogNet, please login below.", font='bold 12')
introduction.place(relx=0.5, anchor='center', rely=0.1)
root.mainloop()
and then the second file
import tkinter as tk
def NewScreen():
root = tk.Tk()
canvas = tk.Canvas(root, bg='black')
canvas.pack()
root.mainloop()
NewScreen()
This is because, in your second file, you are calling the definition when it is imported.
import tkinter as tk
def NewScreen():
root = tk.Tk()
canvas = tk.Canvas(root, bg='black')
canvas.pack()
root.mainloop()
NewScreen() #< here you call the definition
This means that the series of events that happen in your program are as follows.
First file starts
Second file is imported
Definition "NewScreen" is called
tkinter mainloop starts which puts your program into a loop and stops the program from going on to any new lines until the loop is closed

Zoom in and out function in Python

I am trying to use zoom in, zoom out feature in python. I have previously tried to use the functionality to zoom in a line in turtle, canvas, etc. but nothing seem to work out, instead of zooming, the code is either increasing or decreasing the length of the line. I want to zoom in the line to add text on the line so that when a user zoom's in the line he/she can see the text. here is the code which I am trying to change.
from tkinter import *
root = Tk()
Label(root).pack()
canvas = Canvas(root, width=400, height=400)
canvas.pack(fill=BOTH, expand=1)
widget = Button(None, text='zoomin-out')
widget.pack()
canvas.create_line(175,175,225,225)
def zoomin(event):
d = event.delta
if d < 0:
amt=0.9
else:
amt=1.1
canvas.scale(ALL, 200,200, amt, amt)
widget.bind('<Button-1>', zoomin)
def zoomout(event):
d = event.delta
if d >0:
amt=1.1
else:
amt=0.7
canvas.scale(ALL, 200,200 , amt, amt)
widget.bind('<Double-1>', zoomout)
widget.mainloop()
root.mainloop()
There are two issues. First, when you add both the Button-1 and Double-1 events to your Button widget, doing a double-click fires both events. They end up cancelling each other out, so only the single-click works as expected.
Second, as I pointed out in this SO answer, certain elements, like text, won't zoom, they'll remain fixed. You'll need to manually scale your fonts to simulate text zoom.
Below's a rework of your code along the above lines. Instead of the problematic single and double click, I've changed it so that left and right clicks on the button cause the canvas to zoom in or out:
from tkinter import *
EXAMPLE_TEXT = "Left or Right click button to zoom in/out"
FONT_NAME = "Helvetica"
font_size = 12
def zoom(amount):
global font_size
canvas.scale(ALL, 200, 200, amount, amount)
font_size *= amount
canvas.itemconfigure(text_item, font=(FONT_NAME, int(font_size)))
root = Tk()
canvas = Canvas(root, width=400, height=400)
canvas.pack(fill=BOTH, expand=1)
text_item = canvas.create_text(200, 200, font=(FONT_NAME, font_size), text=EXAMPLE_TEXT)
canvas.create_oval(50, 50, 350, 350)
widget = Button(root, text='zoom in/out')
widget.pack()
widget.bind('<Button-1>', lambda e: zoom(1.1))
widget.bind('<Button-2>', lambda e: zoom(0.7))
root.mainloop()
If you comment out the line that starts with canvas.itemconfigure(...), you'll see that the circle continues to zoom in and out, but the text remains fixed size.

Bind gif image to cursor?

Is thre a way to bind and image to the cursor?
I have an event, for the moment it doen'st do much, just this:
from tkinter import *
root = Tk()
def evento(event):
print("explosion"), event.x, event.y
f = Frame(root, width=100, height=100)
f.bind("<Enter>", evento)
f.pack()
root.mainloop()
I'm mostly seen how the events works but I need to bind and image(circle) and then when it enters change it for gif ot at least that's why I'm thinking of, don't know if I can program an animation or not in tkinter
From python docs: try this example
def buildFrame(self):
self.f = Frame(self.master, height=32, width=32, relief=RIDGE,
borderwidth=2)
self.f.place(relx=.5,rely=.5)
#self.f.bind( '<Enter>', self.enterFrame )
#self.f.bind( '<Leave>', self.leaveFrame )
self.f.configure(cursor = 'circle')

Categories