PyQt5: mouseClick and source-code in QWebEngineView - python

I have a working script that uses PyQt-5.5.1, which I now want to port to a new PyQt version (5.7). Adapting most of the things was fine, but I faced two major problems: (1) to perform a (simulated) mouseclick, (2) to access (let's say: print) the html source-code of a webpage which is currently displayed in the QWebView or QWebEngineView, respectively.
For example, I could do the following using QWebView in PyQt-5.5.1:
QTest.mouseClick(self.wvTest, Qt.LeftButton, QPoint(x, y))
and
frame = self.wvTest.page().mainFrame()
print(frame.toHtml().encode('utf-8'))
I am aware of the docs as well as this page about porting to QWebEngineView but unable to convert C++ notation to a working Python code.
How can I adapt this to QWebEngineView in PyQt-5.7? Below is a fully working snippet for PyQt-5.5.1, which fails for the new PyQt-version:
for Button1: no mouse click reaction at all.
for Button2: AttributeError: 'QWebEnginePage' object has no attribute 'mainFrame', and when I delete the mainframe(): TypeError: toHtml(self, Callable[..., None]): not enough arguments.
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication
from PyQt5.QtCore import QRect, Qt, QUrl, QPoint, QEvent
from PyQt5.QtTest import QTest
from PyQt5.Qt import PYQT_VERSION_STR
if PYQT_VERSION_STR=='5.5.1': from PyQt5 import QtWebKitWidgets
else: from PyQt5 import QtWebEngineWidgets
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.button1 = QPushButton('Button1', self)
self.button1.clicked.connect(self.buttonOne)
self.button1.setGeometry(QRect(10, 10, 90, 20))
self.button2 = QPushButton('Button2', self)
self.button2.clicked.connect(self.buttonTwo)
self.button2.setGeometry(QRect(110, 10, 90, 20))
if PYQT_VERSION_STR=='5.5.1': self.wvTest = QtWebKitWidgets.QWebView(self)
else: self.wvTest = QtWebEngineWidgets.QWebEngineView(self)
self.wvTest.setGeometry(QRect(10, 40, 430, 550))
self.wvTest.setUrl(QUrl('http://www.startpage.com'))
self.wvTest.setObjectName('wvTest')
self.setGeometry(300, 300, 450, 600)
self.setWindowTitle('WebView minimalistic')
self.show()
def buttonOne(self):
qp = QPoint(38, 314)
QTest.mouseClick(self.wvTest, Qt.LeftButton, pos=qp) # or: QTest.mouseMove(self.wvTest, pos=self.qp)
print('Button1 pressed.')
def buttonTwo(self):
frame = self.wvTest.page().mainFrame()
print(frame.toHtml().encode('utf-8'))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

The QWebEngineView class is not a drop-in replacement for QWebView. As the porting guide makes clear, many of the APIs have fundamentally changed, and some major features are completely missing. This will probably make it impossible to write a compatibility layer unless your browser implementation is very, very simple. It will most likely be easier to write a separate test-suite (unless you don't mind writing a huge amount of conditional code).
To start with, you will need to implement a work-around for QTBUG-43602. The current design of QWebEngineView means that an internal QOpenGLWidget handles mouse-events, so your code will need to get a new reference to that whenever the page is loaded:
class Example(QWidget):
...
def initUI(self):
...
self._glwidget = None
if PYQT_VERSION_STR=='5.5.1':
self.wvTest = QtWebKitWidgets.QWebView(self)
else:
self.wvTest = QtWebEngineWidgets.QWebEngineView(self)
self.wvTest.installEventFilter(self)
...
def eventFilter(self, source, event):
if (event.type() == QEvent.ChildAdded and
source is self.wvTest and
event.child().isWidgetType()):
self._glwidget = event.child()
self._glwidget.installEventFilter(self)
elif (event.type() == QEvent.MouseButtonPress and
source is self._glwidget):
print('web-view mouse-press:', event.pos())
return super().eventFilter(source, event)
def buttonOne(self):
qp = QPoint(38, 314)
widget = self._glwidget or self.wvTest
QTest.mouseClick(widget, Qt.LeftButton, pos=qp)
For accessing the html of the page, you will need some conditional code, because the web-engine API works asynchronously, and requires a callback. Also there are no built-in APIs for handling frames in web-engine (you need to use javascript for that), so everything needs to go through the web-page:
def buttonTwo(self):
if PYQT_VERSION_STR=='5.5.1':
print(self.wvTest.page().toHtml())
else:
self.wvTest.page().toHtml(self.processHtml)
def processHtml(self, html):
print(html)

Related

Usage of pyqtsignal and emit in python [duplicate]

I am reading through some documentation on PyQt5 to come up with a simple signal-slot mechanism. I have come to a halt due to a design consideration.
Consider the following code:
import sys
from PyQt5.QtCore import (Qt, pyqtSignal)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def printLabel(self, str):
print(str)
def logLabel(self, str):
'''log to a file'''
pass
def initUI(self):
lcd = QLCDNumber(self)
sld = QSlider(Qt.Horizontal, self)
vbox = QVBoxLayout()
vbox.addWidget(lcd)
vbox.addWidget(sld)
self.setLayout(vbox)
#redundant connections
sld.valueChanged.connect(lcd.display)
sld.valueChanged.connect(self.printLabel)
sld.valueChanged.connect(self.logLabel)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
To track the changes made to the slider, I simply print and log the changes made. What I do not like about the code is that I am required to call the sld.valueChanged slot thrice to send the same information to 3 different slots.
Is it possible to create my own pyqtSignal that sends an integer to a single slot function. And in turn have the slot function emit the changes that need to be made?
Maybe I don't fully understand the purpose of emit() because there are no good examples of it's purpose in the PyQt Signal-Slot docs. All we're given is an example of how to implement an emit with no parameters.
What I would like to do is create a function that handles the emit function. Consider the following:
import sys
from PyQt5.QtCore import (Qt, pyqtSignal)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
#create signal
self.val_Changed = pyqtSignal(int, name='valChanged')
self.initUI()
def initUI(self):
lcd = QLCDNumber(self)
sld = QSlider(Qt.Horizontal, self)
vbox = QVBoxLayout()
vbox.addWidget(lcd)
vbox.addWidget(sld)
self.setLayout(vbox)
sld.val_Changed.connect(self.handle_LCD)
self.val_Changed.emit()
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
self.show()
def handle_LCD(self, text):
'''log'''
print(text)
'''connect val_Changed to lcd.display'''
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
There are obviously some serious design flaws here. I cannot wrap my head around the order of function calls. And I am not implementing pyqtSignal correctly. I do however believe that correctly stating the following 3 points will help me produce a proper app:
For a predefined signal: send the signal to the slot function. Slot can be reimplemented to use the signal values.
Produce pyqtSignal object with some parameters. It is not yet clear what the purpose of these parameters are and how they differ from 'emit' parameters.
emit can be reimplemented to send specific signal values to the slot function. It is also not yet clear why I would need to send different values from previously existing signal methods.
Feel free to completely alter the code for what I am trying to do because I have not yet figured out if its in the realm of good style.
You can define your own slot (any python callable) and connect that to the signal, then call the other slots from that one slot.
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def printLabel(self, str):
print(str)
def logLabel(self, str):
'''log to a file'''
pass
#QtCore.pyqtSlot(int)
def on_sld_valueChanged(self, value):
self.lcd.display(value)
self.printLabel(value)
self.logLabel(value)
def initUI(self):
self.lcd = QLCDNumber(self)
self.sld = QSlider(Qt.Horizontal, self)
vbox = QVBoxLayout()
vbox.addWidget(self.lcd)
vbox.addWidget(self.sld)
self.setLayout(vbox)
self.sld.valueChanged.connect(self.on_sld_valueChanged)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
Also, if you want to define your own signals, they have to be defined as class variables
class Example(QWidget):
my_signal = pyqtSignal(int)
The arguments to pyqtSignal define the types of objects that will be emit'd on that signal, so in this case, you could do
self.my_signal.emit(1)
emit can be reimplemented to send specific signal values to the slot
function. It is also not yet clear why I would need to send different
values from previously existing signal methods.
You generally shouldn't be emitting the built in signals. You should only need to emit signals that you define. When defining a signal, you can define different signatures with different types, and slots can choose which signature they want to connect to. For instance, you could do this
my_signal = pyqtSignal([int], [str])
This will define a signal with two different signatures, and a slot could connect to either one
#pyqtSlot(int)
def on_my_signal_int(self, value):
assert isinstance(value, int)
#pyqtSlot(str)
def on_my_signal_str(self, value):
assert isinstance(value, str)
In practice, I rarely overload signal signatures. I would normally just create two separate signals with different signatures rather than overloading the same signal. But it exists and is supported in PyQt because Qt has signals that are overloaded this way (eg. QComboBox.currentIndexChanged)
the accepted answer, was tricky to me to undestand because of the use of the QSlider built in signal valueChanged() I didn't know about https://doc.qt.io/qtforpython/PySide6/QtWidgets/QSlider.html, so I added my own signal that is emitted when self.sld.valueChanged.connect(self.on_sld_valueChanged) is called, to have an example of how to create my own pyqtSignal. the Post title: "PyQt proper use of emit() and pyqtSignal()" was misleading to me , I am trying to understand signals and slots too, so I wanted to add emit() to the code. I know its not the way to go forward but just to figure out how it works:
import sys
from PyQt5 import QtCore
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication)
class Example(QWidget):
#create signal
val_Changed = pyqtSignal(int, str, name='valChanged')
def __init__(self):
super().__init__()
self.initUI()
self.valChanged.connect(self.mine)
self.show()
def printLabel(self, str):
print(str)
def logLabel(self, str):
'''log to a file'''
pass
#QtCore.pyqtSlot(int, str)
def mine(self, value, string):
self.lcd.display(value)
self.printLabel((str(value)+' '+ string))
self.logLabel(value)
def on_sld_valueChanged(self, value):
self.val_Changed.emit(value, 'using-slider')
def initUI(self):
self.lcd = QLCDNumber(self)
self.sld = QSlider(Qt.Horizontal, self)
vbox = QVBoxLayout()
vbox.addWidget(self.lcd)
vbox.addWidget(self.sld)
self.setLayout(vbox)
self.sld.valueChanged.connect(self.on_sld_valueChanged)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
if __name__ == '__main__' :
app = QApplication( sys.argv )
ex = Example()
sys.exit(app.exec_( ))
I'll wait to somebody that could show me how to reimplement QSlider valueChanged() signal

PyQt5 - Making QCombobox Drop List On Hover

So Basically I Am newbie In PyQt5, I found that the PyQt5 support stylesheets!
So I just thought Of The implementing that drop a list when i user hover on QCombobox.
Any Answer Or Suggestion Is Accepted!
Can it be?
import sys
from PyQt5.QtWidgets import (QWidget, QHBoxLayout,
QComboBox, QApplication)
from PyQt5.QtGui import QIcon
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
hbox = QHBoxLayout()
combo = QComboBox(self)
combo.addItem('Disk')
combo.setItemIcon(0, QIcon('disk.png'))
combo.addItem('Web')
combo.setItemIcon(1, QIcon('web.png'))
combo.addItem('Computer')
combo.setItemIcon(2, QIcon('computer.png'))
hbox.addWidget(combo)
hbox.setSpacing(20)
self.setContentsMargins(20, 20, 20, 20)
self.setLayout(hbox)
self.setGeometry(300, 300, 250, 180)
self.setWindowTitle('QComboBox')
self.show()
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Stylesheets in Qt do not allow programmatical access to widget functions (with some exceptions for widget properties, which must be used with care, though).
So, if you thought you could use stylesheets to show the popup on hover, you can't.
In order to do that, you must subclass the QComboBox and reimplement its enterEvent(), otherwise install an event filter. In both cases, you need to call showPopup():
class Example(QWidget):
def initUI(self):
# ...
combo.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QEvent.Enter:
source.showPopup()
return super().eventFilter(source, event)
Note that if you want to filter other widgets and different event types, it's better to create a reference for the object(s) and check that the source of the event is actually what you need:
class Example(QWidget):
def initUI(self):
# ...
self.combo = QComboBox()
self.combo.installEventFilter(self)
# ...
def eventFilter(self, source, event):
if source == self.combo and event.type() == QEvent.Enter:
source.showPopup()
return super().eventFilter(source, event)
About the customization with stylesheets, see the examples on customizing QComboBox, but I also warmly suggest you to study both the stylesheet syntax and reference documentation.
Also note that you should always ask one question per post, if you have more different questions, create a post for each of them.

How to remember last geometry of PyQt application?

I am using PyQt5 5.5.1 (64-bit) with Python 3.4.0 (64-bit) on Windows 8.1
64-bit.
I am having trouble restoring the position and size (geometry) of my
very simple PyQt app.
Here is minimal working application:
import sys
from PyQt5.QtWidgets import QApplication, QWidget
class myApp(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
view = myApp()
sys.exit(app.exec())
What I read online is that this is the default behavior and we need to
use QSettings to save and retrieve settings from Windows registry,
which is stored in
\\HKEY_CURRENT_USER\Software\{CompanyName}\{AppName}\
Here are some of the links I read.
I could have followed those tutorials but those tutorials/docs were
written for C++ users.
C++ is not my glass of beer, and converting those codes are impossible to me.
Related:
QSettings(): How to save to current working directory
This should do.
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import QSettings, QPoint, QSize
class myApp(QWidget):
def __init__(self):
super(myApp, self).__init__()
self.settings = QSettings( 'My company', 'myApp')
# Initial window size/pos last saved. Use default values for first time
self.resize(self.settings.value("size", QSize(270, 225)))
self.move(self.settings.value("pos", QPoint(50, 50)))
def closeEvent(self, e):
# Write window size and position to config file
self.settings.setValue("size", self.size())
self.settings.setValue("pos", self.pos())
e.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
frame = myApp()
frame.show()
app.exec_()
I simplified this example: QSettings(): How to save to current working directory
Similar to #Valentin's response, because I feel settings are being written to registry, which will be issue for cross compatiblity. Here is the relevant startEvent() and closeEvent() for the job.
def startEvent()
self.settings = QSettings(QSettings.IniFormat,QSettings.SystemScope, '__MyBiz', '__settings')
self.settings.setFallbacksEnabled(False) # File only, not registry or or.
# setPath() to try to save to current working directory
self.settings.setPath(QSettings.IniFormat,QSettings.SystemScope, './__settings.ini')
# Initial window size/pos last saved
self.resize(self.settings.value("size", QSize(270, 225)))
self.move(self.settings.value("pos", QPoint(50, 50)))
self.tab = QWidget()
def closeEvent(self, e):
# Write window size and position to config file
self.settings.setValue("size", self.size())
self.settings.setValue("pos", self.pos())
startEvent() should be initiated at startup and closeEvent() should be taken care before quitting the main window.
You should indeed use QSetting for this.
All the Qt examples have been converted to Python. They are included in the source packages of PyQt (or PySide), which you can download here
You can also look online in the github repo, particularly in application.py of mainwindows example.
def readSettings(self):
settings = QSettings("Trolltech", "Application Example")
pos = settings.value("pos", QPoint(200, 200))
size = settings.value("size", QSize(400, 400))
self.resize(size)
self.move(pos)
def writeSettings(self):
settings = QSettings("Trolltech", "Application Example")
settings.setValue("pos", self.pos())
settings.setValue("size", self.size())
Fire writeSettings() before quitting and initiate readSettings() on startup.
In my case I use .ini files to store settings (language, default user, ...). the same code works on both Debian and Windows.
An example:
from PySide.QtCore import QSettings
self.settings = QSettings('settings.ini', QSettings.IniFormat)
...
self.settings.setValue('size', self.size())

PySide: QMetaObject.connectSlotsByName emits warnings "No matching signal..." but still works..?

In Qt Designer, I created a QDialog window and used pysideuic to compile that to a base class which contains a setupUi method initialising all GUI elements and which I extend to implement the functionality, as so:
class MyDialog(QtGui.QDialog, ui_file.Ui_main_dialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
ui_file.Ui_main_dialog.__init__(self)
self.setupUi(self)
This setupUi method has calls to QtCore.QObject.connect for the signal-slot connections I created in Qt Designer, where I also added new slots to the GUI. These slots are non-existent in the base class generated by pysideuic and I added them to the MyDialog class, e.g.
def on_list_selection_changed(self):
self.run_btn.setEnabled(len(self.modules_list.selectedIndexes()) > 0)
For this example, the slot is called on_list_selection_changed() (with empty parameter list) in Qt Designer.
On initialisation, MyDialog.__init__ calls Ui_main_dialog.setupUi, which eventually calls QtCore.QMetaObject.connectSlotsByName (the latter two with the MyDialog instance's self which is currently being created). This emits the following line on sys.stderr:
QMetaObject::connectSlotsByName: No matching signal for on_list_selection_changed()
Still, the signal behaves correctly and the slot is called when the connected modules_list.itemSelectionChanged() (modules_list is a QListWidget).
So here is my question: why do I receive this warning? What can I do so it doesn't appear, given that it seems to be irrelevant?
Edit: Since I didn't receive any answers in the last 5 months, I thought I give a complete example to make reproducing the problem easier.
This example differs from the question above in that it only uses a QLineEdit instance. Here is the code:
import sys
from PySide import QtCore, QtGui
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
self.lineEdit = QtGui.QLineEdit(Dialog)
self.lineEdit.setObjectName("lineEdit")
QtCore.QObject.connect(self.lineEdit, QtCore.SIGNAL("textChanged(QString)"), Dialog.on_lineEdit_changed)
QtCore.QMetaObject.connectSlotsByName(Dialog)
class MainWindow(QtGui.QMainWindow, Ui_Dialog):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
Ui_Dialog.__init__(self)
Ui_Dialog.setupUi(self, self)
#QtCore.Slot(unicode) # signal with no arguments
def on_lineEdit_changed(self, text):
print 'Changed to', text
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
Note that the code for the Ui_Dialog class is generated by the pysideuic from the Qt Designer's .ui file, but I shortened it a bit to better highlight the problem.
I have the same issue in C++. connectSlotsByName is hard-coded to output that message if it finds a slot it can't connect automatically, even though you don't need the automatic connection because you've done it explicitly yourself. You can use qInstallMsgHandler to suppress warnings in general or write the messages somewhere better but I don't think there's any way to tell connectSlotsByName that you don't care about this case in particular.
I'm late but in case someone else comes here for this same question.
PySide auto connects the signals, this is how it worked for me since I didn't want to be connecting manually.
Here I use python 3.9 and PySide2, but it is the same for PySide6 and also for other versions of python.
import sys
import typing
from PySide2.QtCore import QObject, SignalInstance, Signal, Slot
from PySide2.QtWidgets import QMainWindow, QApplication
from .ui_mainwindow import Ui_MainWindow # Don't forget to add your import
class MyQObject(QObject):
mySignal: SignalInstance = Signal()
otherSignal: SignalInstance = Signal(str)
def __init__(self, parent: typing.Optional[QObject] = None) -> None:
super().__init__(parent)
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
QMainWindow.__init__(self)
# Instantiate before self.setupUI
self.myQObject = MyQObject(self)
self.myQObject.setObjectName("syncProduct") # do not forget this
# Load UI from designer & init
self.setupUi(self)
#Slot()
def on_myQObject_mySignal(self):
print("Handle mySignal")
#Slot(str)
def on_myQObject_otherSignal(self, text: str):
print(f"Handle otherSignal {text}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

How to connect a signal from the controller in PyQt4? (iOS like MVC structure in PyQt4)

Why doesn't the following example work?
from PyQt4 import QtGui
import sys
class TestView(QtGui.QWidget):
def __init__(self):
super(TestView, self).__init__()
self.initUI()
def initUI(self):
self.btn = QtGui.QPushButton('Button', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
class TestViewController():
def __init__(self, view):
view.btn.clicked.connect(self.buttonClicked)
view.show()
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestView()
TestViewController(view)
app.exec_()
if __name__ == '__main__':
main()
The example is supposed to represent an MVC structure (like the one in Figure 4 -- without the Model) where the controller (TestViewController) receives a reference to the view (TestView) and connects the clicked signal from the view's button view.btn to its function self.buttonClicked.
I'm sure the line view.btn.clicked.connect(self.buttonClicked) is executed but, apparently, it has no effect. Does anyone knows how to solve that?
Update (awful solution):
In the example, if I replace the line
view.btn.clicked.connect(self.buttonClicked)
with
view.clicked = self.clicked
view.btn.clicked.connect(view.clicked)
it works. I'm still not happy with that.
The reason it is not working is because the controller class is being garbage collected before you can ever click anything for it.
When you set view.clicked = self.clicked, what you're actually doing is making one of the objects from the controller persist on the view object so it never gets cleaned up - which isn't really the solution.
If you store your controller to a variable, it will protect it from collection.
So if you change your code above to read:
ctrl = TestViewController(view)
You'll be all set.
That being said - what exactly you are trying to do here, I am not sure...it seems you're trying to setup an MVC system for Qt - but Qt already has a pretty good system for that using the Qt Designer to separate the interface components into UI (view/template) files from controller logic (QWidget subclasses). Again, I don't know what you are trying to do and this may be a dumb down version of it, but I'd recommend making it all one class like so:
from PyQt4 import QtGui
import sys
class TestView(QtGui.QWidget):
def __init__(self):
super(TestView, self).__init__()
self.initUI()
def initUI(self):
self.btn = QtGui.QPushButton('Button', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.btn.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestView()
view.show()
app.exec_()
if __name__ == '__main__':
main()
Edit: Clarifying the MVC of Qt
So this above example doesn't actually load the ui dynamically and create a controller/view separation. Its a bit hard to show on here. Best to work through some Qt/Designer based examples/tutorials - I have one here http://bitesofcode.blogspot.com/2011/10/introduction-to-designer.html but many can be found online.
The short answer is, your loadUi method can be replace with a PyQt4.uic dynamic load (and there are a number of different ways to set that up) such that your code ultimately reads something like this:
from PyQt4 import QtGui
import PyQt4.uic
import sys
class TestController(QtGui.QWidget):
def __init__(self):
super(TestController, self).__init__()
# load view
uifile = '/path/to/some/widget.ui'
PyQt4.uic.loadUi(uifile, self)
# create connections (assuming there is a widget called 'btn' that is loaded)
self.btn.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestController()
view.show()
app.exec_()
if __name__ == '__main__':
main()
Edit 2: Storing UI references
If it is easier to visualize this concept, you Can also store a reference to the generated UI object:
from PyQt4 import QtGui
import PyQt4.uic
import sys
class TestController(QtGui.QWidget):
def __init__(self):
super(TestController, self).__init__()
# load a view from an external template
uifile = '/path/to/some/widget.ui'
self.ui = PyQt4.uic.loadUi(uifile, self)
# create connections (assuming there is a widget called 'btn' that is loaded)
self.ui.btn.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestController()
view.show()
app.exec_()
if __name__ == '__main__':
main()

Categories