Reputation: 841
This is something I've seen a fair bit of documentation on but I can't get an exact answer to this specific gap in my knowledge and I keep running into walls because of it, nothing seems to really cover it on this level:
I have a few windows, all of them should be draggable, so I'm building a utilities file. In this file I have this:
def mouseMoveEvent(self, event):
if self.moving: self.move(event.globalPos()-self.offset)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.moving = True; self.offset = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.moving = False
And this will work if I put it into a single file (and don't call from another one) with the actual window.
The question is, how do I call to this from another file (the one that has the UI window)?
I am calling to it by
from utils import *
So technically those functions are all accessible simply by typing
mouseReleaseEvent(x,x)
But I need the UI to import those functions, so in the main file after putting up the 'from utils import *' I type:
self.moving = False
But that errors no matter what I do. So the question is what is the proper way to enclose the functions listed first (first code block) so that I can call to them from within my UI file?
Here is my UI code:
#!/usr/bin/env python
import sys
import os
from PyQt4 import QtCore, QtGui
from vibotCss import css
from viUtils import *
class viBadOSUI(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
#setup window
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.resize(300, 150)
center(self)
self.setMouseTracking(True)
#additional code
self.show()
self.setStyleSheet(css)
app = QtGui.QApplication(sys.argv)
window = viBadOSUI()
sys.exit(app.exec_())
I have large gaps in my Python knowledge due to learning and using it solely from within Autodesk Maya for a long time - I am trying to bridge said gaps now. Thank you for any help.
Upvotes: 2
Views: 5604
Reputation: 880509
I think what you are looking for are mixins:
For example, in viUtils.py you could define:
from PyQt4 import QtCore, QtGui
class Draggable(object):
def mouseMoveEvent(self, event):
try:
if self.moving:
self.move(event.globalPos()-self.offset)
except AttributeError:
pass
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.moving = True; self.offset = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.moving = False
class Centerable(object):
pass # add code here
class CountdownTimer(object):
pass # add code here
By bundling related methods together in mixins, you can add functionality to your classes (windows) by simply adding the mixin(s) to the base classes:
import sys
import os
from PyQt4 import QtCore, QtGui
from vibotCss import css
import viUtils
class viBadOSUI(QtGui.QDialog,
viUtils.Draggable, viUtils.CountdownTimer):
def __init__(self):
QtGui.QDialog.__init__(self)
#setup window
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.resize(300, 150)
center(self) # You should probably make `center` a method instead of a function and call `self.center()` instead.
self.setMouseTracking(True)
#additional code
self.show()
self.setStyleSheet(css)
app = QtGui.QApplication(sys.argv)
window = viBadOSUI()
sys.exit(app.exec_())
One thing I do not like about my code above is that I could not figure out how to define a mixin (e.g. Draggable
) with an __init__
which gets called automatically by viBadOSUI.__init__
. Normally you would use super
for this, but it appears QtGui.QDialog.__init__
does not itself call super
.
(Actually, I think it might be possible to use super
if you put the mixins before QtGui.QDialog
, but this relies on trickery which will break once someone forgets that QtGui.QDialog
must appear last in the list of bases.)
Because I could not define an __init__
method which would work robustly:
class Draggable(object):
def __init__(self):
...
self.moving = False
I instead modified mouseMoveEvent
to use a try..except
.
This is better than adding self.moving = False
directly to viBadOSUI.__init__
because this attribute is used only by Draggable, and therefore should be somehow bundled with Draggable
. Setting the mixin and then also having to remember to initialize an attribute just so the mixin will work is ugly programming.
Using try..except
here is perhaps slightly better than using if hasattr(self, 'moving')
since the try is quicker as long as an exception is not raised, and the exception will be raised only at the beginning before the first mousePressEvent.
Still, there is room for improvement and I hope someone here will show us a better way.
An alternative approach would have been to define
class viBadOSUI(QtGui.QDialog):
mouseMoveEvent = viUtils.mouseMoveEvent
mousePressEvent = viUtils.mousePressEvent
mouseReleaseEvent = viUtils.mouseReleaseEvent
This gives you a lot of flexibility to add exactly those methods you want from viUtils directly into any class you define. However, I think it is a bit too much flexibility since mouseMoveEvent, mousePressEvent, and mouseReleaseEvent seem "bundled" to me. They all work together to implement a single piece of functionality. This is why I like the mixin approach better. Also, adding viUtils.Draggable
to the base classes is less typing than adding those 3 lines to every window subclass that you want to be draggable.
By the way, I'd recommend eschewing from viUtils import *
and using import viUtils
instead. It will keep your code more organized in the long run.
Upvotes: 4
Reputation: 799180
The self
(first) argument on a method refers to the instance on which it is bound. The equivalent of self.moving = False
outside a method is to bind False
to the moving
attribute of an instance, e.g. obj.moving = False
.
Upvotes: 2