Getting global scroll events with PyObjC - python

How can I get global scroll events with PyObjC? Can it be done with an NSEvent call?
Sample code would be great...

You were on the right track with NSEvent! As long as you don't want to modify the event, but just observe it, it's as easy as calling addGlobalMonitorForEventsMatchingMask:handler:. Your app will get notified via a callback whenever an event of the type you specify with the mask is posted to another app.* The handler argument is a block, but need cause you no worry, because block arguments are almost easier to deal with in PyObjC than in straight Obj-C: you can pass any callable object (function, method, class, etc.) and the bridge will handle the rest. This is all you need to do:
def callback(event):
NSLog(u"%s" % event)
NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSScrollWheelMask, callback)
In the callback, you get a copy of the actual event, that you can query for things like its deltaX or whatever you like.
Note, that, like so many other interesting methods in AppKit, this one is new in 10.6, and isn't in the metadata file for older (read: Apple-supplied) versions of PyObjC. This means that if you try to build an app using the default install of the bridge, it will fail. You'll have to use a newer version.
*If you want to get events posted to your app, you must use addLocalMonitorForEventsMatchingMask:handler:. Unfortunately, one event monitor cannot get both events for your app and for others.

Related

Fabric - Python 3 - What is context and what does it have to contain and why do I need to pass it?

This is my fabric code:
from fabric import Connection, task
server = Connection(host="usrename#server.com:22", connect_kwargs={"password": "mypassword"})
#task
def dostuff(somethingmustbehere):
server.run("uname -a")
This code works just fine. When I execute fab dostuff it does what I want it to do.
When I remove somethingmustbehere however I get this error message:
raise TypeError("Tasks must have an initial Context argument!")
TypeError: Tasks must have an initial Context argument!
I never defined somethingmustbehere anywhere in my code. I just put it in and the error is gone and everything works. But why? What is this variable? Why do I need it? Why is it so important? And if it is so important why can it just be empty? I am really lost here. Yes it works, but I cannot run code that I don't understand. It drives me insane. :-)
Please be aware that I'm talking about the Python 3(!) version of Fabric!
The Fabric version is 2.4.0
To be able to run a #task you need a context argument. Fabric uses invoke task() which expects to see a context object. Normally we name the variable c or ctx (which I always use to make it more clear). I don't prefer using c because I use it normally for connection
Check this line on github from invoke package repo, you will see that it raises an exception when the context argument is not present, but it doesn't explain why!
To know more about Context object, what it 's and why we need it, you can read the following on the site of pyinvoke:
Aside: what exactly is this ‘context’ arg anyway? A common problem
task runners face is transmission of “global” data - values loaded
from configuration files or other configuration vectors, given via CLI
flags, generated in ‘setup’ tasks, etc.
Some libraries (such as Fabric 1.x) implement this via module-level
attributes, which makes testing difficult and error prone, limits
concurrency, and increases implementation complexity.
Invoke encapsulates state in explicit Context objects, handed to tasks
when they execute . The context is the primary API endpoint, offering
methods which honor the current state (such as Context.run) as well as
access to that state itself.
Check these both links :
Context
what exactly is this ‘context’ arg anyway?
To be honest, I wasted a lot of time figuring out what context is and why my code wouldn't run without it. But at some point I just gave up and started using to make my code run without errors.

How can I get Items to show in my QListWidget before the end of the function?

I want my QListWidget to update with the new item as it is added, but it only updates with all of the items once the function has ended. I have tried using update() and repaint(), but neither work. I actually had to use repaint() on the Widget itself just to get it to show up before the end, but none of the items do. Here is a brief view of the first item to add:
def runPW10(self):
self.PWList.setVisible(True)
self.PWList.setEnabled(True)
# This repaint() has to be here for even the List to show up
self.PWList.repaint()
....
self.PWList.addItem('Executing Change Password Tool')
# This does not help
self.PWList.repaint()
....
There is more to the function, but it is long and this should include what it needed. Please let me know if more is required.
What am I doing wrong that makes this List not update as the item is added?
Add QApplication.processEvents().
QCoreApplication.processEvents (QEventLoop.ProcessEventsFlags flags = QEventLoop.AllEvents)
Processes all pending events for the calling thread according to the specified flags until there are no more events to process.
You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
Your widget originally will be shown but unresponsive. To make the application responsive, add processEvents() calls to some whenever you add an item.
Do keep in mind that this can affect performance a lot. This lets the whole application loop execute including any queued events. Don't add this to performance sensitive loops.
Also consider that this allows your user to interact with the application, so make sure that any interactions that can happen either are not allowed, such as somebutton.enabled(False), or are handled gracefully, like a Cancel button to stop a long task.
See the original C++ docs for further information, since pyqt is a direct port.
To complete Drise's answer on this point:
Also consider that this allows your user to interact with the application, so make sure that any interactions that can happen either are not allowed, such as somebutton.enabled(False), or are handled gracefully, like a Cancel button to stop a long task.
You may want to use the QEventLoop.ExcludeUserInputEvents flag this way: QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) to refresh the GUI while preventing the user to activate any widgets.
QEventLoop.ExcludeUserInputEvents
0x01
Do not process user input events, such as ButtonPress and KeyPress. Note that the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeUserInputEvents flag.

Creating a new input event dispatcher in Pyglet (infra red input)

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.

Using external GUI libraries to make user interfaces in Autodesk Maya

I develop tools in Autodesk Maya. Many of the tools I build have simple windowed GUIs for the animators and modellers to use. These GUIs often contain what you'd normally expect to see in any basic window; labels, lists, menus, buttons, textfields, etc. However, there are limitations to the complexity of the UIs you can build with the available tools, specifically in the types of available widgets.
I'm interested in using some of the more advanced wxPython widgets such as the ListView (grid), Tree, etc. This would involve using a complete wxFrame (window) to display the whole UI, which would essentially mean that window would no longer be tied to Maya. Not a deal breaker, but it means when Maya is minimized, the window won't follow suit.
I've tried something like this before with tkinter as a test, but found that it needed a MainLoop to run in its own thread. This is logical, but in my case, it conflicts with Maya's own thread, essentially making Maya hang until the window is closed. This is due to the fact that Maya runs all scripts, be they MEL or Python, in a single thread that the main Maya GUI shares. This is to prevent one script from, say, deleting an object while another script is trying to do work on the same object.
wxPython has this same "mainloop" methodolgy. I'm wondering if there's any way around it so that it can work within Maya?
I'm not sure if this is germane, but some googling turns up that PyQt is pretty popular inside of Maya. You could try the technique here or here (explained here with source code) of creating a new threadloop via Maya and executing inside of that. It seems Maya has a module included that sets up a new thread object, with a QApplication inside it:
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = QtGui.QApplication(sys.argv)
pumpedThread = threading.Thread(target = pumpQt, args = ())
pumpedThread.start()
and then sets up a function to process the Qt events:
def pumpQt():
global app
def processor():
app.processEvents()
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
You can probably do something similar with wxPython as well. (utils.executeDeferred is a Maya function.) Be sure to check out how to create a non-blocking GUI on the wxPython wiki. Instead of processEvents(), you'll want to set up an event loop and check for "Pending" events inside the (hopefully renamed?) pumpQt function above. (The wxPython source has a Python implementation of MainLoop.) Likely this should be done through the app.Yield() function, but I'm not sure.
def pumpWx():
global app
def processor():
app.Yield(True)
while 1:
time.sleep(0.01)
utils.executeDeferred( processor )
def initializePumpThread():
global pumpedThread
global app
if pumpedThread == None:
app = wx.App(False)
pumpedThread = threading.Thread(target = pumpWx, args = ())
pumpedThread.start()
The wxPython docs indicate SafeYield() is preferred. Again, this seems like it could be a first step, but I'm not sure it will work and not just crash horribly. (There's some discussion about what you want to do on the wxPython mailing list but it's from a few minor versions of wx ago.) There is also some indication in various forums that this technique causes problems with keyboard input. You might also try doing:
def processor():
while app.Pending(): app.Dispatch()
to deal with the current list of events.
Good luck!
I don't know if there is a way around a mainloop for the gui, since it is needed to handle all event chains and redraw queues.
But there are several means of inter-process communication, like pipes or semaphores. Maybe it is an option to split your Maya extension into the actual plugin, being tight into maya, and a separate application for the gui. These two could use such means to communicate and exchange model information between plugin and gui.
I'm not sure, however, if I can really recommend this approach because it very much complicates the application.
You could have a look at IPython, an interactive Python shell, whose dev team has put some effort into integrating it with wxPython. They have some way of interrupting the event loop and hooking into it to do their own stuff.
The best way to go is creating a QWidget with what you need, and using it from within a MPxCommand thru the C++ API. That way you also have the chance to inject complete custom editors into Maya via scriptedPanels.
But if you're bound to Python, pyQt is the way to go.

wx's idle and UI update events in PyQt

wx (and wxPython) has two events I miss in PyQt:
EVT_IDLE that's being sent to a frame. It can be used to update the various widgets according to the application's state
EVT_UPDATE_UI that's being sent to a widget when it has to be repainted and updated, so I can compute its state in the handler
Now, PyQt doesn't seem to have these, and the PyQt book suggests writing an updateUi method and calling it manually. I even ended up calling it from a timer once per 0.1 seconds, in order to avoid many manual calls from methods that may update the GUI. Am I missing something? Is there a better way to achieve this?
An example: I have a simple app with a Start button that initiates some processing. The start button should be enabled only when a file has been opened using the menu. In addition, there's a permanent widget on the status bar that displays information.
My application has states:
Before the file is opened (in this state the status bar show something special and the start button is disabled)
File was opened and processing wasn't started: the start button is enabled, status bar shows something else
The processing is running: the start button now says "Stop", and the status bar reports progress
In Wx, I'd have the update UI event of the button handle its state: the text on it, and whether it's enabled, depending on the application state. The same for the status bar (or I'd use EVT_IDLE for that).
In Qt, I have to update the button in several methods that may affect the state, or just create a update_ui method and call it periodically in a timer. What is the more "QT"-ish way?
The use of EVT_UPDATE_UI in wxWidgets seems to highlight one of the fundamental differences in the way wxWidgets and Qt expect developers to handle events in their code.
With Qt, you connect signals and slots between widgets in the user interface, either handling "business logic" in each slot or delegating it to a dedicated method. You typically don't worry about making separate changes to each widget in your GUI because any repaint requests will be placed in the event queue and delivered when control returns to the event loop. Some paint events may even be merged together for the sake of efficiency.
So, in a normal Qt application where signals and slots are used to handle state changes, there's basically no need to have an idle mechanism that monitors the state of the application and update widgets because those updates should occur automatically.
You would have to say a bit more about what you are doing to explain why you need an equivalent to this event in Qt.
I would send Qt signals to indicate state changes (e.g. fileOpened, processingStarted, processingDone). Slots in objects managing the start button and status bar widget (or subclasses) can be connected to those signals, rather than "polling" for current state in an idle event.
If you want the signal to be deferred later on in the event loop rather than immediately (e.g. because it's going to take a bit of time to do something), you can use a "queued" signal-slot connection rather than the normal kind.
http://doc.trolltech.com/4.5/signalsandslots.html#signals
The connection type is an optional parameter to the connect() function:
http://doc.trolltech.com/4.5/qobject.html#connect , http://doc.trolltech.com/4.5/qt.html#ConnectionType-enum
As far as I understand EVT_IDLE is sent when application message queue is empty. There is no such event in Qt, but if you need to execute something in Qt when there are no pending events, you should use QTimer with 0 timeout.
In general, the more Qt-ish way is to update the button/toolbar as necessary in whatever functions require the update, or to consolidate some of the functionality and directly call that function when the program needs it (such as an updateUi function).
You should be aware that in Qt, changing an attribute of a Ui element doesn't cause an immediate redraw, but queues a redraw in the event system, and multiple redraw calls are compressed into one where possible.
As for the multiple changes relating to state, have a look at this blog post about a hopefully-upcoming addition to Qt to more easily handle states. It looks like this would take care of a lot of your complaints, because in your multiple functions, you could just transition the state variable, and the other parts of the UI should update to match. It's not positive this will make it into the next Qt release (although I would bet on it, or something similar), and I have no idea how closely PyQt tracks the Qt releases. Or alternately, you could use the concept and create your own class to track the state as needed.

Categories