I'm working on a simple tkinter game, and have run into a problem. I want to be able to move an image to a random spot on the screen when you click on it, but what I thought would work hasn't. Below is the code:
spr_earth=PhotoImage(file="earth.gif")
x=r.randrange(64,roomw-64)
y=r.randrange(64,roomh-64)
earth=canvas.create_image(x,y,image=spr_earth)
x1,x2,y1,y2=canvas.bbox(earth)
def click(event):
if ((event.x>x1) and (event.x<x2)) and ((event.y>y1) and (event.y<y2)):
canvas.move(earth,r.randrange(64,roomw-64),r.randrange(64,roomh-64))
root.bind_all("<Button-1>",click)
root.mainloop()
I thought this would work, but it clearly doesn't. You can click on it, but it appears to teleport into space an beyond :)
I'd appreciate anyone's input on this problem. thanks
From reading the documentation, it looks like move coordinates are relative to the current position.
Perhaps something like this would work (warning, this code is untested):
def click(event):
if ((event.x>x1) and (event.x<x2)) and ((event.y>y1) and (event.y<y2)):
canvas.coords(earth,(r.randrange(64,roomw-64),r.randrange(64,roomh-64)))
for what it's worth, the following simplified script seemed to work for me:
import Tkinter as tk
from random import randrange
root = tk.Tk()
canvas = tk.Canvas(root,width=400,height=400)
canvas.pack()
image = tk.PhotoImage(file='mouse.gif')
def position():
return randrange(0,400),randrange(0,400)
mouse = canvas.create_image(*position(),image=image)
def click(event):
canvas.coords(mouse,position())
canvas.bind('<Button-1>',click)
root.mainloop()
(The mouse always stayed partially on the canvas).
Related
This question already has an answer here:
When should I use root.update() in tkInter for python
(1 answer)
Closed 1 year ago.
Beginner programmer here currently trying to learn Tkinter for a school assignment.
I have a GUI class that stores the Tkinter labels etc, the labels are innitiated like this:
# GUI for Player 1
self.player_1_name_field = Label(
self.root,
text="Player 1",
font=GUI_Settings.player_information_font,
anchor=W,
background=GUI_Settings.playerfield_active_color
)
I then create a Game() object that looks like this:
class Game():
def __init__(self):
self.GUI = GUI()
self.GUI.initializeBoard()
self.GUI.root.mainloop()
When I run the code, the labels do get created and are where they are supposed to be, but are completely black. Once I move or resize the window it instantly becomes how I want it to be, it just behaves weird when at the start of the code
The interesting thing is that I also have a Canvas and a List that work perfectly fine, only the Labels are not cooperative
If you need further info, just ask for it!
Thank you!
Edit 1: I have a function called drawWindow() that redraws the chessboard when I re-configure the window. In the init of the GUI class I set self.root.bind("<Configure>", self.drawWindow). If I remove that line of code, the Labels work but the Canvas doesn't anymore. I'm so confused. For anyone wanting to take a look at my tiny code: https://codeshare.io/DZYzyZ
See comment of Thingamabobs
The issue is self.root.update(). Remove this line and you'll be fine.
When should I use root.update() in tkInter for python.
This works but you shouldn't do it
This is a tricky issue. Your problem come from the bind of the configure event. Bind to the root window, it is applied to all sub-widgets of the window, which cause the bug (I don't know why yet).
This will solve your issue (line 202):
self.chessboard.bind("<Configure>", self.drawWindow)
instead of:
self.root.bind("<Configure>", self.drawWindow)
Result without moving or resizing the window:
I found the information here (french forum).
I've been happily going along writing programs using Python Turtle Graphics, and occasionally super-charging that module with forays into tkinter when turtle wasn't quite up to the job. An example is shown below.
What would be the most direct translation into "non-terrible" pure tkinter code? Is it always recommended to use OOP with tkinter? If so what would my program look like using that paradigm? If not, how would it look with just procedural tkinter? I've seen so many different ways to work with tkinter and create roots and masters etc., it's all a bit confusing. I'd like to keep the simplicity of the the approach I have used as much a possible. As usual, any help much appreciated.
import turtle
import tkinter as tk
def do_stuff(a_turtle):
for color in ["red", "yellow", "green"]:
a_turtle.color(color)
a_turtle.right(120)
def press():
print("ouch")
if __name__ == "__main__":
screen = turtle.Screen()
screen.bgcolor("cyan")
canvas = screen.getcanvas()
button = tk.Button(canvas.master, text="Press me", command=press)
canvas.create_window(-200, -200, window=button)
my_lovely_turtle = turtle.Turtle(shape="turtle")
do_stuff(my_lovely_turtle)
turtle.done()
For fun, I'm creating a crappy helicopter/flappybird clone using tkinter and I've run into some really bizarre behavior with regards images apparently not showing up.
(btw, using python3)
So I started with the following code just to see if I could start getting things to draw:
from tkinter import *
from PIL import ImageTk
class Bird(object):
def __init__(self, canvas, x=0, y=0):
self.canvas = canvas
photo = ImageTk.PhotoImage(file="flappy.gif")
self.bird = self.canvas.create_image(x,y,image=photo)
class Environment(Canvas):
def __init__(self, master, width=500, height=500):
super(Environment, self).__init__(master, width=width, height=height)
self.pack()
self.master = master
self.bird = Bird(self)
if __name__=="__main__":
r = Tk()
env = Environment(r)
env.pack()
r.mainloop()
Image didn't appear, all I had was a blank canvas. I thought this was odd, so I started playing around to see why that might be the case. My next step was to test that I knew how to create images, so I just made my file a basic image create:
if __name__=="__main__":
r,c=get_canv()
c.pack()
img = ImageTk.PhotoImage(file="flappy.gif")
c.create_image(100,100,image=img)
r.mainloop()
And this, predictably, works fine. So, my syntax in the prior code seemed to be correct. This is when I stumbled on something a little confusing:
if __name__=="__main__":
r,c=get_canv()
c.pack()
c.create_image(100,100,image=ImageTk.PhotoImage(file="flappy.gif"))
r.mainloop()
This didn't draw. I'm left with a blank canvas again. This is what made me suspect that maybe there was some weird threading issue going on behind the scenes. Does Anyone know why the second snippet worked and the third snippet failed?
I've seen this a number of times already. The problem is that the PhotoImage is garbage collected even though it is used in the Label! To fix the problem, just bind it to a member variable of the GUI itself:
self.photo = ImageTk.PhotoImage(file="flappy.gif")
self.bird = self.canvas.create_image(x,y,image=self.photo)
The reason it works in your second example is that the img variable exists until after the mainloop method has finished, while in your third example, it exists only during the creation of the Label.
I am building an Interface with TKinter and I have the problem, that whenever create a new window the standardized tkinter window of 200x200 pixel with nothing in it flashes up for a fraction of a second and AFTER that all my modifications (widgets ect.) are made. This happens before AND after calling the mainloop.
The Main-Interface is created.
Mainloop stats
Flashing window
Main-Interface appears
Also after Mainloop was called this happens with newly created windows.
Main-Interface appears--> Push a button, that creates a new window
Flashing window
new window appears
Sadly I cannot give you a sample code... If I try to do a minimal example, this doesn't happen. Maybe the standard window is created, but it is changed so fast, that it doesn't appear on screen. I don't even know what to look up in this case... searching "tkinter flashing window" yields nothing.
EDIT: I found the source of the problem. It seems to be caused by wm_iconbitmap, FigureCanvasTkAgg and tkinter.Toplevel. If you remove the the icon out of the code, it works fine, no flashing. But if I use it together with one of the other, the window flashes when created. Try it out with the code below. You have to put the icon in the working directory of course.
Here is a code sample and the link to the icon I am using, but I suppose any icon will do.
# coding=utf-8
import numpy as np
import matplotlib as mpl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import os
class INTERFACE(object):
def __init__(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.EXIT)
self.root.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
self.root.geometry("1024x768")
canvas = FigureCanvasTkAgg(self.testfigure(), master=self.root) #<---- !!!!!!
canvas.get_tk_widget().grid(sticky=tk.N+tk.W+tk.E+tk.S)
self.root.rowconfigure(0, weight=1)
self.root.columnconfigure(0, weight=1)
def testfigure(self):
x=np.linspace(0, 2*np.pi,100)
y=np.sin(x)
fig = mpl.figure.Figure()
sub = fig.add_subplot(111)
sub.plot(x,y)
return fig
def EXIT(self):
Top = tk.Toplevel(master=self.root)
Top.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
Top.transient(self.root)
Top.resizable(width=False, height=False)
Top.title("Exit")
tk.Message(Top,text="Do you really want to quit?", justify=tk.CENTER, width=300).grid(row=0,columnspan=3)
tk.Button(Top,text="YES",command=self.root.destroy).grid(row=1,column=0)
tk.Button(Top,text="No",command=self.root.destroy).grid(row=1,column=1)
tk.Button(Top,text="Maybe",command=self.root.destroy).grid(row=1,column=2)
def start(self):
self.root.mainloop()
if __name__ == '__main__':
INTERFACE().start()
I know this is an old question, but I've experienced a similar situation and have found a solution.
In my case, I've isolated the issue to the use of iconbitmap. I've managed to solve it by calling iconbitmap with the after method just before calling root.mainloop().
Example:
from tkinter import *
root = Tk()
w = Label(root, text="Hello, world!")
w.pack()
root.geometry('300x300+500+500')
root.after(50, root.iconbitmap('icon.ico'))
root.mainloop()
This method has worked on Toplevel() windows with icons as well.
Tested on Win 8.1 with Python 3.5.0.
Edit: Upon further inspection I've noticed that the behavior changes relative to root.geometry's presence as well. My initial example didn't have it and I only noticed after a few tries that it still had the same issue. The time delay in the after method doesn't seem to change anything.
Moving root.geometry below the after method yields the same issue for some reason.
Most likely, somewhere in your initialization code you're calling update or update_idletasks, which causes the current state of the GUI to be drawn on the screen.
Another possible source of the problem is if you're creating multiple instances of Tk rather than Toplevel.
Without seeing your code, though, all we can do is guess.
Your best route to solving this problem is to create a small example that has the same behavior. Not because we need it to help you, but because the effort you put into recreating the bug will likely teach you what is causing the bug.
This should work but it requires win32gui
import win32gui
def FlashMyWindow(title):
ID = win32gui.FindWindow(None, title)
win32gui.FlashWindow(ID,True)
I have a simple program as follows:
from Tkinter import *
class Run:
def __init__(self,parent):
parent.overrideredirect(True)
root = Tk()
app = Run(root)
root.mainloop()
When I run this program, the undecorated root window always stays on top.
How can I make it so any other window can be on top of it, whilst having no decorations?
I have tried setting 'topmost' to 'False' as well, but to no avail.
I am running Ubuntu 13.04.
Thanks in advance
This code below, will put your window on the background on every 100ms, no matter what, so everything will be in the front of it all the time. I think this is what you were asking for:
from tkinter import *
class Run:
def __init__(self):
self.root = Tk()
self.root.overrideredirect(True)
def put_back(self):
# this method will put the window to the background
self.root.lower()
# recursive calling of put_back method
self.root.after(100, self.put_back)
app = Run()
# Start the recursion
app.put_back()
app.root.mainloop()
Seems like this should happen automatically if I click a different window.