--- Begin Message ---
Source: qr-tools
Version: 2.1-2
Severity: minor
Tags: upstream patch
Attached is a patch to port qr-tools from PyQt5 to PyQt6.
I noticed upstream is not active anymore, so I don't think there is
anywhere to forward it. In the absence of an active upstream, I think
it'd be meaningful to carry it to the Debian package.
Thank you for maintaining qr-tools! I've found it to be quite useful.
It's one of the last Qt5 tools on my system, and I started getting
worried about its inclusion in Debian, hence this patch :) HTH!
Regards,
Faidon
diff --git a/debian/control b/debian/control
index eaf78ec..55f335e 100644
--- a/debian/control
+++ b/debian/control
@@ -7,7 +7,7 @@ Build-Depends:
dh-python,
python3,
python3-setuptools,
- qttools5-dev-tools,
+ qt6-tools-dev,
Standards-Version: 4.7.3
Rules-Requires-Root: no
Homepage: https://launchpad.net/qr-tools
@@ -20,7 +20,7 @@ Depends:
python3-pil,
python3-zbar,
qrencode,
- libqt5multimedia5-plugins,
+ libqt6multimedia6,
${misc:Depends},
${python3:Depends}
Description: high level library for reading and generating QR codes
@@ -33,8 +33,8 @@ Architecture: all
Section: utils
Depends:
python3-qrtools,
- python3-pyqt5,
- python3-pyqt5.qtmultimedia,
+ python3-pyqt6,
+ python3-pyqt6.qtmultimedia,
${misc:Depends},
${python3:Depends}
Description: Qt frontend for QR code generator and decoder
diff --git a/debian/patches/Port-from-PyQt5-to-PyQt6.patch b/debian/patches/Port-from-PyQt5-to-PyQt6.patch
new file mode 100644
index 0000000..e73f3b5
--- /dev/null
+++ b/debian/patches/Port-from-PyQt5-to-PyQt6.patch
@@ -0,0 +1,231 @@
+From: Faidon Liambotis <[email protected]>
+Date: Mon, 2 Mar 2026 11:14:01 +0200
+Subject: Port from PyQt5 to PyQt6
+
+---
+ qtqr.py | 86 ++++++++++++++++++++++++++++++++++++-----------------------------
+ 1 file changed, 48 insertions(+), 38 deletions(-)
+
+diff --git a/qtqr.py b/qtqr.py
+index 84f9c7c..0f8276e 100644
+--- a/qtqr.py
++++ b/qtqr.py
+@@ -11,11 +11,10 @@ uses python-zbar for decoding from files and webcam
+
+ import sys, os
+ from math import ceil
+-from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets
++from PyQt6 import QtCore, QtGui, QtNetwork, QtWidgets
+ from qrtools import QR
+ from PIL import Image
+-from PyQt5.QtMultimedia import QCameraInfo
+-from PyQt5.QtWidgets import QDialog
++from PyQt6.QtMultimedia import QMediaDevices
+
+ __author__ = "Ramiro Algozino"
+ __email__ = "[email protected]"
+@@ -233,7 +232,7 @@ class MainWindow(QtWidgets.QMainWindow):
+
+ #QLabel for displaying the Generated QRCode
+ self.qrcode = QtWidgets.QLabel(self.tr('Start typing to create QR Code\n or drop here image files for decoding.'))
+- self.qrcode.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
++ self.qrcode.setAlignment(QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.AlignmentFlag.AlignHCenter)
+ self.scroll = QtWidgets.QScrollArea()
+ self.scroll.setWidgetResizable(True)
+ self.scroll.setWidget(self.qrcode)
+@@ -245,9 +244,9 @@ class MainWindow(QtWidgets.QMainWindow):
+ self.decodeFileButton = QtWidgets.QPushButton(QtGui.QIcon.fromTheme(u'document-open'), self.tr('Decode from &File'))
+ self.decodeWebcamButton = QtWidgets.QPushButton(QtGui.QIcon.fromTheme(u'camera-web'), self.tr('Decode from &Webcam'))
+
+- self.exitAction = QtWidgets.QAction(QtGui.QIcon.fromTheme(u'application-exit'), self.tr('E&xit'), self)
++ self.exitAction = QtGui.QAction(QtGui.QIcon.fromTheme(u'application-exit'), self.tr('E&xit'), self)
+ self.addAction(self.exitAction)
+- self.aboutAction = QtWidgets.QAction(QtGui.QIcon.fromTheme(u"help-about"), self.tr("&About"), self)
++ self.aboutAction = QtGui.QAction(QtGui.QIcon.fromTheme(u"help-about"), self.tr("&About"), self)
+ self.addAction(self.aboutAction)
+
+ # UI Tunning
+@@ -689,7 +688,7 @@ class MainWindow(QtWidgets.QMainWindow):
+ u"Decode from file",
+ u"The file <b>%s</b> doesn't exist." %
+ os.path.abspath(fn),
+- QtWidgets.QMessageBox.Ok
++ QtWidgets.QMessageBox.StandardButton.Ok
+ )
+
+ def decodeFromMemory(self, image):
+@@ -778,29 +777,29 @@ class MainWindow(QtWidgets.QMainWindow):
+ }
+ if action[qr.data_type] != u"":
+ msgBox = QtWidgets.QMessageBox(
+- QtWidgets.QMessageBox.Question,
++ QtWidgets.QMessageBox.Icon.Question,
+ self.tr('Decode QRCode'),
+ msg[qr.data_type]() + action[qr.data_type],
+- QtWidgets.QMessageBox.No |
+- QtWidgets.QMessageBox.Yes,
++ QtWidgets.QMessageBox.StandardButton.No |
++ QtWidgets.QMessageBox.StandardButton.Yes,
+ self
+ )
+- msgBox.addButton(self.tr("&Edit"), QtWidgets.QMessageBox.ApplyRole)
+- msgBox.setDefaultButton(QtWidgets.QMessageBox.Yes)
+- rsp = msgBox.exec_()
++ msgBox.addButton(self.tr("&Edit"), QtWidgets.QMessageBox.ButtonRole.ApplyRole)
++ msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Yes)
++ rsp = msgBox.exec()
+ else:
+ msgBox = QtWidgets.QMessageBox(
+- QtWidgets.QMessageBox.Information,
++ QtWidgets.QMessageBox.Icon.Information,
+ self.tr("Decode QRCode"),
+ msg[qr.data_type]() + action[qr.data_type],
+- QtWidgets.QMessageBox.Ok,
++ QtWidgets.QMessageBox.StandardButton.Ok,
+ self
+ )
+- msgBox.addButton(self.tr("&Edit"), QtWidgets.QMessageBox.ApplyRole)
+- msgBox.setDefaultButton(QtWidgets.QMessageBox.Ok)
+- rsp = msgBox.exec_()
++ msgBox.addButton(self.tr("&Edit"), QtWidgets.QMessageBox.ButtonRole.ApplyRole)
++ msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Ok)
++ rsp = msgBox.exec()
+
+- if rsp == QtWidgets.QMessageBox.Yes:
++ if rsp == QtWidgets.QMessageBox.StandardButton.Yes:
+ #Open Link
+ if qr.data_type == 'email':
+ link = 'mailto:'+ data
+@@ -814,7 +813,7 @@ class MainWindow(QtWidgets.QMainWindow):
+ link = qr.data_decode[qr.data_type](qr.data)
+ print (u"Opening " + link)
+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(link))
+- elif rsp == 0:
++ elif rsp == 0 or rsp == QtWidgets.QMessageBox.StandardButton.NoButton:
+ #Edit the code
+ data = qr.data_decode[qr.data_type](qr.data)
+ try:
+@@ -871,7 +870,7 @@ class MainWindow(QtWidgets.QMainWindow):
+ self.wifiSSIDEdit.setText(data[0] or "")
+ self.wifiEncryptionType.setCurrentIndex({u"WEP":0,u"WPA":1,u"nopass":2}.get(data[1]) or 0)
+ self.wifiPasswordEdit.setText(data[2] or "")
+- self.wifiHiddenNetwork.setCheckState(True if data[3] == "true" else False)
++ self.wifiHiddenNetwork.setChecked(True if data[3] == "true" else False)
+ self.tabs.setCurrentIndex(tabIndex)
+ elif qr.data_type == 'sepa':
+ self.sepaNameEdit.setText(data.get('name')[0])
+@@ -892,10 +891,18 @@ class MainWindow(QtWidgets.QMainWindow):
+ vdDialog = VideoDevices()
+ device_desc = vdDialog.videoDevice.currentText()
+ if vdDialog.videoDevice.count() != 1:
+- d_res = vdDialog.exec_()
+- device_desc = {QDialog.Rejected: '', QDialog.Accepted: vdDialog.videoDevice.currentText()}[d_res]
+- device = {device.description(): device.deviceName()
+- for device in QCameraInfo.availableCameras()}.get(device_desc, None)
++ d_res = vdDialog.exec()
++ device_desc = {
++ QtWidgets.QDialog.DialogCode.Rejected: '',
++ QtWidgets.QDialog.DialogCode.Accepted: vdDialog.videoDevice.currentText(),
++ }[d_res]
++ device = {device.description(): device.id()
++ for device in QMediaDevices.videoInputs()}.get(device_desc, None)
++ if device and not isinstance(device, str):
++ try:
++ device = bytes(device).decode('utf-8', errors='ignore')
++ except Exception:
++ device = str(device)
+
+ if device:
+ qr = QR()
+@@ -906,7 +913,7 @@ class MainWindow(QtWidgets.QMainWindow):
+ self,
+ self.tr("Webcam not availabled"),
+ self.tr("<p>Oops! failed to connect to the webcam.<br /> Maybe your webcam is already busy in another application?</p>"),
+- QtWidgets.QMessageBox.Ok
++ QtWidgets.QMessageBox.StandardButton.Ok
+ )
+ return
+ try:
+@@ -916,7 +923,7 @@ class MainWindow(QtWidgets.QMainWindow):
+ self,
+ self.tr("Decoding Failed"),
+ self.tr(f"<p>oops! Your code seems to be of type '{qr.data_type}', but no decoding for data '{qr.data}' could be found.</p>"),
+- QtWidgets.QMessageBox.Ok
++ QtWidgets.QMessageBox.StandardButton.Ok
+ )
+ else:
+ if matchData == 'NULL':
+@@ -924,7 +931,7 @@ class MainWindow(QtWidgets.QMainWindow):
+ self,
+ self.tr("Decoding Failed"),
+ self.tr("<p>Oops! no code was found.<br /> Maybe your webcam didn't focus.</p>"),
+- QtWidgets.QMessageBox.Ok
++ QtWidgets.QMessageBox.StandardButton.Ok
+ )
+ else:
+ self.showInfo(qr)
+@@ -966,9 +973,9 @@ class MainWindow(QtWidgets.QMainWindow):
+
+ def toggleShowPassword(self, status):
+ if status == 0:
+- self.wifiPasswordEdit.setEchoMode(QtWidgets.QLineEdit.Password)
++ self.wifiPasswordEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
+ elif status == 2:
+- self.wifiPasswordEdit.setEchoMode(QtWidgets.QLineEdit.Normal)
++ self.wifiPasswordEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal)
+
+
+ class VideoDevices(QtWidgets.QDialog):
+@@ -982,13 +989,16 @@ class VideoDevices(QtWidgets.QDialog):
+ self.videoDevice = QtWidgets.QComboBox()
+ self.label = QtWidgets.QLabel(self.tr("You are about to decode from your webcam. Please put the code in front of your camera with a good light source and keep it steady.\nQtQR will try to detect automatically the QR Code.\n\nPlease select the video device you want to use for decoding:"))
+ self.label.setWordWrap(True)
+- self.Buttons = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
++ self.Buttons = QtWidgets.QDialogButtonBox(
++ QtWidgets.QDialogButtonBox.StandardButton.Ok |
++ QtWidgets.QDialogButtonBox.StandardButton.Cancel
++ )
+ self.Buttons.accepted.connect(self.accept)
+ self.Buttons.rejected.connect(self.reject)
+ self.layout = QtWidgets.QVBoxLayout()
+ self.hlayout = QtWidgets.QHBoxLayout()
+ self.vlayout = QtWidgets.QVBoxLayout()
+- self.hlayout.addWidget(self.icon, 0, QtCore.Qt.AlignTop)
++ self.hlayout.addWidget(self.icon, 0, QtCore.Qt.AlignmentFlag.AlignTop)
+ self.vlayout.addWidget(self.label)
+ self.vlayout.addWidget(self.videoDevice)
+ self.hlayout.addLayout(self.vlayout)
+@@ -996,7 +1006,7 @@ class VideoDevices(QtWidgets.QDialog):
+ self.layout.addStretch()
+ self.layout.addWidget(self.Buttons)
+ self.setLayout(self.layout)
+- self.videoDevice.addItems([info.description() for info in QCameraInfo.availableCameras()])
++ self.videoDevice.addItems([info.description() for info in QMediaDevices.videoInputs()])
+
+
+ if __name__ == '__main__':
+@@ -1010,14 +1020,14 @@ if __name__ == '__main__':
+ # "qtqr_" + locale))
+ # We load from standard location the translations
+ translator.load("qtqr_" + locale,
+- QtCore.QLibraryInfo.location(
+- QtCore.QLibraryInfo.TranslationsPath)
++ QtCore.QLibraryInfo.path(
++ QtCore.QLibraryInfo.LibraryPath.TranslationsPath)
+ )
+ app.installTranslator(translator)
+ qtTranslator=QtCore.QTranslator()
+ qtTranslator.load("qt_" + locale,
+- QtCore.QLibraryInfo.location(
+- QtCore.QLibraryInfo.TranslationsPath)
++ QtCore.QLibraryInfo.path(
++ QtCore.QLibraryInfo.LibraryPath.TranslationsPath)
+ )
+ app.installTranslator(qtTranslator)
+
+@@ -1031,4 +1041,4 @@ if __name__ == '__main__':
+ for fn in sys.argv[1:]:
+ # We should check if the file exists.
+ mw.decodeFile(fn)
+- sys.exit(app.exec_())
++ sys.exit(app.exec())
diff --git a/debian/patches/series b/debian/patches/series
index 818836e..3f6fa6d 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,2 @@
fix-961503.patch
+Port-from-PyQt5-to-PyQt6.patch
diff --git a/debian/qtqr.install b/debian/qtqr.install
index 40e08da..8193878 100644
--- a/debian/qtqr.install
+++ b/debian/qtqr.install
@@ -1,2 +1,2 @@
-*.qm usr/share/qt5/translations
+*.qm usr/share/qt6/translations
debian/qtqr.desktop usr/share/applications
diff --git a/debian/rules b/debian/rules
index e760782..c62aa9a 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,7 +1,5 @@
#!/usr/bin/make -f
-export QT_SELECT := 5
-
%:
dh $@ --buildsystem=pybuild --with python3
@@ -9,7 +7,7 @@ ts = $(wildcard qtqr_*.ts)
qm = $(ts:ts=qm)
execute_after_dh_auto_build: $(qm)
$(qm): %.qm: %.ts
- lrelease -nounfinished $< -qm $@
+ /usr/lib/qt6/bin/lrelease -nounfinished $< -qm $@
execute_after_dh_auto_install:
install -m 644 qtqr.py debian/qtqr/usr/bin/qtqr
--- End Message ---