Thank you for this bug report, the new release is uploaded to debian/unstable -----------------8<------------ qr-tools (2.1-3) unstable; urgency=medium
* applied Faidon Liambotis' patch, thanks!. Closes: #1129518
(Patch to port to PyQt6)
-----------------8<------------
Best regards, Georges.
Faidon Liambotis a écrit :
> 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
signature.asc
Description: PGP signature

