This question already has answers here:
Python Tkinter Canvas fail to bind keyboard
(3 answers)
Closed 5 years ago.
I am trying to make a Python Tkinter program display a circle and move the circle right when I press Return/Enter. My code is currently:
from Tkinter import *
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.setupStuff()
def setupStuff(self):
self.canvas = Canvas(self, height=500, width=600)
self.canvas.pack()
self.blueCircle = self.canvas.create_oval(10, 10, 40, 40, fill='dodger blue')
self.canvas.bind('<Return>', self.moveRight)
def moveRight(self):
print 'Yo',
self.canvas.move(self.blueCircle, 1, 0)
print 'yo'
if __name__ == '__main__':
window = GUI(Tk())
window.mainloop()
My problem is that the ball doesn't move when I press Return/Enter.
You may bind your keys to root which is self.master in your case instead of binding it to canvas. Please see the modified working code below.As #Alex has specified, bind returns an event
from Tkinter import *
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.setupStuff()
def setupStuff(self):
self.canvas = Canvas(self, height=500, width=600)
self.canvas.pack()
self.blueCircle = self.canvas.create_oval(10, 10, 40, 40, fill='dodger blue')
#self.canvas.bind('<Return>',self.moveRight)
self.master.bind('<Return>', self.moveRight)
def moveRight(self, event = None):
print 'Yo',
self.canvas.move(self.blueCircle, 200, 0)
print 'yo'
if __name__ == '__main__':
root = Tk()
window = GUI(root)
window.mainloop()
You need to focus the tkinter canvas with the .focus_force() method if you want the widget to receive events, as only the focused widget can receive events. Also, when the your keypress handler is called, it passes an argument containing data about the event, so you need to add an argument to moveRight or you will get a TypeError.
from Tkinter import *
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.setupStuff()
self.canvas.focus_force() #force the canvas to take focus
def setupStuff(self):
self.canvas = Canvas(self, height=500, width=600)
self.canvas.pack()
self.blueCircle = self.canvas.create_oval(10, 10, 40, 40, fill='dodger blue')
self.canvas.bind('<Return>', self.moveRight)
def moveRight(self, eventData): #.bind passes an argument
self.canvas.move(self.blueCircle, 1, 0)
if __name__ == '__main__':
window = GUI(Tk())
window.mainloop()
Related
I have an app with multiple windows. I use pack_forget to eliminate the login window and invoke the main window. However this main window loses the default centered position of tkinter. The window is created at position (0 , 0).
Is there any simple way to make this main window be created in the default centered position?
example code, 3 files ->
main.py
#!/usr/bin/env python3
from tkinter import *
from frm_login import Wlogin
class Mainframe(Tk):
def __init__(self):
Tk.__init__(self)
self.frame = Wlogin(self)
self.frame.pack()
def change(self, frame):
self.frame.pack_forget() # delete currrent frame
self.frame = frame(self)
self.frame.pack() # make new frame
if __name__== '__main__':
app = Mainframe()
app.mainloop()
frm_login.py
from tkinter import *
from frm_default import Wmain
class Func(Frame):
def check(self, event=None):
if self.pwd.get() == '1':
self.master.change(Wmain)
else:
self.status.config(text='wrong password')
class Wlogin(Func):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
master.title('Enter password')
master.geometry('300x200')
self.status = Label(self, fg='red')
self.status.pack()
self.lbl = Label(self, text='Enter password')
self.lbl.pack()
self.pwd = Entry(self, show='*')
self.pwd.insert(-1, '1')
self.pwd.pack()
self.pwd.focus()
self.pwd.bind('<Return>', self.check)
self.pwd.bind('<KP_Enter>', self.check)
self.btn = Button(self, text='Done', command=self.check)
self.btn.pack()
self.btn = Button(self, text='Cancel', command=self.quit)
self.btn.pack()
frm_default.py
from tkinter import *
class Wmain(Frame):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
master.title('Main application')
master.geometry('600x400')
There is nothing about your forget / repack code that makes this unique. You can use the same commands you would otherwise. So either define the position yourself:
master.geometry('600x400+300+400')
Or use tk PlaceWindow function:
master.eval('tk::PlaceWindow . center')
Or calculate the position from the window size and monitor size:
master.geometry("600x400")
master.update_idletasks()
x = (master.winfo_screenwidth() - master.winfo_reqwidth()) // 2
y = (master.winfo_screenheight() - master.winfo_reqheight()) // 2
master.geometry(f"+{x}+{y}")
FWIW, my experience tells me that setting the window size yourself instead of letting tkinter calculate it will lead to bugs down the road.
I am trying to make a replica of dodger using python. In my code, I made a window using tkinter. I tried to use getch() as a way of inputting arrow key values to make the main character move. Here is my code:
from tkinter import *
from msvcrt import getch
import time
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.initWindow()
def initWindow(self):
self.master.title('Dodger')
self.pack(fill=BOTH, expand=1)
self.master.geometry('600x800')
self.master.config(bg='black')
menu = Menu(self.master)
self.master.config(menu=menu)
def clientExit():
exit()
file = Menu(menu)
file.add_command(label='Exit', command=clientExit)
file.add_command(label='Start', command=self.game)
menu.add_cascade(label='File', menu=file)
def game(self):
canvas = Canvas(self.master, width='600', height='800')
canvas.pack()
canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')
character = canvas.create_rectangle(270, 730, 330, 760, fill='blue', outline='red')
left = 75
right = 77
time.sleep(10)
while True:
if ord(getch()) == left:
canvas.move(character, -5, 0)
canvas.update()
elif ord(getch()) == right:
canvas.move(character, 5, 0)
canvas.update()
root = Tk()
app = Window(root)
app.mainloop()
As you can see, in the def game(self) function, I created a rectangle as the character. Then I used getch() to compare keyboard inputs; pressing the right/left arrow key will move the character respectively. However, this does not work in the window: my window freezes and it says "not responding". I am forced to close the window, thus I do not know if my code is not working or if my computer sucks. Copy and paste this into your editor and please let me know how I can fix this, if possible.
BTW, when you load the window, click file, then start.
I have modified your code to follow PEP8 a little better and corrected the game() method by removing the sleep() method and adding 2 more methods for controlling left and right movement.
By making sure our Canvas is a class attribute and the character is a class attribute we can interact with them from any method within the class.
I made everything into a class attribute that I thought should probably be one.
Updated to include max left and max right.
import tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master.title('Dodger')
self.master.geometry('600x800')
self.master.config(bg='black')
menu = tk.Menu(self.master)
file = tk.Menu(menu)
file.add_command(label='Exit', command=exit)
file.add_command(label='Start', command=self.game)
menu.add_cascade(label='File', menu=file)
self.master.config(menu=menu)
self.canvas = None
self.character = None
self.master.bind("<Left>", self.left_key)
self.master.bind("<Right>", self.right_key)
def game(self):
self.canvas = tk.Canvas(self.master, width='600', height='800')
self.canvas.pack()
self.canvas.create_rectangle(0, 0, 600, 800, fill='black', outline='black')
self.character = self.canvas.create_rectangle(270, 730, 330, 760, fill='blue', outline='red')
def left_key(self, event):
cords = self.canvas.coords(self.character)
if cords[0] <= 5:
print("Max left")
else:
self.canvas.move(self.character, -5, 0)
def right_key(self, event):
cords = self.canvas.coords(self.character)
if cords[2] >= 595:
print("Max Right")
else:
self.canvas.move(self.character, 5, 0)
root = tk.Tk()
app = Window(root).pack(fill="both", expand=1)
root.mainloop()
I'm quite new to Tkinter and this is my first program, can anyone tell me why the canvas is not showing up? I'm not getting any errors so I assume it is working but just not visible? I tried moving it up a layer but it was still invisible. Here is my code:
from Tkinter import *
import Tkinter as tk
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.frame = Frame(self.master)
self.master = master
self.window()
self.drawFigure()
# self.master.attributes('-fullscreen', True)
self.master.bind("<Escape>", self.end_fullscreen)
def window(self):
self.frame = Frame(self.master)
screen_width = self.frame.winfo_screenwidth() / 2
screen_height = self.frame.winfo_screenheight() / 2
self.master.geometry('%dx%d' % (screen_width, screen_height))
def end_fullscreen(self, event=None):
self.master.attributes("-fullscreen", False)
def drawFigure(self):
self.frame = Frame(self.master)
self.C = Canvas(self.frame, width=200, height=200, bg = 'red')
self.C.pack()
self.C.create_rectangle(50, 20, 150, 80, fill="#476042")
if __name__ == '__main__':
root = tk.Tk()
w = Application(root)
w.master.mainloop()
Appreciate all the input.
You import Tkinter and Tkinter as tk, which gets confusing.
Application inherits from Frame so you don't have to create additional frames inside. Certainly not more than one named self.frame.
How about this:
from Tkinter import *
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.master = master
self.pack()
self.window()
self.drawFigure()
self.master.bind("<Escape>", self.end_fullscreen)
def window(self):
screen_width = self.winfo_screenwidth() / 2
screen_height = self.winfo_screenheight() / 2
self.master.geometry('%dx%d' % (screen_width, screen_height))
def end_fullscreen(self, event=None):
self.master.attributes("-fullscreen", False)
def drawFigure(self):
self.C = Canvas(self, width=200, height=200, bg = 'red')
self.C.pack()
self.C.create_rectangle(50, 20, 150, 80, fill="#476042")
if __name__ == '__main__':
root = Tk()
w = Application(root)
w.master.mainloop()
You forgot to call pack() on the Frame you created in drawFigure():
def drawFigure(self):
self.frame = Frame(self.master)
self.frame.pack() # <--- There
self.C = Canvas(self.frame, width=200, height=200, bg = 'red')
self.C.pack()
self.C.create_rectangle(50, 20, 150, 80, fill="#476042")
You’re creating three sub frames of your parent, storing each of them as self.frame (so all but the last one are lost), and not placing any of them anywhere.
So, you’ve correctly placed the canvas on one of these invisible frames, but that doesn’t do any good.
I’m not sure what you’re trying to do with all these separate frames.
If you really need three sibling frames, you have to store them in separate variables, or in a list, or something, and you need to place them.
If you need one sibling frame, just create it once instead of three times, and again, you need to place it.
If you need three or one child frames instead of sibling frames, create them with self rather than self.master.
If you don’t need any sibling or child frames at all, don’t create them, and just place the canvas on self instead of self.frame.
I cannot figure out how to use the tag_bind method associated with a canvas. I created an oval and expected to be able to generate an event by clicking on it. When I do this, nothing happens. What is the correct use of tag_bind?
import tkinter as tk
class Window():
def __init__(self, master):
self.master = master
self.create_widgets()
def create_widgets(self):
self.c = tk.Canvas(self.master, bg='white', height=200, width=300)
self.c.pack()
b = tk.Button(self.master, text='Draw', command=self.draw)
b.pack()
def draw(self):
self.c.delete('all')
self.oval = self.c.create_oval([30,50], [130,80])
self.rect = self.c.create_rectangle([180,10], [280,80])
self.c.tag_bind(self.oval, '<Button-1>', self.oval_func)
def oval_func(self, event):
self.c.delete(self.rect)
self.c.create_text(150, 150, text='Hello, world!', anchor='w')
if __name__ == '__main__':
root = tk.Tk()
app = Window(root)
root.mainloop()
The code is working. However, when you bind to a canvas object, you have to click on part of the drawn object. Since you didn't fill the oval, that means you must click on its outline.
If you fill it with the same color as the background (or any other color) you can click anywhere in the oval.
self.oval = self.c.create_oval([30,50], [130,80], fill="white")
I have an external script that calls the drawWorld() function of this class.
I want the drawing to be shown for 1-2 seconds and then to close and the control to return to the main script.
I can manage to let the window disappear with the line
root.after(1000, lambda: root.destroy())
but I cannot return the flow to the main script.
I tried
root.after(1000, lambda: root.quit())
but it doesn't work.
This is my code for the Tkinter class:
from Tkinter import Tk, Canvas, Frame, BOTH
class World(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("CliffWorld")
self.pack(fill=BOTH, expand=1)
canvas = Canvas(self)
canvas.create_rectangle(4, 4, 31, 31,
outline="#f11", fill="#1f1", width=1)
canvas.pack(fill=BOTH, expand=1)
def drawWorld():
root = Tk()
ex = World(root)
root.geometry("330x220+300+300")
root.after(1000, lambda: root.destroy())
root.after(1000, lambda: root.quit())
root.mainloop()
In a comment to your question you wrote that your main program is just this:
import tkWorld
tkWorld.drawWorld()
print "end"
When I use that in a program, and use your example code (after fixing the indentation), it works fine. I see the window appear for one second, it goes away, and I send "end" printed on the console.
It works no matter whether the lambda calls root.quit() or root.destroy().
from Tkinter import Tk, Canvas, Frame, BOTH
class World(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("CliffWorld")
self.pack(fill=BOTH, expand=1)
canvas = Canvas(self)
canvas.create_rectangle(4, 4, 31, 31,
outline="#f11", fill="#1f1", width=1)
canvas.pack(fill=BOTH, expand=1)
def drawWorld():
root = Tk()
ex = World(root)
root.geometry("330x220+300+300")
root.after(1000, lambda: root.destroy())
root.after(1000, lambda: root.quit())
root.mainloop()
if __name__ == "__main__":
import tkWorld
tkWorld.drawWorld()
print "end"