I am having trouble understanding kivy's custom events. I am making a simple music player using kivy and I want some basic functionality: when a song ends, the next one plays. Kivy's sound class provides 2 events, on_press and on_stop.
Obviously, the correct choice here would be on_stop. However, this event is triggered both when the song ends naturally and when the user stops it manually. Thing is, I don't want the latter to happen, so I decided I should create a custom event. The resources available online to help me understand custom events are very few and confusing. Here's the example in the kivy docs:
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.register_event_type('on_swipe')
def on_swipe(self):
pass
def on_swipe_callback(*largs):
print('my swipe is called', largs)
w = MyWidget()
w.dispatch('on_swipe')
So, we have the event (on_swipe) and an event handler (on_swipe_callback). When is this event supposed to be triggered? How do I define that? Let's say I want my event to be called on_finish, and I want it to be triggered when a song reaches the end of its duration. How and where do I write the details about that? I'd appreciate it if someone can help me understand how these events work.
The event is triggered exactly when dispatch is called. What is missing in the bit of code in your question is the binding. You'd have something like
w = MyWidget()
w.bind(on_swipe=on_swipe_callback)
This tells w to call on_swipe_callback when the event on_swipe occurs.
Regarding your original question, this may be more difficult than it looks, because at the time of an on_stop the current position within a sound is not known.
A rather hackish solution might be to use Clock to schedule a function that checks whether the sound is still playing just before the end of the sound. Of course, this would be off e.g. if the user seeks. In the end, you might have to directly change the Sound class in kivy.audio to generate new events.
Another option would be to check in your on_stop callback if the Stop button was pressed in your UI. If it wasn't, then the song ended “naturally”. You'd set a flag in your on_press handler for the button and clear it in your on_stop handler.
Related
I am currently trying to stop mouse clicks while my Script is running and still work with them.
If you are confused about the use case I will elaborate at the end of the question.
Currently I can get clicks like this:
import mouse
import time
def mouseHook(event):
if type(event) == mouse.ButtonEvent:
print(event)
mouse.hook(mouseHook)
while 1:
time.sleep(0.25)
But this still lets the clicks go through, how would I intercept them?
use case: simulate a monitor and while mouse is on that monitor send all movements, clicks and keypresses to MacBook (similar to Synergy, Mouse without Borders or Share Mouse)
The hook used by the application only 'hooks' into the process, which means it gets information from it, but can't insert or modify it's code.
For reference on windows that would use (https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644988(v=vs.85))
The easiest way would be to have your application ignore the mouseclicks.
I need to send updates to tkinter from another thread, which should be handled immediately. Sadly Tkinter freezes whenever I try multithreading.
My Attempt:
I've read through several Tkinter-threaded pages but didn't find anything that works well, since most of them try creating a new thread on button.click(), which doesn't help here.
I tried not calling .mainloop() and instead calling the update functions myself, whenever an update comes in:
#GUI
def update(self, string):
self._interactor.config(text=string)
#interactor is a button
self._tk.update_idletasks()
self._tk.update()
This works fine until I use a loop with sleep() in the master to constantly update the text. GUI and master are frozen during sleep().
So I tried to use a threaded timer as discussed here.
#MASTER
gui=gui_remote(self)
def changetext():
text=self.gettextsomewhere()
self._gui.update(text)
loop=RepeatedTimer(5, changetext)
But this just leads to the following error, thrown by Tkinter:
RuntimeError: main thread is not in main loop
Having a hard time on how to solve this. Is it possible to call a GUI class on main thread and still have proper access to its functions?
How I got to this point:
For my project, I need a button, which represents several Buttons.
Every y (eg. 1.5) seconds, the displayed text should be updated to a new one from the outside.
Also, I want to keep GUI, Controller and Data separated, (using blueprint methods) so that later adjustments on each of them will be easier.
I already got it to work, using TK's .after() function, but I had to use GUI and controlling functions closely together.
My Plan
Have a GUI class, which is updateable from another object via simple public functions. The other object (the master) should be able to create a GUI object and call the GUI's update functions with new data every y seconds.
When the GUI button is clicked, simply call a certain method at master every time:
#GUI example
from tkinter import Tk, Button, Frame, Label
class gui_sample:
def __init__(self, master):
"""This is the very simple GUI"""
self._master=master
self._tk=Tk()
self._interactor= Button(self._tk, text="Apfelsaft", command=self._click)
self._interactor.pack()
self._tk.mainloop()
def update(self, string):
"""Handle interactor update"""
self._interactor.config(text=string)
def _click(self):
self._master.click()
#MASTER
from gui_module import *
class Controller:
def __init__(self):
self._gui=gui_sample(self)
self._run()
def _run(self):
#call this every 5 seconds
new_text=self.gettextfromsomewhere()
self._gui.update(new_text)
def click():
#do something
pass
#this code is just a blueprint it probably does nothing
My Problem:
I don't want the master to use TK functions since I might switch to another UI module later and keep the master's functionality. The master will constantly loop through what's being displayed next and needs to be accessible at the same time. Using loops with sleep() isn't a good idea since they will block both, the master and the GUI. Calling .mainloop() is also problematic since it will block all other programs. The gui should always respond to updates and not ask for them.
I recently switched from wxPython to PyQT and can't find an equivalent of CallAfter. I need to use pubsub due to some imports and with wx I just sent messages with CallAfter -- is there a way to do something similar in PyQT? Basically, I want to inject something into the mainloop with pyQT.
EDIT FOR MORE INFO:
In my old GUI, using wxPython, I was using python-openzwave which uses an old dispatcher module. I would capture the old dispatcher signals and convert them to pubsub messages (for ease of use) and send the new messages with a CallAfter like this:
wx.CallAfter(pub.sendMessage, messagePack.signal, message = messagePack.message)
And then I was able to update the GUI by capturing the message and working directly on the gui elements because it essentially injected something into the mainloop.
Now, using pyqt, there is no callafter so, I have the same system setup without the callafter but the actions that have to occur after the message is received can't happen because it is in the middle of the mainloop.
The closest thing I can think of is using QTimer.singleShot with a short timeout, which will force it into the next event loop.
def other_function(self):
print 'other'
def my_function(self):
print 'one'
QTimer.singleShot(1, self.other_function)
print 'two'
Qt has the idea of an event loop, where it will check if there are events that need processing, like a button click, or part of a widget needs to be redrawn, etc. Typically, a function gets called as the result of an event. The QTimer.singleShot will stick your function call at the end of the list of things to be processed on the next cycle of the event loop.
But I agree with some of the comments that you probably could just use a separate QObject running in another thread to handle the openzwave events and re-dispatch the messages as Qt Signals, which the main thread can listen for and update the GUI.
I'm currently need GUI library for a project. I'm familiar with python and found PyQt might be a good choice.
I'm reading a tutorial about PyQt, and quite confused about the following example program
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
ZetCode PyQt4 tutorial
In this example, we draw text in Russian azbuka.
author: Jan Bodnar
website: zetcode.com
last edited: September 2011
"""
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.text = u'\u041b\u0435\u0432 \u041d\u0438\u043a\u043e\u043b\u0430\
\u0435\u0432\u0438\u0447 \u0422\u043e\u043b\u0441\u0442\u043e\u0439: \n\
\u0410\u043d\u043d\u0430 \u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430'
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('Draw text')
self.show()
def paintEvent(self, event):
qp = QtGui.QPainter()
qp.begin(self)
self.drawText(event, qp)
qp.end()
def drawText(self, event, qp):
qp.setPen(QtGui.QColor(168, 34, 3))
qp.setFont(QtGui.QFont('Decorative', 10))
qp.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here, in main function, an Example object is created, thus __init__() function, initUI() is called.
My question is where does paintEvent() function is called?? since if we run the program, self.text(some Russian letters) will exactly appear on the widget.
In other words, what does sys.exit(app.exec_()) actually do? why it will call paintEvent() function?
Thanks!
From PyQt docs:
int QApplication.exec_ ()
Enters the main event loop and waits until exit() is called, then
returns the value that was set to exit() (which is 0 if exit() is
called via quit()).
It is necessary to call this function to start event handling. The
main event loop receives events from the window system and dispatches
these to the application widgets.
From another source:
sys.exit(app.exec_())
Finally, we enter the mainloop of the
application. The event handling starts from this point. The mainloop
receives events from the window system and dispatches them to the
application widgets. The mainloop ends, if we call the exit() method
or the main widget is destroyed. The sys.exit() method ensures a clean
exit. The environment will be informed, how the application ended.
The exec_() method has an underscore. It is because the exec is a
Python keyword. And thus, exec_() was used instead.
About painting:
4.2.1. When Painting Occurs
The paintEvent() method is called automatically when
Your widget is shown for the first time.
After a window has been moved to reveal some part (or all) of the
widget.
The window in which the widget lies is restored after being minimized.
The window in which the widget lies is resized.
The user switches from another desktop to the desktop on which the
widget's window lies.
You can generate paint events manually by calling QWidget::update().
QWidget::update() erases the widget before generating the paint event.
You can pass arguments to update(), which can restrict painting only
to areas (rectangles, in particular) that need it. The two equivalent
forms of the method are QWidget::update (int x, int y, int width, int
height) and QWidget::update (QRect rectangle), where x and y give the
upper-left corner of the rectangle, and width and height are obvious.
Because update() places a paint event into the event queue, no
painting occurs until the current method exits and control returns to
the event handler. This is a good thing because other events may be
waiting there to be processed, and events need to be processed in a
timely manner for the GUI to operate smoothly.
You can also invoke painting of the widget by calling QWidget::repaint (int x, int y, int width, int height, bool erase) (or one of several
convenience-method forms), where all the arguments mean the same as in
the case of the update() method, and erase tells repaint whether to
erase the rectangle before painting it. repaint() calls paintEvent()
directly. It does not place a paint event into the event queue, so use
this method with care. If you try to call repaint() repeatedly from a
simple loop to create an animation, for example, the animation will be
drawn, but the rest of your user interface will be unresponsive
because the events corresponding to mouse button clicks, keyboard
presses, and so on will be waiting in the queue. Even if you are not
performing a task as potentially time-consuming as animation, it is
generally better to use update() to help keep your GUI alive.
If you paint something on your widget outside the paintEvent(), you
still need to include the logic and commands necessary to paint that
same thing in paintEvent(). Otherwise, the painting you did would
disappear the next time the widget is updated.
It becomes more clear when one has some experience with low level programming,
for example in Winapi or the X toolkit in C language. PyQt is a (very) high level toolkit.
It comes with a huge built-in functionality. Similar example would require hundreds or maybe thousands of lines of C code. As a consequence, there is a lot of going on
behind the scenes. Somebody already created code that deals with painting on a basic level.
GUI programming is very complex and with modern GUI toolkits the application programmer is shielded from this complexity. It is inevitable that programmers are confused if they do not know all the technical details.
In PyQt, we are essentially dealing with events in two ways. We connect signals to slots or reimplement event handlers (an event handler is a synonym for a slot). The provided example inherited from the QtGui.QWidget which already has some painting code available.
In order to do our custom painting, we have to reimplement the existing paintEvent() event
handler. Depending on the situation, we may or may not call the parent's paintEvent() method.
The sys.exit(app.exec_()) does not call the paintEvent() method. The exec_() method starts an event loop. The event loop catches and dispatches events. Paint events are triggered by users or by the operating system. For example, when we launch the example, the
paint event is triggered twice. (Put a print "Event occurred" line in the event handler to see how many times this method is called.) Resizing windows, loosing or gaining focus, minimizing or maximizing windows, all these cause painting events to be triggered.
app.exec_() starts the qt main loop, which ends when every created widget is destroyed (e.g. by closing its window). The paintEvent function is a method that you can overload from a QWidget subclass like the given Example class, which gets called when QT displays, updates or repaints the Widget.
You can look up these things in the Qt documentation or the PyQt Documentation (which is mostly just a copy of the QT Documentation in a different format, but sometimes contains some valuable information regarding PyQt-specific things).
I recently asked this question in the pyglet-users group, but got response, so I'm trying here instead.
I would like to extend Pyglet to be able to use an infra red input device supported by lirc. I've used pyLirc before ( http://pylirc.mccabe.nu/ ) with PyGame and I want to rewrite my application to use Pyglet instead.
To see if a button was pressed you would typically poll pyLirc to see if there is any button presses in its queue.
My question is, what is the correct way in Pyglet to integrate pyLirc?
I would prefer if it works in the same was as the current window keyboard/mouse events, but I'm not sure where to start.
I know I can create a new EventDispatcher, in which I can register the
new types of events and dispatch them after polling, like so:
class pyLircDispatcher(pyglet.event.EventDispatcher):
def poll(self):
codes = pylirc.nextcode()
if codes is not None:
for code in codes:
self.dispatch_event('on_irbutton', code)
def on_irbutton(self, code):
pass
But how do I integrate that into the application's main loop to keep on calling poll() if I use pyglet.app.run() and how do I attach this eventdispatcher to my window so it works the same as the mouse and keyboard dispatchers?
I see that I can set up a scheduler to call poll() at regular intervals with pyglet.clock.schedule_interval, but is this the correct way to do it?
It's probably too late for the OP, but I'll reply anyway in case it's helpful to anyone else.
Creating the event dispatcher and using pyglet.clock.schedule_interval to call poll() at regular intervals is a good way to do it.
To attach the event dispatcher to your window, you need to create an instance of the dispatcher and then call its push_handlers method:
dispatcher.push_handlers(window)
Then you can treat the events just like any other events coming into the window.
The correct way is whatever works. You can always change it later if you find a better way.