can i connect two objects that are in different classes ?
lets say i want button1's clicked() signal to clear line2
class A(QGroupBox):
def __init__(self, parent=None):
super(A, self).__init__(parent)
self.button1= QPushButton('bt1')
self.button1.show()
class B(QGroupBox):
def __init__(self, parent=None):
super(B, self).__init__(parent)
self.line2 = QLineEdit()
self.line2.show()
ob1 = A()
ob2 = B()
Yes, create a method in object B that's tied to a signal in object A. Note how connect is called (this is just an example):
self.connect(self.okButton, QtCore.SIGNAL("clicked()"),
self, QtCore.SLOT("accept()"))
The third argument is the object with the slot, and the fourth the slot name. The sending and receiving objects can definitely be different.
Related
Click the button, QTabWidget will add a tab (patient object).
Here's how I designed it.
First, the model requests the network to call the patient method after the data is obtained to display the data.
Second, the patient will actively use the data in the model.
So I defined self.model = Model () and self.view = view, but this created a problem. Patient and model refer to each other, which will cause a memory leak. So when I closed the tab, I had to delete patient.model. Attributes so that they are no longer referenced by each other, and the problem of memory leaks has been resolved.
However, in my project, I have many cases where the view and model reference each other. I need to find them all, and then close the relationship between them when closing the tab. I think this design is a bit weak on memory leaks. Can you give me some better design suggestions? Thank you very much.
import sys
from PyQt4 import QtGui
from PyQt4.QtGui import QTabWidget, QHBoxLayout, QWidget, QPushButton
class Model(object):
def __init__(self, view):
self.view = view
class Patient(QWidget):
def __init__(self):
super(Patient, self).__init__()
self.model = Model(self)
self.data = [map(lambda x: {'name': 'ken'}, [x for x in range(10000000)])]
class Tab(QTabWidget):
def __init__(self):
super(Tab, self).__init__()
self.setTabsClosable(True)
self.tabCloseRequested.connect(self.delete)
def add(self):
self.addTab(Patient(), 'name')
def delete(self, index):
patient = self.widget(index)
self.removeTab(index)
import sip
sip.delete(patient)
del patient.model
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.hbox = QHBoxLayout()
self.setLayout(self.hbox)
self.tab = Tab()
self.hbox.addWidget(self.tab)
btn = QPushButton()
btn.clicked.connect(self.click)
self.hbox.addWidget(btn)
self.setGeometry(100, 100, 500, 500)
self.setWindowTitle("PyQt")
self.show()
def click(self):
self.tab.add()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If you always use Model and Patient classes, you could use the destroyed() signal to be notified when a widget is removed, and delete the model.
Since deleteLater() also takes care of disconnecting all slots related to the destroying object, the method cannot be called on its own instance. To circumvent this, using a lambda can solve the issue.
class Patient(QWidget):
def __init__(self):
super(Patient, self).__init__()
self.model = Model(self)
self.data = [map(lambda x: {'name': 'ken'}, [x for x in range(10000000)])]
self.destroyed.connect(lambda: self.aboutToBeDeleted())
def aboutToBeDeleted(self):
del self.model
class Tab(QTabWidget):
#...
def delete(self, index):
patient = self.widget(index)
self.removeTab(index)
patient.deleteLater()
This should take care of everything, including disconnecting all slots and signals related to patient. Calling deleteLater() should also be enough, instead of using sip.delete (which I believe is done anyway by deleteLater).
I'm trying to create a set of PySide classes that inherit QWidget, QMainWindow, and QDialog. Also, I would like to inherit another class to overrides a few functions, and also set the layout of the widget.
Example:
Mixin:
class Mixin(object):
def __init__(self, parent, arg):
self.arg = arg
self.parent = parent
# Setup the UI from QDesigner
ui = Ui_widget()
ui.setupUi(self.parent)
def setLayout(self, layout, title):
self.parent.setWindowTitle(title)
self.parent.setLayout(layout)
def doSomething(self):
# Do something awesome.
pass
Widget:
class Widget(Mixin, QtGui.QWidget):
def __init__(self, parent, arg):
super(Widget, self).__init__(parent=parent, arg=arg)
This won't work, but doing this through composition works
Widget (Composition):
class Widget(QtGui.QWidget):
def __init__(self, parent, arg):
super(Widget, self).__init__(parent=parent)
mixin = Mixin(parent=self, arg=arg)
self.setLayout = mixin.setLayout
self.doSomething = mixin.doSomething
I would like to try to have the widget inherit everything instead of having part of it done through composition. Thanks!
Keep class Widget(Mixin, QtGui.Widget):, but add a super call in Mixin.__init__. This should ensure the __init__ method of both Mixin and QWidget are called, and that the Mixin implementation of the setLayout method is found first in the MRO for Widget.
class Mixin(object):
def __init__(self, parent=None, arg=None):
super(Mixin, self).__init__(parent=parent) # This will call QWidget.__init__
self.arg = arg
self.parent = parent
# Setup the UI from QDesigner
ui = Ui_widget()
ui.setupUi(self.parent)
def setLayout(self, layout, title):
self.parent.setWindowTitle(title)
self.parent.setLayout(layout)
def doSomething(self):
# Do something awesome.
pass
class Widget(Mixin, QtGui.QWidget):
def __init__(self, parent, arg):
super(Widget, self).__init__(parent=parent, arg=arg) # Calls Mixin.__init__
The issue that I'm facing is when I want to split the functionality of the menubar into multiple files (classes), each of them specific for handling options (File/Help/Edit and so on).
In the Main UI class I have:
class MyFrame(QMainWindow):
def __init__(self):
super().__init__()
self.menu_bar = self.menuBar()
# Create menu
self.add_menu()
def add_menu(self):
help_menu = MenuHelp(self)
def getMenuBar(self):
return self.menu_bar
In the MenuHelp (class):
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__()
self.menu_variable = parrent_widget.getMenuBar().addMenu('Help')
about_action = self.menu_variable.addAction('About')
about_action.setStatusTip('About')
about_action.triggered.connect(self.handle_trigger)
def handle_trigger(self):
print('Im here')
The menubar is correctly shown, but handle_trigger method is never called, any ideas on what am I doing wrong?
You must pass a parent to your QMenu. You must change:
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__()
to:
class MenuHelp(QMenu):
def __init__(self, parrent_widget):
super(MenuHelp, self).__init__(parrent_widget)
I'm trying to subclass a generic data class to perform some additional operations on the data computed by the parent. I would like to reuse the dataReady signal in the subclass. The problem is that the signal is still emitted from the parent, and triggers the connected slot(s) before the additional computations have completed.
Is it possible to override/suppress signals emitted from the parent class, or will I simply have to choose a different signal name?
Here's a simplified example of my classes:
class Generic(QtCore.QObject):
dataReady = pyqtSignal()
def __init__(self, parent=None):
super(Generic, self).__init__(parent)
def initData(self):
# Perform computations
...
self.dataReady.emit()
class MoreSpecific(Generic):
dataReady = pyqtSignal()
def __init__(self, parent=None):
super(MoreSpecific, self).__init__(parent)
def initData(self):
super(MoreSpecific, self).initData()
# Further computations
...
self.dataReady.emit()
You can use blockSignals:
def initData(self):
self.blockSignals(True)
super(MoreSpecific, self).initData()
self.blockSignals(False)
I would just restructure the classes a bit.
class Generic(QtCore.QObject):
dataReady = pyqtSignal()
def __init__(self, parent=None):
super(Generic, self).__init__(parent)
def initData(self):
self.computations()
self.dataReady.emit()
def computations(self):
# put your computations in a method
...
class MoreSpecific(Generic):
def __init__(self, parent=None):
super(MoreSpecific, self).__init__(parent)
def computations(self):
super(MoreSpecific, self).computations()
# further computations
Now your initData method, which is supposed to do some calculations and then send a signal, doesn't have to change and your MoreSpecific class will only send the signal once.
Using the below code, the __del__ method of my Preview widget never gets called. If I uncomment the "del window" line, it does. Why?
#!/usr/bin/env python
from PyQt4 import QtGui
class Preview(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
def __del__(self):
print("Deleting Preview")
class PreviewWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.widget = Preview(self)
self.setCentralWidget(self.widget)
def __del__(self):
print("Deleting PreviewWindow")
if __name__ == "__main__":
app = QtGui.QApplication(["Dimension Preview"])
window = PreviewWindow()
window.show()
app.exec()
# del window
If a QObject subclass has a parent, then Qt will delete it when the parent is deleted. On the other hand, if a QObject subclass has no parent, it will (eventually) be deleted by python.
Hopefully this example will make things somewhat clearer:
from PyQt4 import QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
self.destroyed.connect(self.handleDestroyed)
def __del__(self):
print ('__del__:', self)
def handleDestroyed(self, source):
print ('destroyed:', source)
class Foo(Widget):
def __init__(self, parent):
Widget.__init__(self, parent)
class Bar(Widget):
def __init__(self, parent):
Widget.__init__(self, parent)
class Window(Widget):
def __init__(self, parent=None):
Widget.__init__(self, parent)
self.foo = Foo(self)
self.bar = Bar(None)
if __name__ == "__main__":
app = QtGui.QApplication([__file__, '-widgetcount'])
window = Window()
window.show()
app.exec_()
Which outputs:
__del__: <__main__.Window object at 0x88f514c>
destroyed: <__main__.Foo object at 0x88f5194>
__del__: <__main__.Bar object at 0x88f51dc>
Widgets left: 0 Max widgets: 4
EDIT
On second thoughts, it appears that there may be a bug (or at least a difference in behaviour) with some versions of PyQt4.
As a possible workaround, it seems that creating two python names for the main widget and then explicitly deleting each of them may help to ensure that both the C++ and python sides of the object get destroyed.
If the following line is added to the above script:
tmp = window; del tmp, window
Then the output becomes:
__del__: <__main__.Window object at 0x8d3a14c>
__del__: <__main__.Foo object at 0x8d3a194>
__del__: <__main__.Bar object at 0x8d3a1dc>
Widgets left: 0 Max widgets: 4