From 4cd0df72993261631e4fdf0f6d1353fd48780751 Mon Sep 17 00:00:00 2001
From: Kashiko Koibumi <kashiko@tuta.io>
Date: Wed, 22 May 2024 13:05:32 +0900
Subject: [PATCH] use qtpy directly instead of using fallback-PyQt5

---
 docs/conf.py                            |  3 +-
 src/bitmessageqt/__init__.py            |  4 +-
 src/bitmessageqt/address_dialogs.py     |  2 +-
 src/bitmessageqt/addressvalidator.py    |  2 +-
 src/bitmessageqt/bitmessage_icons_rc.py |  2 +-
 src/bitmessageqt/bitmessageui.py        |  2 +-
 src/bitmessageqt/blacklist.py           |  2 +-
 src/bitmessageqt/dialogs.py             |  2 +-
 src/bitmessageqt/foldertree.py          |  2 +-
 src/bitmessageqt/languagebox.py         |  2 +-
 src/bitmessageqt/messagecompose.py      |  2 +-
 src/bitmessageqt/messageview.py         |  2 +-
 src/bitmessageqt/migrationwizard.py     |  3 +-
 src/bitmessageqt/networkstatus.py       |  2 +-
 src/bitmessageqt/newchandialog.py       |  2 +-
 src/bitmessageqt/retranslateui.py       |  2 +-
 src/bitmessageqt/settings.py            |  2 +-
 src/bitmessageqt/settingsmixin.py       |  2 +-
 src/bitmessageqt/statusbar.py           |  2 +-
 src/bitmessageqt/tests/main.py          |  2 +-
 src/bitmessageqt/uisignaler.py          |  2 +-
 src/bitmessageqt/utils.py               |  2 +-
 src/bitmessageqt/widgets.py             |  2 +-
 src/depends.py                          | 55 +++++++++++++++----------
 src/fallback/__init__.py                | 43 -------------------
 src/qidenticon.py                       |  5 +--
 src/tests/test_identicon.py             |  2 +-
 src/tr.py                               | 20 +--------
 28 files changed, 62 insertions(+), 113 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index b0cfef7b..bc0505cc 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -216,8 +216,7 @@ autodoc_mock_imports = [
     'pkg_resources',
     'pycanberra',
     'pyopencl',
-    'PyQt4',
-    'PyQt5',
+    'qtpy',
     'qrcode',
     'stem',
     'xdg',
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 43b958a2..4b3e63da 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -16,7 +16,7 @@ import time
 from datetime import datetime, timedelta
 from sqlite3 import register_adapter
 
-from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
+from qtpy import QtCore, QtGui, QtWidgets, QtNetwork
 
 import dialogs
 import helper_addressbook
@@ -1442,7 +1442,7 @@ class MyForm(settingsmixin.SMainWindow):
         self._theme_player = get_plugin('notification.sound', 'theme')
 
         try:
-            from PyQt5 import QtMultimedia
+            from qtpy import QtMultimedia
             self._player = QtMultimedia.QSound.play
         except ImportError:
             _plugin = get_plugin(
diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py
index 92b18ae7..903b588a 100644
--- a/src/bitmessageqt/address_dialogs.py
+++ b/src/bitmessageqt/address_dialogs.py
@@ -6,7 +6,7 @@ Dialogs that work with BM address.
 
 import hashlib
 
-from PyQt5 import QtGui, QtWidgets
+from qtpy import QtGui, QtWidgets
 
 import queues
 import widgets
diff --git a/src/bitmessageqt/addressvalidator.py b/src/bitmessageqt/addressvalidator.py
index ac1e70bc..600347c6 100644
--- a/src/bitmessageqt/addressvalidator.py
+++ b/src/bitmessageqt/addressvalidator.py
@@ -6,7 +6,7 @@ used in `.dialogs.NewChanDialog`.
 
 from Queue import Empty
 
-from PyQt5 import QtGui
+from qtpy import QtGui
 
 from addresses import decodeAddress, addBMIfNotPresent
 from bmconfigparser import config
diff --git a/src/bitmessageqt/bitmessage_icons_rc.py b/src/bitmessageqt/bitmessage_icons_rc.py
index 5d792c48..68404748 100644
--- a/src/bitmessageqt/bitmessage_icons_rc.py
+++ b/src/bitmessageqt/bitmessage_icons_rc.py
@@ -7,7 +7,7 @@
 #
 # WARNING! All changes made in this file will be lost!
 
-from PyQt5 import QtCore
+from qtpy import QtCore
 
 qt_resource_data = "\
 \x00\x00\x03\x66\
diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py
index bd888309..7a034dfa 100644
--- a/src/bitmessageqt/bitmessageui.py
+++ b/src/bitmessageqt/bitmessageui.py
@@ -1,7 +1,7 @@
 # pylint: skip-file
 # flake8: noqa
 
-from PyQt5 import QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
 from tr import _translate
 from bmconfigparser import config
 from foldertree import AddressBookCompleter
diff --git a/src/bitmessageqt/blacklist.py b/src/bitmessageqt/blacklist.py
index 52a366ac..ae271866 100644
--- a/src/bitmessageqt/blacklist.py
+++ b/src/bitmessageqt/blacklist.py
@@ -1,4 +1,4 @@
-from PyQt5 import QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
 
 import widgets
 from addresses import addBMIfNotPresent
diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py
index fc828fef..07224f00 100644
--- a/src/bitmessageqt/dialogs.py
+++ b/src/bitmessageqt/dialogs.py
@@ -3,7 +3,7 @@ All dialogs are available in this module.
 """
 # pylint: disable=too-few-public-methods
 
-from PyQt5 import QtWidgets
+from qtpy import QtWidgets
 
 import paths
 import widgets
diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py
index 235aaf5c..57c2cd12 100644
--- a/src/bitmessageqt/foldertree.py
+++ b/src/bitmessageqt/foldertree.py
@@ -6,7 +6,7 @@ Folder tree and messagelist widgets definitions.
 
 from cgi import escape
 
-from PyQt5 import QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
 
 from bmconfigparser import config
 from helper_sql import sqlExecute, sqlQuery
diff --git a/src/bitmessageqt/languagebox.py b/src/bitmessageqt/languagebox.py
index 1fd69334..c4b61154 100644
--- a/src/bitmessageqt/languagebox.py
+++ b/src/bitmessageqt/languagebox.py
@@ -3,7 +3,7 @@
 import glob
 import os
 
-from PyQt5 import QtCore, QtWidgets
+from qtpy import QtCore, QtWidgets
 
 import paths
 from bmconfigparser import config
diff --git a/src/bitmessageqt/messagecompose.py b/src/bitmessageqt/messagecompose.py
index c520030b..68de5ce0 100644
--- a/src/bitmessageqt/messagecompose.py
+++ b/src/bitmessageqt/messagecompose.py
@@ -1,6 +1,6 @@
 """The MessageCompose class definition"""
 
-from PyQt5 import QtCore, QtWidgets
+from qtpy import QtCore, QtWidgets
 from tr import _translate
 
 
diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py
index 364a92c2..ebf23c87 100644
--- a/src/bitmessageqt/messageview.py
+++ b/src/bitmessageqt/messageview.py
@@ -4,7 +4,7 @@ text rendering, HTML sanitization, lazy rendering (as you scroll down),
 zoom and URL click warning popup.
 """
 
-from PyQt5 import QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
 
 from safehtmlparser import SafeHTMLParser
 from tr import _translate
diff --git a/src/bitmessageqt/migrationwizard.py b/src/bitmessageqt/migrationwizard.py
index 2bc32849..239770d2 100644
--- a/src/bitmessageqt/migrationwizard.py
+++ b/src/bitmessageqt/migrationwizard.py
@@ -1,5 +1,4 @@
-#!/usr/bin/env python2.7
-from PyQt5 import QtCore, QtWidgets
+from qtpy import QtCore, QtWidgets
 
 class MigrationWizardIntroPage(QtWidgets.QWizardPage):
     def __init__(self):
diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py
index 318d44ab..9c7e5a25 100644
--- a/src/bitmessageqt/networkstatus.py
+++ b/src/bitmessageqt/networkstatus.py
@@ -4,7 +4,7 @@ Network status tab widget definition.
 
 import time
 
-from PyQt5 import QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
 
 import l10n
 import network.stats
diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py
index f4a89eea..2091a7ab 100644
--- a/src/bitmessageqt/newchandialog.py
+++ b/src/bitmessageqt/newchandialog.py
@@ -2,7 +2,7 @@
 NewChanDialog class definition
 """
 
-from PyQt5 import QtCore, QtWidgets
+from qtpy import QtCore, QtWidgets
 
 import widgets
 from addresses import addBMIfNotPresent
diff --git a/src/bitmessageqt/retranslateui.py b/src/bitmessageqt/retranslateui.py
index dd08e53c..706c3e81 100644
--- a/src/bitmessageqt/retranslateui.py
+++ b/src/bitmessageqt/retranslateui.py
@@ -1,4 +1,4 @@
-from PyQt5 import QtWidgets
+from qtpy import QtWidgets
 
 import widgets
 
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index 34c9c5d0..c784b2aa 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -7,7 +7,7 @@ import os
 import sys
 import tempfile
 
-from PyQt5 import QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
 import six
 
 import debug
diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py
index a5b8db5c..9e53c6fb 100644
--- a/src/bitmessageqt/settingsmixin.py
+++ b/src/bitmessageqt/settingsmixin.py
@@ -4,7 +4,7 @@ src/settingsmixin.py
 
 """
 
-from PyQt5 import QtCore, QtWidgets
+from qtpy import QtCore, QtWidgets
 
 
 class SettingsMixin(object):
diff --git a/src/bitmessageqt/statusbar.py b/src/bitmessageqt/statusbar.py
index 006ba564..478d570c 100644
--- a/src/bitmessageqt/statusbar.py
+++ b/src/bitmessageqt/statusbar.py
@@ -2,7 +2,7 @@
 
 from time import time
 
-from PyQt5 import QtWidgets
+from qtpy import QtWidgets
 
 
 class BMStatusBar(QtWidgets.QStatusBar):
diff --git a/src/bitmessageqt/tests/main.py b/src/bitmessageqt/tests/main.py
index 9631878d..1d65f16e 100644
--- a/src/bitmessageqt/tests/main.py
+++ b/src/bitmessageqt/tests/main.py
@@ -4,7 +4,7 @@ import Queue
 import sys
 import unittest
 
-from PyQt5 import QtCore, QtWidgets
+from qtpy import QtCore, QtWidgets
 from six import string_types
 
 import bitmessageqt
diff --git a/src/bitmessageqt/uisignaler.py b/src/bitmessageqt/uisignaler.py
index 6b72344e..8f34aff0 100644
--- a/src/bitmessageqt/uisignaler.py
+++ b/src/bitmessageqt/uisignaler.py
@@ -1,6 +1,6 @@
 import sys
 
-from PyQt5 import QtCore
+from qtpy import QtCore
 
 import queues
 from network.node import Peer
diff --git a/src/bitmessageqt/utils.py b/src/bitmessageqt/utils.py
index cb5aedcb..cdeb0331 100644
--- a/src/bitmessageqt/utils.py
+++ b/src/bitmessageqt/utils.py
@@ -1,7 +1,7 @@
 import hashlib
 import os
 
-from PyQt5 import QtGui
+from qtpy import QtGui
 
 import state
 from addresses import addBMIfNotPresent
diff --git a/src/bitmessageqt/widgets.py b/src/bitmessageqt/widgets.py
index c4a91375..e3232fe6 100644
--- a/src/bitmessageqt/widgets.py
+++ b/src/bitmessageqt/widgets.py
@@ -1,4 +1,4 @@
-from PyQt5 import uic
+from qtpy import uic
 import os.path
 import paths
 
diff --git a/src/depends.py b/src/depends.py
index 1c42f441..6aeb32ef 100755
--- a/src/depends.py
+++ b/src/depends.py
@@ -69,8 +69,8 @@ PACKAGES = {
         "description":
         "You only need qtpy if you want to use the GUI."
         " When only running as a daemon, this can be skipped.\n"
-        "Also maybe you need to install PyQt5 if your package manager"
-        " not installs it as qtpy dependency"
+        "Also maybe you need to install PyQt5 or PyQt4"
+        " if your package manager not installs it as qtpy dependency"
     },
     "msgpack": {
         "OpenBSD": "py-msgpack",
@@ -381,34 +381,47 @@ def check_curses():
 def check_pyqt():
     """Do pyqt dependency check.
 
-    Here we are checking for PyQt4 with its version, as for it require
+    Here we are checking for qtpy with its version, as for it require
     PyQt 4.8 or later.
     """
     # pylint: disable=no-member
     try:
-        from fallback import PyQt5
+        import qtpy
     except ImportError:
         logger.error(
-            'PyBitmessage requires PyQt5 or qtpy, PyQt 4.8 or later'
-            ' and Qt 4.7 or later.')
-        PyQt5 = None
-
-    if not PyQt5:
+            'PyBitmessage requires qtpy, and PyQt5 or PyQt4, '
+            ' PyQt 4.8 or later and Qt 4.7 or later.')
         return False
 
-    logger.info('PyQt Version: %s', PyQt5.PYQT_VERSION)
-    logger.info('Qt Version: %s', PyQt5.QT_VERSION)
+    from qtpy import QtCore
+    try:
+        logger.info('PyQt Version: %s', QtCore.PYQT_VERSION_STR)
+    except AttributeError:
+        logger.info('Can be PySide..')
+    try:
+        logger.info('Qt Version: %s', QtCore.__version__)
+    except AttributeError:
+        # Can be PySide..
+        pass
     passed = True
-    if version.LooseVersion(PyQt5.PYQT_VERSION) < '4.8':
-        logger.error(
-            'This version of PyQt is too old. PyBitmessage requries'
-            ' PyQt 4.8 or later.')
-        passed = False
-    if version.LooseVersion(PyQt5.QT_VERSION) < '4.7':
-        logger.error(
-            'This version of Qt is too old. PyBitmessage requries'
-            ' Qt 4.7 or later.')
-        passed = False
+    try:
+        if version.LooseVersion(QtCore.PYQT_VERSION_STR) < '4.8':
+            logger.error(
+                'This version of PyQt is too old. PyBitmessage requries'
+                ' PyQt 4.8 or later.')
+            passed = False
+    except AttributeError:
+        # Can be PySide..
+        pass
+    try:
+        if version.LooseVersion(QtCore.__version__) < '4.7':
+            logger.error(
+                'This version of Qt is too old. PyBitmessage requries'
+                ' Qt 4.7 or later.')
+            passed = False
+    except AttributeError:
+        # Can be PySide..
+        pass
     return passed
 
 
diff --git a/src/fallback/__init__.py b/src/fallback/__init__.py
index a0b8b6b4..f65999a1 100644
--- a/src/fallback/__init__.py
+++ b/src/fallback/__init__.py
@@ -30,46 +30,3 @@ else:
         if data:
             hasher.update(data)
         return hasher
-
-try:
-    import PyQt5
-except ImportError:
-    try:
-        import qtpy as PyQt5
-    except ImportError:
-        pass
-else:
-    from PyQt5 import QtCore
-
-    QtCore.Signal = QtCore.pyqtSignal
-    PyQt5.PYQT_VERSION = QtCore.PYQT_VERSION_STR
-    PyQt5.QT_VERSION = QtCore.QT_VERSION_STR
-    # try:
-    #     from qtpy import QtCore, QtGui, QtWidgets, QtNetwork, uic
-    # except ImportError:
-    #     PyQt5 = None
-    # else:
-    #     import sys
-    #     import types
-
-    #     QtCore.Signal = QtCore.pyqtSignal
-    #     context = {
-    #         'API': 'pyqt5',  # for tr
-    #         'PYQT_VERSION': QtCore.PYQT_VERSION_STR,
-    #         'QT_VERSION': QtCore.QT_VERSION_STR,
-    #         'QtCore': QtCore,
-    #         'QtGui': QtGui,
-    #         'QtWidgets': QtWidgets,
-    #         'QtNetwork': QtNetwork,
-    #         'uic': uic
-    #     }
-    #     try:
-    #         from PyQt5 import QtTest
-    #     except ImportError:
-    #         pass
-    #     else:
-    #         context['QtTest'] = QtTest
-    #     PyQt5 = types.ModuleType(
-    #         'PyQt5', 'qtpy based dynamic fallback for PyQt5')
-    #     PyQt5.__dict__.update(context)
-    #     sys.modules['PyQt5'] = PyQt5
diff --git a/src/qidenticon.py b/src/qidenticon.py
index 049c0b2a..722c47ca 100644
--- a/src/qidenticon.py
+++ b/src/qidenticon.py
@@ -42,10 +42,7 @@ Returns an instance of :class:`QPixmap` which have generated identicon image.
 
 from six.moves import range
 
-try:
-    from PyQt5 import QtCore, QtGui
-except (ImportError, RuntimeError):
-    from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtGui
 
 
 class IdenticonRendererBase(object):
diff --git a/src/tests/test_identicon.py b/src/tests/test_identicon.py
index 4c6be32d..35503d1c 100644
--- a/src/tests/test_identicon.py
+++ b/src/tests/test_identicon.py
@@ -4,7 +4,7 @@ import atexit
 import unittest
 
 try:
-    from PyQt5 import QtGui, QtWidgets
+    from qtpy import QtGui, QtWidgets
     from xvfbwrapper import Xvfb
     from pybitmessage import qidenticon
 except ImportError:
diff --git a/src/tr.py b/src/tr.py
index 765ba55c..45e8668c 100644
--- a/src/tr.py
+++ b/src/tr.py
@@ -15,26 +15,10 @@ def _tr_dummy(context, text, disambiguation=None, n=None):
 
 if state.enableGUI and not state.curses:
     try:
-        from fallback import PyQt5  # noqa:F401
-        from PyQt5 import QtWidgets, QtCore
+        from qtpy import QtWidgets, QtCore
     except ImportError:
         _translate = _tr_dummy
     else:
-        try:
-            from PyQt5 import API
-        except ImportError:
-            API = 'pyqt5'
-        if API == 'pyqt5':
-            _translate = QtWidgets.QApplication.translate
-        else:
-            def _translate(context, text, disambiguation=None, n=None):
-                return (
-                    QtWidgets.QApplication.translate(
-                        context, text, disambiguation)
-                    if n is None else
-                    QtWidgets.QApplication.translate(
-                        context, text, disambiguation,
-                        QtCore.QCoreApplication.CodecForTr, n)
-                )
+        _translate = QtWidgets.QApplication.translate
 else:
     _translate = _tr_dummy