With wxPython, how does one trigger an event whenever the whole window goes into/out of focus?
To elaborate, I'm building a serial terminal GUI and would like to close down the connection whenever the user doesn't have my application selected, and re-open the connection whenever the user brings my app back into the foreground. My application is just a single window derived from wx.Frame.
The correct answer for this case is to use an EVT_ACTIVATE handler bound to the frame. There will be an event whenever the frame is activated (brought into the foreground relative to other windows currently open) or deactivated. You can use the event object's GetActive method to tell which just happened.
as WxPerl programmer i know there is
EVT_SET_FOCUS(
EVT_KILL_FOCUS(
if you initialize this event by listening to the frame as first parameter it should work as in Perl since the API is almost the same
Interesting article at http://www.blog.pythonlibrary.org/2009/08/27/wxpython-learning-to-focus/
Gist of it: wx.EVT_KILL_FOCUS works fine, but wx.EVT_SET_FOCUS behaves a little oddly for any panel containing widgets (the child's set-focus prevents the panel's set-focus event from firing as expected?)
In addition to what these fellows are saying, you might also want to try EVT_ENTER_WINDOW and EVT_LEAVE_WINDOW. I think these are fired when you move the mouse into and out of the frame widget, although I don't think the frame has to be in focus for those events to fire.
# Hugh - thanks for the readership!
Related
I am building an app that, when the user hits a 'run' button, generates a table of buttons.
Because this process takes a while, I want to add a popup or progress bar to alert the user that the function is running and not frozen. To do this I decided to create a popup and call my function using threading so that the screen will be updated when the function starts (as opposed to once it is done).
mythread = threading.Thread(target=run_function)
mythread.start()
The trouble is that when I call my function from the above code it works very strangely: the columns of my table are the wrong width, some of my buttons are arbitrarily empty, and others have the wrong fill color. To fix this, all I need to do is to remove the threading operation and simply call run_function()
Any idea why this is happening?
I am new to Python, so it is likely some dumb mistake, but I have no idea. What is different between a process running as a thread and its default operation?
Disclaimer: I haven't worked with Kivy.
Not every framework works well with multithreading.
But most of the GUI frameworks have an event loop which is responsible for managing user events (mouse clicks, keyboard) and queued drawing operations (widgets).
In your case if don't want the UI to be freezed, you should regularly give control to your framework's event loop.
I guess kivy.base.EventLoopBase.dispatch_input is what you need to call to show an added widget or to handle user events.
The Problem:
It seems like one event is disabling another event. Before calling event B, event A works just fine. After event B fires, event A no longer works. Both events are custom ones that people made for their wxPython libraries (FloatCanvas and ColourSelect). I'd like to trace the generated events to make sure that, after event B, event A is still being fired (but perhaps not triggering the handler code?)
The Details:
I have a wxPython app where I create a wx.Panel object with two child items - a plot and a legend. This top level panel handles the majority of the events.
The plot is a wx.Panel object with a single wx.lib.FloatCanvas.FloatCanvas canvas in it.
The legend is a wx.Panel with multiple wx.StaticText and wx.lib.colourselect.ColourSelect objects in it (I'm plotting discrete data points).
On the plot, I bind FloatCanvas.EVT_MOUSEWHEEL to my zoom in/out function. On the legend, I bind the wx.lib.colourselect.EVT_COLOURSELECT event to my update_colors function which then sends the event to the parent panel via wx.PostEvent(event).
The parent panel then receives EVT_COLOURSELECT from the child and executes code that changes the colors of my plots.
Source Code:
I haven't had time to write a small sample that demo's the problem, but you can see the problem by running the source code:
https://github.com/dougthor42/wafer_map
Run the wm_app.py file and then go through the following test steps.
Testing Steps:
Scroll/zoom in and out - works just fine
Change a plot color (fires EVT_COLOURSELECT). If you're testing with the source code you do this by clicking on a legend color box and choosing a new color.
Attempt to zoom in and out again. Doesn't work!
Verify that all other events (key down, click-and-drag, mouse move) all work.
Things I've Tried:
Obviously these didn't work or else I wouldn't be here :-P
Unbinding the mousewheel event and rebinding it when the top level panel receives the event from the child.
Completely disabling the handlers for EVT_COLOURSELECT in both the parent panel and the legend.
I thought that perhaps there was something going on in my handlers. Turns out, just
triggering EVT_COLOURSELECT causes the EVT_MOUSEWHEEL to stop working.
Changing FloatCanvas.EVT_MOUSEWHEEL to some other event, for example FloatCanvas.EVT_RIGHT_DOWN
This does actually work! I loose the 'speed' info from the mouse wheel, but right-clicking
before and after step 2 both work.
Seems to indicate that it's an issue with the mouse scroll event specifically.
Has anyone had a problem where events interfere with each other?
It turns out that there wasn't any interference with events - at least not the events that I mentioned.
After a ton of fiddling and attempts at making a bare-bones version that still demonstrated the problem (which I was unsuccessful at doing), I figured out the root cause. Sadly, I did not figure out a workaround.
The Problem:
My plot wx.Panel was binding wx.MOUSE_LEFT_DOWN. It seems that this was preventing the parent wx.Frame window from giving focus to the Panel that held the plot and the legend.
The Solution:
I removed the bind to wx.EVT_LEFT_DOWN and that seems to fix the underlying issue. However, this is still not the ideal case because it causes two other problems:
I can no longer use my left mouse button on the plot. Not a big deal, I guess, because I can always bind right-mouse.
A user must first left-click on the plot area to give it focus before the mouse scroll or any keyboard shortcuts will work. This doesn't necessarily stem from not binding wx.EVT_LEFT_DOWN, but rather (I believe), from something that Windows does. See Focus-follows-mouse in wxPython? for a bit more info.
Failed Workaround:
I tried a workaround: adding code that sends the wx.EVT_LEFT_DOWN event to the parent but either that still didn't work or I was doing it wrong. Here's the left-click event handler that I tried:
def left_click(self, event):
print("left click!")
parent = wx.GetTopLevelParent()
wx.PostEvent(self.parent, event)
So anyway, this question is kinda solved. Hopefully what I've got here helps someone else out.
I have a Qt program with many buttons, user-interactable widgets, etc.
At one stage in the program, I would like all the widgets to temporarily 'stop working'; stop behaving to mouse clicks and instead pass the event on to one function.
(This is so the User can select a widget to perform meta operations. Part explanation here: Get variable name of Qt Widget (for use in Stylesheet)? )
The User would pick a widget (to do stuff with) by clicking it, and of course clicking a button must not cause the button's bound function to run.
What is the correct (most abstracted, sensible) method of doing this?
(which doesn't involve too much new code. ie; not subclassing every widget)
Is there anything in Qt designed for this?
So far, I am able to retrieve a list of all the widgets in the program (by calling
QObject.findChildren(QtGui.QWidget)
so the solution can incorporate this.
My current horrible ideas are;
Some how dealing with all the applications events all the time in one
function and not letting through the events when I need the
application to be dormant.
When I need dormancy, make a new transparent widget which recieves
mouse clicks and stretch it over the entire window. Take coordinates
of click and figure out the widget underneath.
Somehow create a new 'shell' instance of the window.
THANKS!
(Sorry for the terrible write-up; in a slight rush)
python 2.7.2
PyQt4
Windows 7
You can intercept events send to specific widgets with QObject::installEventFilter.
graphite answered this one first so give credit where credit is due.
For an actual example in PySide, here's an example you might draw some useful code from:
my_app.py
from KeyPressEater import KeyPressEater
if __name__ == "__main__":
app = QApplication(sys.argv)
eater = KeyPressEater()
app.installEventFilter(eater)
KeyPressEater.py
class KeyPressEater(QObject):
# subclassing for eventFilter
def eventFilter(self, obj, event):
if self.ignore_input:
# swallow events
pass
else:
# bubble events
return QObject.eventFilter(self,obj,event)
As explained here, I have a simple tray icon using PyGTK.
Being very new to GTK, it appears to me that gtk.main() is synchronous, blocking any further processing until the respective UI is closed.
So how can I periodically (e.g. every 5 seconds) update/refresh StatusIcon's icon - do I have to resort to Twisted et al. for this?
You can use gobject.timeout_add() to add periodical tasks. The first parameter is the interval in seconds, the second parameter is the callback you want to be called. The callback is called as long as it returns something that evaluates to True when used as a bool.
See also section 20 of the PyGTK FAQ, in particular question 20.7.
You can't refresh easily the tray icon , take it from some one that did already try this and failed (Actually i just drop it for the reason that "If the implementation is hard to explain, it's a bad idea.") ,
but here is what i have so far, to refresh the Status Icon you will have to set your status icon using gtk.status_icon_new_from_gicon() method, from the doc :
Creates a status icon displaying a
gio.Icon(). If the icon is a themed
icon, it will be updated when the
theme changes.
So if you want to refresh the Status Icon you will have to create your icon using gio themed icon , and now you can update directly your themed icon and this change will be shown directly in the tray status.
Now about your question :
Yes gtk.main() block waiting for signal, so you just have to bind a signal to an action and when this signal is triggered the action can be executed , for your case in your action callback you can put the code that will refresh your Status Image.
Hope i did help you here.
I use GtkAboutDialog and everything works fine except the close button of this widget. All other buttons works fine, I don't know how but all buttons have default callbacks and they create and destroy the windows.
But the "Close" button of GtkAboutDialog widget does not work. I can not even see it's widget. So, can I access it?
[CLARIFICATION] What you're looking at is gtk.AboutDialog — popup window displaying information about an application (new in PyGTK 2.6). This window contains the 'close' button widget which is contained in a GtkHButtonBox widget. The GtkHButtonBox widget is the highest level widget I am able to access for some. Any ideas on how to get to the "close" button and connect a handler for a callback signal?
You don't conenct signals in the same way for a dialog as you do for a window. I made the same mistake when learning PyGTK.
The most basic form of a dialog is you display and run the dialog with:
aboutdialog.run()
Often you will then immediately call:
aboutdialog.destroy()
The .run() line is a loop, which runs until something happens within the dialog.
There is a working example here.
The gtk.AboutDialog is just a gtk.Dialog, and you handle responses from it in the same way. Instead of connecting to the clicked signal of the buttons, the dialog code handles that for you and returns a reponse from your run() call. You can check the value of the response returned to figure out what button was clicked.
If you're trying to override some behaviour instead, you can connect to the response signal of gtk.Dialog.
This is an old question, but since it's one of the first hits from google, I thought I'd throw in the solution that I found. You need an event handler to show the about dialog and one to close it. The first will likely be connected to your help->about menuitem's activate signal. The latter should be connected to the response signal of the about dialog. The two handlers will look something like this:
def on_menuitemHelpAbout_activate(self, *args):
self.builder.get_object('aboutdialog').show()
def on_aboutdialog_response(self, *args):
self.builder.get_object('aboutdialog').hide()
In the example above, I'm using the GtkBuilder to find my about dialog because I've constructed the interface with glade. Note that I'm using .show() over .run() because I don't see the sense in pausing program execution until the dialog is closed. Finally, the response handler can be made to take whatever action depending upon the response, but I'm ignoring it here.