Python tkinter - closing of new window - python

I made this program, I recommend run it first. The main idea is popping window, it should pop after clicking on text info. This works correctly, than, an user needs to close this window, so I made red closing button (rectangle with lines) in top right corner. After clicking on this button, new window should disappear, but first window with ovals should stay.
I need function for closing this window, I was trying to find solution a long time, but I am not able to. Any idea please?
import tkinter
class Desktop:
def __init__(self):
self.canvas = tkinter.Canvas()
self.canvas.pack()
x, y = 25,25
for i in range(1,61):
self.canvas.create_oval(x-10, y-10, x+10, y+10, fill='blue')
self.canvas.create_text(x, y, text=i)
x += 30
if i%10==0:
x = 25
y += 40
self.canvas.create_text(340, 150, text='info', font='arial 17', tags='info')
self.canvas.tag_bind('info', '<Button-1>', self.window)
def window(self, event):
self.canvas.create_rectangle(40,40,310,220,fill='white')
x, y = 75,100
for i in range(1,9):
self.canvas.create_rectangle(x-30,y-30,x+30,y+30)
self.canvas.create_text(x,y,text='number ' + str(i), font='arial 9')
x += 65
if i == 4:
x = 75
y += 70
self.rec = self.canvas.create_rectangle(310,40,290,60, fill="red", tags="cancel")
self.line = self.canvas.create_line(310,40,290,60,310,60,290,40, tags="cancel")
self.canvas.tag_bind('cancel', '<Button-1>', self.close)
def close(self,event):
#?
print('should close this part of screen')
d = Desktop()

What I use in my code is:
def quit(self):
root.destroy()
self.quit()
This might be a really long winded way to do it but it works for me every time.
EDIT: Just thinking about it now, depending on the way you open a new frame,root.destroy would be the way I would do it.

Related

Tkinter window showing up too early

I'm trying to build a binary lamp in Python using Tkinter.
The steps are as follows:
1. Prompt user for input, using the console/shell.
2. Draw the lamp
3. Use mainloop to show the image
However, this doesn't always work as I expect it to.
The code:
from tkinter import *
HEIGHT = 235
WIDTH = 385
tk = Tk()
canvas = Canvas(tk, width=WIDTH, height=HEIGHT, bg="grey")
size = 35
canvas.pack()
def dec_to_binh(num, lst):
if num>1:
dec_to_binh(num // 2, lst)
lst.append(num%2)
return lst
def dec_to_bin(num):
answ = dec_to_binh(num, [])
while len(answ) < 4:
answ.insert(0,0)
return answ
class Diode:
def __init__(self, x, y, label):
self.shape = canvas.create_oval(x, y, x+size, y+size, width=4,
outline='black', fill='#232323')
self.label = canvas.create_text(x+int(size/2), y+size+10, text=str(label))
self.onOff = 0
def update(self, num):
if int(num) == 1:
canvas.itemconfig(self.shape, fill=oncolor)
onOff = 1
else:
canvas.itemconfig(self.shape, fill='black')
onOff = 0
class Lamp:
def __init__(self):
self.LEDs = [Diode(100, 100, 8), Diode(150, 100, 4), Diode(200, 100, 2), Diode(250, 100, 1)]
def update(self, number):
n=0
for i in self.LEDs:
i.update(dec_to_bin(number)[n])
n=n+1
print("What to show as a binary lamp?")
answer=int(input())
while answer > 15 or answer < 0:
print("Invalid input, can only show between range of 0 to 15 inclusive")
answer=int(input())
lamp = Lamp()
lamp.update(answer)
tk.mainloop()
The bug:
When I run this in my IDE (Wing 101), it patiently waits for me to input the number I want to show before drawing the image.
However, if I run it straight from the python shell (i.e. I just click on bin_lamp.py) it opens and creates the tk window and shows a blank screen, without waiting for me to tell it what number I want.
After I actually enter the number, it draws what I want it to, but is there any way I can stop the blank tk window with the blank canvas from actually showing up until I'm done entering the input?
You can either put the input before you create the window with tk = Tk(), or you can leave everything as is, but hide the window and show it once it's done. Simply hide the window after it has been created and show it afterwards:
tk = Tk()
tk.withdraw()
# do all your processing
tk.deiconify()

Using Python and Tkinter, How would I run code every loop of .mainloop()?

I have a program where I need to move an image object every time the mainloop() loops. I haven't tried doing much, mostly because I don't know where to start. I made a dummy version of my project that simulates the issue I'm having.
from tkinter import *
window = tk.Tk()
window.geometry('%ix%i+400+0' % (500, 600))
canvas = Canvas(window, width=500, height=600, bg='white')
canvas.pack()
w, x, y, z = 300, 300, 200, 200
x = canvas.create_rectangle(w, x, y, z)
def moveRectangle():
canvas.move(x, 10, 0)
# Run the moveRectangle function everytime the mainloop loops
window.mainloop()
To sum up my issue, I need to run mainloop as if it isn't a blocking function. Rather, either run it asynchronous, or maybe pause it and then run the function, though I don't think that's possible.
Anything helps
Thanks
Mainloop in tkinter doesn't loop through your code. It's looping through list of events. You can create an event by clicking buttons etc. Another way is that you can call commands like update() or update_idletasks(). Combinig that with after() can give you results you are looking for. So look up these in documentation, it will be helpful. Also you can read: understanding mainloop.
def moveRectangle():
canvas.move(x, 10, 0)
for i in range(20): # 20 moves 10px every 50ms
window.after(50, canvas.move(x, 10, 0))
window.update()
moveRectangle()
This little code above demonstrate how you could use mentioned commands to get your object move on screen.
I think there are ways to set up a custom mainloop() which could update your UI every time the loop runs but depending on what you want to do the more usual method is to use after. By arranging for the function called by after to call itself with after an effective loop can be created.
I've amended you code to bounce the rectangle as it reaches the sides of the canvas so it can show something as it runs indefinitely.
import tkinter as tk
WAIT = 10 # in milliseconds, can be zero
window = tk.Tk()
window.geometry('%ix%i+400+0' % (500, 600))
canvas = tk.Canvas(window, width=500, height=600, bg='white')
canvas.pack()
w, x, y, z = 300, 300, 200, 200
x = canvas.create_rectangle(w, x, y, z)
amount = 10
def direction( current ):
x0, y0, x1, y1 = canvas.bbox( x )
if x0 <= 0:
return 10 # Move rect right
elif x1 >= 500:
return -10 # Move rect left
else:
return current
def moveRectangle():
global amount
canvas.move(x, amount, 0)
window.update()
# Change Direction as the rectangle hits the edge of the canvas.
amount = direction( amount )
window.after( WAIT, moveRectangle )
# ms , function
# Loop implemented by moveRectangle calling itself.
window.after( 1000, moveRectangle )
# Wait 1 second before starting to move the rectangle.
window.mainloop()

I'm trying to make a simple line drawing program with tkinter but it won't work

I'm trying to make this really simple program, all it does is store the current x/y pos of the mouse on the canvas and then use them to draw a line when you click for the second time. I've already bound it and I'm not getting any errors, it seems like it's not even being activated. Any help is greatly appreciated
from tkinter import *
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
#For colored lines
presses = 0
def click(event):
if presses == 0:
initX = int(c.canvasx(event.x))
initY = int(c.canvasy(event.y))
presses == 1
elif presses == 1:
c.create_line(initX, initY,
int(c.canvasx(event.x)),
int(c.canvasy(event.y)))
presses == 0
c.bind("<Button-1>", click)
mainloop()
How does something like this work for you?
from tkinter import *
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
line = []
def click(event):
global line
X = int(c.canvasx(event.x))
Y = int(c.canvasy(event.y))
line.append((X,Y))
if len(line) > 1:
startX,startY = line[-2]
c.create_line(startX, startY, X, Y)
c.bind("<Button-1>", click)
mainloop()
I've changed around your code a bit to store a list of the X,Y coordinates that have been clicked on. If more than 1 point on the screen has been clicked, it will draw a line between the current point clicked on and the last point clicked on.
Reason your code wasn't working was that initX and initY are forgotten in between calls on the the click function. Adding them to a list solves this.

Tkinter Many Key Binding [duplicate]

I have a problem in Python.
I'm using Tkinter and have four bind events, that listen to key presses on my form.
My problem is, that these don't run asynchronously. So, for example I can press one button, and the events are recognized. But when I press and hold two keys at the same time, just one event gets fired.
Is there an alternative way to do this?
self.f.bind("w", self.player1Up)
self.f.bind("s", self.player1Down)
self.f.bind("o", self.player2Up)
self.f.bind("l", self.player2Down)
Unfortunately, you are somewhat at the mercy of the underlying auto-repeat mechanism of your system. For example, on the mac I'm using at the moment if I press and hold "w" I'll get a stream of press and release events. While pressed, if I press "o" I get a stream of presses and releases for "o" but no more events for "w".
You will need to set up a mini state machine, and bind to both key press and key release events. This will let you track which keys are pressed and which are not. Then, each time you draw a frame you can query the machine to see which keys are pressed and act accordingly.
Here's a quick hack I threw together. I've only tested it on my mac, and only with python 2.5. I've made no real attempt at being "pythonic" or efficient. The code merely serves to illustrate the technique. With this code you can simultaneously press either "w" or "s" and "o" or "l" to move two paddles up and down.
'''Example that demonstrates keeping track of multiple key events'''
from Tkinter import *
class Playfield:
def __init__(self):
# this dict keeps track of keys that have been pressed but not
# released
self.pressed = {}
self._create_ui()
def start(self):
self._animate()
self.root.mainloop()
def _create_ui(self):
self.root = Tk()
self.p1label = Label(text="press w, s to move player 1 up, down",
anchor="w")
self.p2label = Label(text="press o, l to move player 2 up, down",
anchor="w")
self.canvas = Canvas(width=440, height=440)
self.canvas.config(scrollregion=(-20, -20, 420, 420))
self.p1label.pack(side="top", fill="x")
self.p2label.pack(side="top", fill="x")
self.canvas.pack(side="top", fill="both", expand="true")
self.p1 = Paddle(self.canvas, tag="p1", color="red", x=0, y=0)
self.p2 = Paddle(self.canvas, tag="p2", color="blue", x=400, y=0)
self._set_bindings()
def _animate(self):
if self.pressed["w"]: self.p1.move_up()
if self.pressed["s"]: self.p1.move_down()
if self.pressed["o"]: self.p2.move_up()
if self.pressed["l"]: self.p2.move_down()
self.p1.redraw()
self.p2.redraw()
self.root.after(10, self._animate)
def _set_bindings(self):
for char in ["w","s","o", "l"]:
self.root.bind("<KeyPress-%s>" % char, self._pressed)
self.root.bind("<KeyRelease-%s>" % char, self._released)
self.pressed[char] = False
def _pressed(self, event):
self.pressed[event.char] = True
def _released(self, event):
self.pressed[event.char] = False
class Paddle():
def __init__(self, canvas, tag, color="red", x=0, y=0):
self.canvas = canvas
self.tag = tag
self.x = x
self.y = y
self.color = color
self.redraw()
def move_up(self):
self.y = max(self.y -2, 0)
def move_down(self):
self.y = min(self.y + 2, 400)
def redraw(self):
x0 = self.x - 10
x1 = self.x + 10
y0 = self.y - 20
y1 = self.y + 20
self.canvas.delete(self.tag)
self.canvas.create_rectangle(x0,y0,x1,y1,tags=self.tag, fill=self.color)
if __name__ == "__main__":
p = Playfield()
p.start()
You could bind to "<Key>" and then check event.char and perform the action you want based on the value of that? Granted, I have no idea if this works when multiple keys are pressed at the same time, it may still run in to the exact same problem. I haven't used Tk in ages.
"<Key> The user pressed any key. The key is provided in the char member of the event object passed to the callback (this is an empty string for special keys)."

my tkinter window doesn't open up when I add the sleep() part

I am trying to make a ball go to one side of the screen turn around, then come back. Whenever I try running this program the tkinter window doesn't show up, but when i get rid of the sleep(0.5) part it shows up when the ball is already off the screen. Can someone tell my what I did wrong?
from tkinter import *
from time import sleep
window = Tk()
cWidth = 800
cHeight = 500
c = Canvas(window, width = cWidth, height = cHeight, bg='black')
c.pack()
x = 400
y = 250
ball = c.create_polygon(x, y, x, y+25, x+25, y+25, x+25,y, fill='yellow')
Ball_move = 10
for i in range(200):
c.move(ball, Ball_move, 0)
window.update
x += Ball_move
if x == cWidth:
Ball_move = -Ball_move
sleep(0.5)
window.mainloop()
In windows, Tkinter frame shows up only after you call mainloop(). In your case, the for loop might be blocking it. Keep the for loop in a function and then call that function using threads so that it won't block the main loop.

Categories

Resources