Pyqt4 to Pyqt5 How

PyQt4 to PyQt5 how?

The main change from Qt4 to Qt5 and hence from PyQt4 to PyQt5 is the rearrangement of certain classes so that the Qt project is scalable and generates a smaller executable.

The QtGui library was divided into 2 submodules: QtGui and QtWidgets, in the second only the widgets, namely QMainWindow, QPushButton, etc. And that is the change you must make:

[...]
from PyQt5 import QtGui, QtCore, uic, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import *
[...]

Ui_MainWindow, QtBaseClass = uic.loadUiType(uifile)
Ui_Dialog= uic.loadUiType(uifile)

class About(QtWidgets.QMainWindow, about.Ui_Dialog):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.setWindowModality(QtCore.Qt.ApplicationModal)
point = parent.rect().bottomRight()
global_point = parent.mapToGlobal(point)
self.move(global_point - QPoint(395, 265))

class MyApp(QtWidgets.QMainWindow, app_window_dark.Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
self.about_btn.clicked.connect(self.popup)

#prev next
self.btn_next.clicked.connect(self.renderSet)
self.btn_prev.clicked.connect(self.renderSet)

Note: Phonon does not exist in PyQt5, you must use QtMultimedia, an accurate solution you can find it in the following answer: Phonon class not present in PyQt5

how to upgrade from pyqt4 to pyqt5 in python

Translating a PyQt4 code to PyQt5 is not a trivial task:

  • PyQt4 and PyQt5 are wrappers of Qt4 and Qt5, respectively, so both are affected by the changes of that transition, and one of the transitions is that the QtGui sub-module of Qt4 was divided into the QtGui and QtWidgets sub-modules of Qt5.
  • Some classes and methods are deprecated so you will have to find an equivalent if it exists.

In this case both things happen, the solution for the first case is simple: You must look in the Qt docs and check to which sub-module it belongs, for example QToolTip, at the top there is a table:

Sample Image

And the part of QT += widgets that indicates that it belongs to the QtWidgets sub-module is observed.

But the second case is somewhat more complicated since it involves looking for an equivalent that may or may not be in the same class, in this case it happens with the QPixmap.grabWindow() method which is deprecates (see here for more information). After doing a search you can replace that code with QApplication.primaryScreen().grabWindow(0).

Considering all of the above, the translation is:

import sys
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtGui import QColor, QFont, QPainter, QPixmap, QTextOption, QScreen
from PyQt5.QtWidgets import QApplication, QToolTip, QWidget

class AreaSelector(QWidget):
def __init__(self, parent=None):

QWidget.__init__(self, None, Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setWindowState(Qt.WindowFullScreen)
self.setAutoFillBackground(False)

self.parent = parent
self.start_x = 0
self.start_y = 0
self.end_x = 0
self.end_y = 0
self.current_x = 0
self.current_y = 0

def showEvent(self, event):
self.bg = QApplication.primaryScreen().grabWindow(0)
self.screen_geometry = QApplication.primaryScreen().geometry()

def mousePressEvent(self, event):

self.start_x = event.globalX()
self.start_y = event.globalY()

def mouseReleaseEvent(self, event):

self.end_x = event.globalX()
self.end_y = event.globalY()

def mouseMoveEvent(self, event):

self.current_x = event.globalX()
self.current_y = event.globalY()
self.repaint()

text = "Start: %sx%s \nEnd: %sx%s" % (
self.start_x,
self.start_y,
self.current_x,
self.current_y,
)
QToolTip.showText(event.pos(), text)

def keyPressEvent(self, event):
if event.key() == Qt.Key_Return:
self._acceptSelection()
elif event.key() == Qt.Key_Escape:
self.close()

def _acceptSelection(self):

if self.parent is not None:
self.parent.areaSelectEvent(
self.start_x, self.start_y, self.end_x, self.end_y
)
self.close()

def paintEvent(self, event):

painter = QPainter()
painter.begin(self)

painter.fillRect(self.screen_geometry, QColor(10, 10, 10, 125))

self._paint_selection(painter)
self._paint_usage_text(painter)
painter.end()

def _paint_usage_text(self, painter):

font = QFont("Helvetica [Cronyx]", 26, QFont.Bold)
painter.setFont(font)
painter.setPen(QColor(255, 255, 255, 255))

screen_width = self.screen_geometry.width()
text_width = 800
text_start_x = screen_width / 2 - text_width / 2

screen_height = self.screen_geometry.height()
text_height = 200
text_start_y = screen_height / 2 - text_height / 2

textoption = QTextOption(Qt.AlignCenter)
textbox = QRectF(text_start_x, text_start_y, text_width, text_height)
painter.drawText(
textbox,
"Click & Drag to select an area\n" "ENTER to confirm or ESC to cancel",
textoption,
)
painter.drawRoundedRect(textbox, 20, 20)

def _paint_selection(self, painter):
"""Draws the current user selection"""
rectangle = QRectF()

if self.start_x > self.current_x:
rectangle.setLeft(self.current_x)
rectangle.setRight(self.start_x)

else:
rectangle.setLeft(self.start_x)
rectangle.setRight(self.current_x)

if self.start_y > self.current_y:
rectangle.setTop(self.current_y)
rectangle.setBottom(self.start_y)

else:
rectangle.setTop(self.start_y)
rectangle.setBottom(self.current_y)

painter.drawPixmap(rectangle, self.bg, rectangle)
painter.drawRect(rectangle)

if __name__ == "__main__":
app = QApplication(sys.argv)
main = AreaSelector()
main.show()
sys.exit(app.exec_())

How to translate this code from PyQt4 to PyQt5

To close your window you have to connect your button clicked signal to the close function of your window

btnQuit.clicked.connect(window.close)

The whole code would be :

from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal
import sys

app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("The first example in PyQt")
window.resize(300, 70)

label = QLabel("<center>Hello, world!</center>")
btnQuit = QPushButton("&Close window")
vbox = QVBoxLayout()
vbox.addWidget(label)
vbox.addWidget(btnQuit)
window.setLayout(vbox)

btnQuit.clicked.connect(window.close)

window.show()
sys.exit(app.exec_())

Be careful for your unused imports, you should use

from PyQt5 import QtWidgets

and replace QApplication by QtWidgets.QApplication, QLabel by QtWidgets.QLabel etc...

Keep in mind that signals have changed style since PyQt5 : Description

PyQt4 to PyQt5 migration

It is not possible to dynamically emit arbitrary signals using the new-style syntax. All signals must be pre-defined in the class.

Your example does not make it clear why you need to use a different signal-name for each model, since you are always connecting to the same slot. It would seem to make more sense to have each model emit the same signal, perhaps also sending the model name, if necessary:

class SomeModel(QObject):
gotCommand = pyqtSignal(str, str, object)

def doSomething(self):
...
self.gotCommand.emit(model, cmd, data)

...

self.netlink.gotCommand.connect(self.processCommand)

But if you still need to connect/emit signals by key, you can use getattr:

getattr(self.netlink, self.modelName + "_gotCommand")).connect(self.processCommand)

and:

getattr(self, model + "_gotCommand").emit(cmd, data)

Convert from PyQt5 to PyQt4

I am not an expert in PyQt however I use it to develop a telemetry for Cars, Robots, Small Aircraft. Thanks to eyllanesc its solved now and working well. The problem was that PyQt4 uses QtGui instead of QtWidgets in PyQt5

Here is the sample code:

import sys
import time
import random
from PyQt4 import QtGui, QtCore

class WorkerThread(QtCore.QObject):
signal = QtCore.pyqtSignal(int)

def __init__(self):
super().__init__()

@QtCore.pyqtSlot()
def run(self):
while True:
number = random.randint(1, 1000)
self.signal.emit(number)
time.sleep(1)

class WorkerLabel(QtGui.QLabel):
def __init__(self, parent):
super().__init__()

@QtCore.pyqtSlot(int)
def slot(self, i):
self.setText(str(i))

class UserInterface(QtGui.QWidget):
def __init__(self, parent):
super().__init__()
self.label = WorkerLabel(self)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.label)
self.setLayout(self.layout)

class Main(QtGui.QMainWindow):
def __init__(self):
super().__init__()
self.ui = UserInterface(self)
self.setCentralWidget(self.ui)

self.worker = WorkerThread()
self.workerThread = QtCore.QThread() # Move the Worker object to the Thread object
self.workerThread.started.connect(self.worker.run) # Init worker run() at startup
self.worker.moveToThread(self.workerThread)
self.worker.signal.connect(self.ui.label.slot)
self.workerThread.start()

self.show()

if __name__== '__main__':
app = QtGui.QApplication([])
gui = Main()
sys.exit(app.exec_())

Issue faced while migrating from PyQt4 to PyQt5

Resource files on PyQt are actually python scripts with base64 encoded data.

When porting to newer systems (both python 3 and Qt5) requires proper updating of those files.

Generally, it can be done by calling again the pyrcc command (pyrcc5 or pyrcc5.exe if both Qt versions are installed), but they can be manually ported, considering the following aspects:

  • the import statement has obviously be modified to PyQt5;
  • all variables (qt_resource_data and qt_resource_name) are bytes literals and require the b'...' prefix;

from PyQt5 import QtCore

qt_resource_data = b"\
-- raw data --
"

qt_resource_name = b"\
-- raw data --
"


Related Topics



Leave a reply



Submit