So I've created a class, which contains GUI using wxPython.
How do you make it so that it refreshes itself say every minute?
For things that happen on intervals, use a Timer. From WxPyWiki:
def on_timer(event):
pass # do whatever
TIMER_ID = 100 # pick a number
timer = wx.Timer(panel, TIMER_ID) # message will be sent to the panel
timer.Start(100) # x100 milliseconds
wx.EVT_TIMER(panel, TIMER_ID, on_timer) # call the on_timer function
For some reason, this code didn't work when I tried it. The reason was the timer had to be a class member. If you put that code into the init() method and add self. before timer, it should work. If it doesn't, try making on_timer() a class member too. -- PabloAntonio
I've had problems closing my frame, when there was a Timer running.
Here's how I handled it:
def on_close(event):
timer.Stop()
frame.Destroy()
wx.EVT_CLOSE(frame, on_close)
I don't work with wxPython, but if there is a method called refresh or something alike, you could start a Thread calling that method every minute.
from threading import Thread
from time import sleep
def refreshApp(app, timespan):
while app.isRunning:
app.refresh()
sleep(timespan)
refresher = Thread(target=worker, args=(myAppInstance, 60))
refresher.start()
EDIT: fixed code so it fits into PEP8
As Niklas suggested I think you're looking for the Refresh() method: http://wxpython.org/docs/api/wx.Window-class.html#Refresh .
Related
I want to make a simple application that allows you to draw in a canvas but also have a timer count how long you have been drawing. I got the application to allow me to draw and display the time but I have no idea how to have the timer update while drawing in the canvas.
Something like this should work:
import time.time
from threading import Timer
# for further usage keep your label in a global variable / class attribute
# _timer_label =
def update_timer():
_time_label.set(str(time.time()))
new_timer = Timer(30.0, update_timer)
new_timer.start()
You need to call update timer once after you have configured your label variable and it will keep running.
As a side note, when you ask a question on StackOverflow try to give context, and code of what you tried.
I borrowed a design that I found on stackoverflow to redirect console output to a PyQt5 GUI textEdit widget. This works fine, but the text is not displayed in "real-time". It seems to output the text to the GUI once a process has completed. This has not been a problem until I tried to use time.sleep(secs) to print something, pause, then print something else. What ends up happening is that the program pauses for secs, then it prints all of the statements at once.
This class is in the mainWindow file for the GUI:
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
This is in the __init__ method of my event handling file:
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
self.case_setup_console.setReadOnly(True)
self.main_console.setReadOnly(True)
This function is in the main class of event handling file (outside __init__):
def normalOutputWritten(self, text):
"""Append text to the QTextEdit."""
# Maybe QTextEdit.append() works as well, but this is how I do it:
cursor = self.case_setup_console.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.case_setup_console.setTextCursor(cursor)
self.case_setup_console.ensureCursorVisible()
This works as intended to re-route the output to the text edit widget self.case_setup_console. But, when I try to run a code such as:
print('This is the first message')
time.sleep(5)
print('This should print 5 seconds later')
What happens is that the program waits 5 seconds, then it prints both statements together.
When programing for GUI code, there is a fundamental shift in how the program is designed. To make it short: after building and initialisation, the program is all the time running in an "event loop" provided by the GUI framework, and your code is only called when specific events take place.
That is in contrast with a terminal application where your code is running all the time, and you tell when to do "print"s, "input"s and pauses with "time.sleep".
The GUI code is responsible for taking notes of events (keyboard, UI, network, etc...), redrawing window contents and calling your code in response to events, or just when it is time to redraw a content that is defined in your code (like updating a background image).
So, it can only render the text that is supposed to show up in a window, with your redirected "print", when the control is passed back to its event loop. When you do time.sleep you pause the return - no code in the event loop is run, and it can't, of course, do any screen drawing.
What is needed is that you write your pauses in the program in a way that during the pause, the GUI event loop is running - not "time.sleep", that just suspends your whole thread.
In Qt the way to do that is create a QTimer object to call the code you want to use to print text at a particular moment, and then just surrender the execution to the the QtMainloop by returning from your function.
Thanks to Python's support for nested functions, that can be done in painless ways, even using lambda functions when setting the timer itself.
...
print('This is the first message')
timer = QtCore.QTimer
timer.singleShot(5000, lambda *_: print('This should print 5 seconds later'))
Should work for the given example. (The call, as usual for UIs, takes the pause time in miliseconds rather than in seconds).
If you will need to schedule more text to be printed after each phrase is output, you will need to call the scheduling inside the callback itself, and will need a little more sophistication, but it still could be as simple as:
phrases = iter(("hello", "world", "I", "am", "talking", "slowly!"))
timer = QtCore.QTimer()
def talker(*_):
phrase = next(phrases, None)
if not phrase:
return
print(phrase)
timer.singleShot(1000, talker)
timer.singleShot(1000, talker)
(Note that there is nothing special about the *_ parameter name either: I am just indicating that there might be any number of positional arguments for the callback - (The "*" part, that is Python syntax) - and that I won't care about then (I call the argument sequence as "_" to indicate I don't care how this is called, as it won't be used anyway - that is a coding convention) )
The iter and next calls are more "Python dialect" than one might be used, but one could just use a list and a counter up to the list length all the same.
i try to start and stop a while-loop with a simple checkbox. But i don't know how i am able to update the checkbox-state in the while loop.
Below you can see what i have, but when i uncheck the box, the while loop won't recognize it.
I searched through the interactive functions but have not found any function to get the updated state of the checkbox.
Hope you guys can help me out :)
import ipywidgets
import time
def f(x):
while x==True:
print("!")
time.sleep(1)
c=ipywidgets.interactive(f,x=False)
c
If you create the checkbox explicitly with:
checkbox = widgets.Checkbox(description='click me')
... you can get the current value with checkbox.value.
Annoyingly, that's not enough to just allow your checkbox to halt the execution of a while loop. Once your while loop has started, it occupies the Python interpreter. There is therefore no space for the checkbox changes to be interpreted until the while loop finishes.
Since this is fundamentally a concurrency problem (you want to react to changes in the checkbox while also running a computation), you need to use Python's concurrency primitives. You can, for instance, achieve this with coroutines:
import ipywidgets as widgets
import asyncio
class HaltableExecutor:
def __init__(self, checkbox):
self._checkbox = checkbox
async def my_code(self):
# This is your user code
while True:
if self._checkbox.value:
print('running') # put your code here
await asyncio.sleep(0.1) # use this to temporarily give up control of the event loop to allow scheduling checkbox changes
def start(self):
print('starting')
asyncio.ensure_future(self.my_code())
Then, create and display your checkbox:
c = widgets.Checkbox(description='click me')
c
... and start your executor:
exe = HaltableExecutor(c)
exe.start()
The while loop will now run. At every iteration, we pause to give up control of the event loop with asyncio.sleep. The amount we sleep by is irrelevant, the point is to give control back. This gives the main thread an opportunity to deal with outstanding messages like checkbox changes. At the next iteration of the while loop, self._checkbox.value is checked again and so on.
So I want to know how I can make a delay between executing two functions. The goal is to replace regular, blank button by black after it was on screen for one second. My current program, simplified looks like this, and it just delays the the execution of CreateInterface():
class Program(Frame):
def __init__(self,root):
self.root=root
self.root.title('Test')
super().__init__(self.root)
self.grid()
self.Start()
return
def Start(self):
startbtn=Button(self,width=5, font=('Calibri',16,'bold'), height=2, text='start',command=lambda:self.CreateInterface())
startbtn.grid(row=1,column=1)
def CreateInterface(self):
time.import
btn1=Button()
btn1.grid(row=1,column=1)
time.sleep(10)
self.Function2(self)
return
def Function2(self):
btn2=Button(bg='black')
btn2.grid(row=1,column=1)
return
In a GUI interface, calling time.sleep makes the whole process wait, so the application appears to freeze. With Tk in Python, a way to do is to use the Tk after method on a window or frame, and then call a function that makes the necessary change to your Button. There are examples of how to do this at How to create a timer using tkinter
Use time.sleep to pause program execution for a certain amount of time. If you wanted to pause for 1 second after calling CreateInterface, change it to this:
def CreateInterface(self):
btn1=Button()
btn1.grid(row=1,column=1)
time.sleep(10)
self.Function2(self)
time.sleep(1)
Don't forget to import time when you do this.
I'm using QSlider in my GUI application in order to perform a heavy task after value changed in the QSlider. I'm doing that as follows.
self.slider.valueChanged.connect(self.value_changed) # Inside __init__() function
def value_changed(self): # Inside the class
# Do the heavy task
But I can't change the value of the slider smoothly because the heavy task is running every time I change the value.
What I need is to run the heavy task after the value changed but only if the value of the slider is not changing for a while.
I can't figure out how to do this in python. Any help..?
I am no expert, and I arrive long after the battle, but i needed a thing like that too, and i don't understand a thing about the timers. It worked fine for one slider, but i needed 3. So I came up with this solution : when the slider is pressed, I disconnect the valueChanged slot, and when the slider is released, I reconnect it and I throw a valueChanged signal, like this :
self.sldAZap.valueChanged.connect(self.sliderChanged)
self.sldAZap.sliderPressed.connect(self.sldDisconnect)
self.sldAZap.sliderReleased.connect(self.sldReconnect)
def sldDisconnect(self):
self.sender().valueChanged.disconnect()
def sldReconnect(self):
self.sender().valueChanged.connect(self.sliderChanged)
self.sender().valueChanged.emit(self.sender().value())
def sliderChanged(self):
print(self.sender().objectName() + " : " + str(self.sender().value())
With this solution, there is no delay between the moment the mouse is released and the execution of the code, and the code is executed just one time.
I hope I am clear enough and it may help someone.
You can use startTimer/killTimer to delay your task:
class Foo(QWidget):
def __init__(self):
super().__init__()
self.timer_id = -1
self.slider = QSlider(self)
self.slider.setMinimum(0)
self.slider.setMaximum(100)
self.slider.valueChanged.connect(self.value_changed)
def timerEvent(self, event):
self.killTimer(self.timer_id)
self.timer_id = -1
heavy_task()
def value_changed(self):
if self.timer_id != -1:
self.killTimer(self.timer_id)
self.timer_id = self.startTimer(3000)
so, as can you see we restart timer every time when
user something change, so if 3000 millseconds not expires
since last change heavy_task not run,
but any way it will be running in main thread, so for some
time interface freeze for user, so you should use
QThread in timerEvent to not have interface that not freeze during
heavy_task execution.