Is there an instant-updating function for texts or patterns as button.config() in tkinter? - python

I wrote a modified program of the 'mines' game, and I hope it shows every step/click graphically. I use time.sleep(0.5) to make a pause. So, in general the main program is like:
check_block():
if mine == 0:
buttons[current].config(image = tile_clicked)
elif mine == 1:
buttons[current].config(image = tile[1])
...
while(1):
time.sleep(0.5)
check_block()
get_next()
if check_fail():
break
However, the buttons don't update every 0.5 second: they are all updated together when the game(loop) finishes.
I guess it's just like 'cout' in C++: if you don't flush they will get stacked. So, is there a method to get them updated step by step, or say, instantly?
Thanks!

In all GUI systems you have to allow the message loop to run so that Windowing events occur promptly. So do not use a while loop like this. Instead, create a method that calls check_block() and get_next() and use after to call that function after a delay. At the end of that function, you use after again to call the same function again so that this function is called every 0.5 second forever. The after function queues a timer event and then lets the message queue be processed. Once your timer event fires, the callback function is run which allows you to do things and keep the UI responsive.

You should never call sleep in a GUI program. This is because the GUI must be "awake" at all times so that it can service events (including internal events that cause the screen to update). Instead, leverage the already-running eventloop by using the after method to put events on the queue at regular intervals.
In your case, you would replace the while loop with something like:
def do_check():
check_block()
if not check_fail():
root.after(500, do_check)
# in your initialization code, start the loop by calling it directly:
do_check()
I don't know what your get_next function does, so I don't know if you need to call it periodically too. Probably not. I'm guessing it waits for the next button press, which you don't need to do with tkinter or most other GUI toolkits. Instead, you configure the button to call a function when clicked.
Regardless, the way to do the type of looping you want is to place events on the event queue at a regular interval.

Related

A confirmation message that disappears pyqt5 [duplicate]

This question already has answers here:
Equivalent to time.sleep for a PyQt application
(5 answers)
Closed 1 year ago.
I would like to display a confirmation image like below, which disappears in 2s after being launched. I can display the image but I cannot make it disappear afterwards. I tried to use a sleep (2) but in this case the image turns all black for every 2 seconds. thank you for helping me please
Your application turns black, because the event loop is not running.
Qt runs by having an event loop run forever. It waits for events and processes them as they come in the queue. The event loop is getting an event, calling your function, and waiting for your function to finish until it moves on to running the next event. Your function shows the window/widget/dialog, then sits in time.sleep for 2 seconds. While it is sitting in time.sleep the Qt Event Loop is still waiting for your function to end to process more events.
There are 3 ways to handle this situation.
QTimer (recommended for your situation).
Show your widget and tell a timer to call your function in 2 seconds.
This will make your function exit right away, so the event loop can continue processing
After 2 seconds the timer will call the close function
Thread (threads are really for I/O like TCP Sockets).
QApplication.processEvents()
Show your dialog and run a loop waiting for 2 seconds.
While the loop is running and checking if 2 seconds has passed tell the event loop to process events.
QTimer - you simply show your window then have a timer call a function to close your window.
self.widg = ...
self.widg.show()
self.widg.raise_() # if already show bring to top
def close_and_delete_widg():
self.widg.close()
self.widg.setParent(None) # Remove reference
self.widg.deleteLater()
self.widg = None # Remove python reference count.
self.tmr = QtCore.QTimer()
self.tmr.setSingleShot(True)
self.tmr.timeout.connect(close_and_delete_widg)
self.tmr.start(2000) # 2 sec
I made a library to help run things on approximate timeouts qt_thread_updater. This library works by continuously running a timer and calling function that were posted. You basically tell it to run a function, and it will run a function in the main event loop later. The delay function is not accurate. This library was made more for threading. However, it makes it so you don't need to manage your timer.
from qt_thread_updater import get_updater
self.widg = ...
self.widg.show()
self.widg.raise_() # if already show bring to top
def close_and_delete_widg():
self.widg.close()
self.widg.setParent(None) # Remove reference
self.widg.deleteLater()
self.widg = None # Remove python reference count.
get_updater().delay(2, close_and_delete_widg) # After approximately 2 seconds call
Threading - I am going to skip, because you don't need it for your use case.
QApplication.processEvents() - This is not really recommended. It can cause issues, but may still work. Essentially, the event loop is waiting for your function to finish. If you call QApplication.processEvents() you are telling your application to process more events while you are currently waiting for this event to finish.
self.widg = ...
self.widg.show()
self.widg.raise_() # if already show bring to top
start = time.time()
while (time.time() - start) <= 2: # Sec or msec?
QtCore.QApplication.processEvents() # QtCore.QApplication.instance().processEvents()
# Close and delete the widget
self.widg.close()
self.widg.deleteLater()
self.widg = None

How can my program be prevented from freezing?

I have decided to finally work on a project, as I've tried to code in python before, with at least some success. In my project, I am trying to build a menu that lets me "Auto-farm" in a game. It uses 3 modules, namely pynput, pause, and PySimpleGUI.
Whenever I run the code, it runs fine, until I click the button that starts the automation part. It runs completely fine, but I have to force close the GUI prompt that shows up as it just completely stops responding to input until you close it.
How I can make a stop button, and stop my program from freezing up?
I am using 2 files to keep this project slightly more organized, although I don't know if this is the best way to go around doing this. These 2 files are main.py and chand.py.
main.py
import PySimpleGUI as sg
import chand
loop = 0
layout = [[sg.Text("Welcome to RedGrowie's autofarm menu!")], [sg.Button("Chandeliers")]]
window = sg.Window("Autofarming Menu", layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
if event == "Chandeliers":
loop = 1
if loop == 1:
chand.Chandeliers.start(self=chand.Chandeliers)
window.close
chand.py
from pynput.keyboard import Key, Controller
import pause
keyboard = Controller()
start = "0"
class Chandeliers:
def d_press(self):
keyboard.press("d")
pause.milliseconds(70)
keyboard.release("d")
pause.milliseconds(300)
keyboard.release(Key.space)
def space_press(self):
keyboard.press(Key.space)
pause.milliseconds(1)
#keyboard.release(Key.space)
def start(self):
start = "1"
while start == "1":
self.d_press(self)
self.space_press(self)
Your Chandeliers.start function loops indefinitely, so the call from the main loop in main.py never gets returned to. If you want both loops to be running at the same time, you probably need to use threading or some other means of concurrency. Or you might be able to interleave the two loops somehow, depending on the timing requirements for each one.
As a side note, you are using your Chandeliers class in a very odd way. You're never creating an instance of the class, but rather calling the methods it defines as if they were class methods (but with manual passing of the class, in the misleadingly named self argument.
You should probably not do that. Either treat the class as a normal one, and create an instance:
cha = chand.Chandeliers()
chat.start() # and change start to not manually pass self any more
Or you should do away with the unneeded class all together and just make the methods into top-level functions.

Exit Function Into Main Code

I am trying to build a gambling dice game for fun using Python's Tkinter in Python 3. The error I am having is after the money is taken away from your bank account (it does this in a different function) I want it to go back to the mainloop. So basically I want to exit a function to get back into the main code (which isn't in a function). Any ideas on how?
I am not 100% sure what your problem is but it sounds like you might not fully understand how a function works.
In a tkinter application the mainloop() is always looping so once your function has finished the the next task in that loop will run and if no more task are scheduled in that loop the loop will reset.
Take this below code for example.
from tkinter import *
root = Tk()
def some_function():
print("this function just ran")
Button(root, text="Run some func", command = some_function).pack()
root.mainloop()
What we see is a button that allows us to call on some_function and then once that function is over we are "back in" the mainloop() so to speak.
There is nothing you need to do special here unless inside of your function you are running some kind of loop and want to end the loop on some criteria. Then you can use a break line to end that loop.
The function will automatically stop when it is finished running. Using return, though, will immediately exit the current function, perhaps before it gets through all of its code, if that's what you mean.

Simple animation with Tkinter Python

I've searched for a simple animation code with Tkinter but I've found very different examples and I can't understand the correct way to write an animation.
Here my working code to display a simple moving circle:
import tkinter as tk
import time
root=tk.Tk()
canvas=tk.Canvas(root,width=400,height=400)
canvas.pack()
circle=canvas.create_oval(50,50,80,80,outline="white",fill="blue")
def redraw():
canvas.after(100,redraw)
canvas.move(circle,5,5)
canvas.update()
canvas.after(100,redraw)
root.mainloop()
In this code I can't correctly understand: how the after method works, where correctly put the update and the move method (before after method ?), is there another way to write an animation code? may you post me another example and comment the code please?
Thanks :)
Calling update
You should not call canvas.update(). As a general rule of thumb you should never call update. For a short essay on why, see this essay written by one of the original developers of the underlying tcl interpreter.
If you take out the call to canvas.update(), you have the proper way to do animation in a tkinter program.
Calling after to start the animation
You don't need to call after immediately before calling root.mainloop(). This works just as well:
...
redraw()
root.mainloop()
The choice to use or not use after in this specific case is dependent on if you want the animation to start immediately (possibly even before the widget is visible) or if you want it to happen after a short delay (possibly after the widget is made visible)
How after works
mainloop is nothing more than an infinite loop that checks the event queue for events. When it finds an event, it pops it off of the list and processes it. after is nothing more than making a request that says "in 100 ms, please add a new event to the queue". When the time limit expires, an event is added to the queue that says, in effect, "run this command". The next time the loop checks for events, it sees this event, pulls it off of the queue, and runs the command.
When you call after from within a method that itself was called by after, you're saying in effect "wait 100ms and do it again", creating an infinite loop. If you put the call to after before moving the object, you're saying "every 100ms run this function". If you put it after you're saying "run this function 100 ms after the last time it was run". The difference is very subtle and usually not perceptible unless your function takes a long time to run.
my code is:
from tkinter import *
import time
tk = Tk()
płótno = Canvas(tk, width=500, height=500)
płótno.pack()
płótno.create_polygon(10,10,10,70,70,10,fill="blue",outline="black")
for x in range(0,51):
płótno.move(1,5,0)
płótno.update()
rest(0.05)
płótno means canvas

Sleep without lock Gtk.main() in a function

I have got a function in my gtk app. I want to wait 2 seconds in that function.
For example:
[...]
def do_something_and_wait(self):
#do something here
time.sleep(2)
# do something more
Gtk.main()
If I use time.sleep(2) directly, Gui freezes for 2 seconds.
How can I wait 2 seconds in a function without lock Gtk.main()?
You can use gtk.Main_iteration() to temporarily hand control back to the GUI, see:
http://www.pygtk.org/pygtk2reference/gtk-functions.html#function-gtk--main-iteration
and
http://book.huihoo.com/gtk+-gnome-application-development/sec-mainloop.html
This will not sleep for exactly 2s, but will pause for approximately 2s and still allow the GUI to respond to other events. As bazza points out, disabling the calling widget would also be a good idea.
def updateGUI():
'''Force update of GTK mainloop during a long-running process'''
while gtk.events_pending():
gtk.main_iteration(False)
def wait_2s():
for i in range(10):
time.sleep(0.2)
updateGUI()
You could add a gtk timeout. See
http://www.pygtk.org/pygtk2tutorial/ch-TimeoutsIOAndIdleFunctions.html
Your existing function would add the timeout instead of time.sleep(), and then return. A second function would 'do something more', and would be specified as the callback function for the timeout.
You'd probably want the first function to disable whichever GUI widget is used to set this part of your program running. This will stop the user pressing that button twice whilst you're waiting for the 2 seconds to elapse. The second function should re-enable it.

Categories

Resources