diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b7fb044a..608394ba 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -40,7 +40,7 @@ jobs: name: Build Cpp run: | sudo apt-get update - sudo apt-get install python3-pyqt5 + sudo apt-get install python3-pyqt6 make modules - if: matrix.language == 'python' name: Autobuild diff --git a/core/app.py b/core/app.py index 0d30680b..3e8217a0 100644 --- a/core/app.py +++ b/core/app.py @@ -555,7 +555,9 @@ class DupeGuru(Broadcaster): # a workaround to make the damn thing work. exepath, args = match.groups() path, exename = op.split(exepath) - p = subprocess.Popen(exename + args, shell=True, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p = subprocess.Popen( + exename + args, shell=True, cwd=path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = p.stdout.read() logging.info("Custom command %s %s: %s", exename, args, output) else: diff --git a/hscommon/desktop.py b/hscommon/desktop.py index 09cbdc7d..d96a4704 100644 --- a/hscommon/desktop.py +++ b/hscommon/desktop.py @@ -44,8 +44,8 @@ def special_folder_path(special_folder: SpecialFolder, portable: bool = False) - try: - from PyQt5.QtCore import QUrl, QStandardPaths - from PyQt5.QtGui import QDesktopServices + from PyQt6.QtCore import QUrl, QStandardPaths + from PyQt6.QtGui import QDesktopServices from qt.util import get_appdata from core.util import executable_folder from hscommon.plat import ISWINDOWS, ISOSX @@ -71,7 +71,7 @@ try: if ISWINDOWS and portable: folder = op.join(executable_folder(), "cache") else: - folder = QStandardPaths.standardLocations(QStandardPaths.CacheLocation)[0] + folder = QStandardPaths.standardLocations(QStandardPaths.StandardLocation.CacheLocation)[0] else: folder = get_appdata(portable) return folder diff --git a/hscommon/trans.py b/hscommon/trans.py index 86628831..9d9cb3bc 100644 --- a/hscommon/trans.py +++ b/hscommon/trans.py @@ -82,7 +82,7 @@ def get_locale_name(lang: str) -> Union[str, None]: # --- Qt def install_qt_trans(lang: str = None) -> None: - from PyQt5.QtCore import QCoreApplication, QTranslator, QLocale + from PyQt6.QtCore import QCoreApplication, QTranslator, QLocale if not lang: lang = str(QLocale.system().name())[:2] @@ -139,7 +139,7 @@ def install_gettext_trans_under_qt(base_folder: os.PathLike, lang: str = None) - # So, we install the gettext locale, great, but we also should try to install qt_*.qm if # available so that strings that are inside Qt itself over which I have no control are in the # right language. - from PyQt5.QtCore import QCoreApplication, QTranslator, QLocale, QLibraryInfo + from PyQt6.QtCore import QCoreApplication, QTranslator, QLocale, QLibraryInfo if not lang: lang = str(QLocale.system().name())[:2] @@ -155,7 +155,7 @@ def install_gettext_trans_under_qt(base_folder: os.PathLike, lang: str = None) - if ISLINUX: # Under linux, a full Qt installation is already available in the system, we didn't bundle # up the qm files in our package, so we have to load translations from the system. - qmpath = op.join(QLibraryInfo.location(QLibraryInfo.TranslationsPath), qmname) + qmpath = op.join(QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath), qmname) else: qmpath = op.join(base_folder, qmname) qtr = QTranslator(QCoreApplication.instance()) diff --git a/macos.md b/macos.md index 355d97be..44556351 100644 --- a/macos.md +++ b/macos.md @@ -27,7 +27,7 @@ also need to install that via brew or with pyenv. ### With build.py OSX comes with a version of python 3 by default in newer versions of OSX. To produce universal builds either the 3.8 version shipped in macos or 3.9.1 or newer needs to be used. If needing to -build pyqt5 from source then the first line below is needed, else it may be omitted. (Path shown is +build pyqt6 from source then the first line below is needed, else it may be omitted. (Path shown is for an arm mac.) $ export PATH="/opt/homebrew/opt/qt/bin:$PATH" diff --git a/pkg/debian/control b/pkg/debian/control index 37a41f55..ab7e5e8c 100644 --- a/pkg/debian/control +++ b/pkg/debian/control @@ -10,7 +10,7 @@ Vcs-Git: https://github.com/arsenetar/dupeguru.git Package: {pkgname} Architecture: {arch} -Depends: ${shlibs:Depends}, python3 (>=3.7), python3-pyqt5, python3-mutagen, python3-semantic-version +Depends: ${shlibs:Depends}, python3 (>=3.7), python3-pyqt6, python3-mutagen, python3-semantic-version Provides: dupeguru-se, dupeguru-me, dupeguru-pe Replaces: dupeguru-se, dupeguru-me, dupeguru-pe Conflicts: dupeguru-se, dupeguru-me, dupeguru-pe diff --git a/qt/about_box.py b/qt/about_box.py index 07039cc3..6d69e961 100644 --- a/qt/about_box.py +++ b/qt/about_box.py @@ -6,36 +6,46 @@ # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html -from PyQt5.QtCore import Qt, QCoreApplication, QTimer -from PyQt5.QtGui import QPixmap, QFont -from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QSizePolicy, QHBoxLayout, QVBoxLayout, QLabel +from PyQt6.QtCore import Qt, QCoreApplication, QTimer +from PyQt6.QtGui import QPixmap, QFont, QShowEvent +from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QSizePolicy, QHBoxLayout, QVBoxLayout, QLabel, QWidget from core.util import check_for_update from qt.util import move_to_screen_center from hscommon.trans import trget +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from qt.app import DupeGuru + tr = trget("ui") class AboutBox(QDialog): - def __init__(self, parent, app, **kwargs): - flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.MSWindowsFixedSizeDialogHint + def __init__(self, parent: QWidget, app: "DupeGuru", **kwargs) -> None: + flags = ( + Qt.WindowType.CustomizeWindowHint + | Qt.WindowType.WindowTitleHint + | Qt.WindowType.WindowSystemMenuHint + | Qt.WindowType.MSWindowsFixedSizeDialogHint + ) super().__init__(parent, flags, **kwargs) self.app = app self._setupUi() - self.button_box.accepted.connect(self.accept) - self.button_box.rejected.connect(self.reject) - - def _setupUi(self): + def _setupUi(self) -> None: self.setWindowTitle(tr("About {}").format(QCoreApplication.instance().applicationName())) - size_policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + size_policy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) self.setSizePolicy(size_policy) main_layout = QHBoxLayout(self) + logo_label = QLabel() - logo_label.setPixmap(QPixmap(":/%s_big" % self.app.LOGO_NAME)) + logo_label.setPixmap(QPixmap(f"images:{self.app.LOGO_NAME}_128.png")) main_layout.addWidget(logo_label) + detail_layout = QVBoxLayout() + name_label = QLabel() font = QFont() font.setWeight(75) @@ -43,26 +53,35 @@ class AboutBox(QDialog): name_label.setFont(font) name_label.setText(QCoreApplication.instance().applicationName()) detail_layout.addWidget(name_label) + version_label = QLabel() version_label.setText(tr("Version {}").format(QCoreApplication.instance().applicationVersion())) detail_layout.addWidget(version_label) + self.update_label = QLabel(tr("Checking for updates...")) - self.update_label.setTextInteractionFlags(Qt.TextBrowserInteraction) + self.update_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction) self.update_label.setOpenExternalLinks(True) detail_layout.addWidget(self.update_label) + license_label = QLabel() license_label.setText(tr("Licensed under GPLv3")) detail_layout.addWidget(license_label) + spacer_label = QLabel() spacer_label.setFont(font) detail_layout.addWidget(spacer_label) - self.button_box = QDialogButtonBox() - self.button_box.setOrientation(Qt.Horizontal) - self.button_box.setStandardButtons(QDialogButtonBox.Ok) - detail_layout.addWidget(self.button_box) + + button_box = QDialogButtonBox() + button_box.setOrientation(Qt.Orientation.Horizontal) + button_box.setStandardButtons(QDialogButtonBox.StandardButton.Ok) + detail_layout.addWidget(button_box) + main_layout.addLayout(detail_layout) - def _check_for_update(self): + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + + def _check_for_update(self) -> None: update = check_for_update(QCoreApplication.instance().applicationVersion(), include_prerelease=False) if update is None: self.update_label.setText(tr("No update available.")) @@ -71,7 +90,7 @@ class AboutBox(QDialog): tr('New version {} available, download here.').format(update["version"], update["url"]) ) - def showEvent(self, event): + def showEvent(self, event: QShowEvent) -> None: self.update_label.setText(tr("Checking for updates...")) # have to do this here as the frameGeometry is not correct until shown move_to_screen_center(self) diff --git a/qt/error_report_dialog.py b/qt/error_report_dialog.py index 9a8dc2f9..b3245c96 100644 --- a/qt/error_report_dialog.py +++ b/qt/error_report_dialog.py @@ -11,8 +11,8 @@ import sys import os import platform -from PyQt5.QtCore import Qt, QCoreApplication, QSize -from PyQt5.QtWidgets import ( +from PyQt6.QtCore import Qt, QCoreApplication, QSize +from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, @@ -30,7 +30,7 @@ tr = trget("ui") class ErrorReportDialog(QDialog): def __init__(self, parent, github_url, error, **kwargs): - flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint + flags = Qt.WindowType.CustomizeWindowHint | Qt.WindowType.WindowTitleHint | Qt.WindowType.WindowSystemMenuHint super().__init__(parent, flags, **kwargs) self._setupUi() name = QCoreApplication.applicationName() @@ -40,23 +40,23 @@ class ErrorReportDialog(QDialog): ) # Under windows, we end up with an error report without linesep if we don't mangle it error_text = error_text.replace("\n", os.linesep) - self.errorTextEdit.setPlainText(error_text) + self.error_text_edit.setPlainText(error_text) self.github_url = github_url - self.sendButton.clicked.connect(self.goToGithub) - self.dontSendButton.clicked.connect(self.reject) - def _setupUi(self): self.setWindowTitle(tr("Error Report")) self.resize(553, 349) - self.verticalLayout = QVBoxLayout(self) - self.label = QLabel(self) - self.label.setText(tr("Something went wrong. How about reporting the error?")) - self.label.setWordWrap(True) - self.verticalLayout.addWidget(self.label) - self.errorTextEdit = QPlainTextEdit(self) - self.errorTextEdit.setReadOnly(True) - self.verticalLayout.addWidget(self.errorTextEdit) + main_layout = QVBoxLayout(self) + + title_label = QLabel(self) + title_label.setText(tr("Something went wrong. How about reporting the error?")) + title_label.setWordWrap(True) + main_layout.addWidget(title_label) + + self.error_text_edit = QPlainTextEdit(self) + self.error_text_edit.setReadOnly(True) + main_layout.addWidget(self.error_text_edit) + msg = tr( "Error reports should be reported as Github issues. You can copy the error traceback " "above and paste it in a new issue.\n\nPlease make sure to run a search for any already " @@ -67,21 +67,28 @@ class ErrorReportDialog(QDialog): "Although the application should continue to run after this error, it may be in an " "unstable state, so it is recommended that you restart the application." ) - self.label2 = QLabel(msg) - self.label2.setWordWrap(True) - self.verticalLayout.addWidget(self.label2) - self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.addItem(horizontal_spacer()) - self.dontSendButton = QPushButton(self) - self.dontSendButton.setText(tr("Close")) - self.dontSendButton.setMinimumSize(QSize(110, 0)) - self.horizontalLayout.addWidget(self.dontSendButton) - self.sendButton = QPushButton(self) - self.sendButton.setText(tr("Go to Github")) - self.sendButton.setMinimumSize(QSize(110, 0)) - self.sendButton.setDefault(True) - self.horizontalLayout.addWidget(self.sendButton) - self.verticalLayout.addLayout(self.horizontalLayout) + instructions_label = QLabel(msg) + instructions_label.setWordWrap(True) + main_layout.addWidget(instructions_label) + + button_layout = QHBoxLayout() + button_layout.addItem(horizontal_spacer()) + + close_button = QPushButton(self) + close_button.setText(tr("Close")) + close_button.setMinimumSize(QSize(110, 0)) + button_layout.addWidget(close_button) + + report_button = QPushButton(self) + report_button.setText(tr("Go to Github")) + report_button.setMinimumSize(QSize(110, 0)) + report_button.setDefault(True) + button_layout.addWidget(report_button) + + main_layout.addLayout(button_layout) + + report_button.clicked.connect(self.goToGithub) + close_button.clicked.connect(self.reject) def goToGithub(self): open_url(self.github_url) @@ -91,6 +98,6 @@ def install_excepthook(github_url): def my_excepthook(exctype, value, tb): s = "".join(traceback.format_exception(exctype, value, tb)) dialog = ErrorReportDialog(None, github_url, s) - dialog.exec_() + dialog.exec() sys.excepthook = my_excepthook diff --git a/run.py b/run.py index cc3e94cf..ab7998bc 100644 --- a/run.py +++ b/run.py @@ -9,8 +9,8 @@ import sys import os.path as op import gc -from PyQt6.QtCore import QCoreApplication -from PyQt6.QtGui import QIcon, QPixmap +from PyQt6.QtCore import QDir +from PyQt6.QtGui import QIcon from PyQt6.QtWidgets import QApplication from hscommon.trans import install_gettext_trans_under_qt @@ -48,9 +48,10 @@ def setup_signals(): def main(): app = QApplication(sys.argv) - QCoreApplication.setOrganizationName("Hardcoded Software") - QCoreApplication.setApplicationName(__appname__) - QCoreApplication.setApplicationVersion(__version__) + QApplication.setOrganizationName("Hardcoded Software") + QApplication.setApplicationName(__appname__) + QApplication.setApplicationVersion(__version__) + QDir.addSearchPath("images", op.join(BASE_PATH, "images")) setup_qt_logging() settings = create_qsettings() lang = settings.value("Language") @@ -70,7 +71,7 @@ def main(): # has been installed from qt.app import DupeGuru - app.setWindowIcon(QIcon(QPixmap(f":/{DupeGuru.LOGO_NAME}"))) + app.setWindowIcon(QIcon(f"images:{DupeGuru.LOGO_NAME}_32.png")) global dgapp dgapp = DupeGuru() install_excepthook("https://github.com/arsenetar/dupeguru/issues")