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

Reply via email to