Start flattening Qtlib into qt

- Remove app.py from qtlib (unused)
- Remove .gitignore from qtlib (unecessary)
- Move contents of preferences.py in qtlib to qt, clean up references
- Simplify language dropdown code
This commit is contained in:
Andrew Senetar 2022-05-08 18:51:10 -05:00
parent 0a4e61edf5
commit 18359c3ea6
Signed by: arsenetar
GPG Key ID: C63300DCE48AB2F1
6 changed files with 181 additions and 245 deletions

View File

@ -4,15 +4,176 @@
# which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QDockWidget
from PyQt5.QtCore import Qt, QSettings, QRect, QObject, pyqtSignal, QStandardPaths
from PyQt5.QtGui import QColor
from hscommon import trans
from hscommon.plat import ISLINUX
from hscommon.plat import ISLINUX, ISWINDOWS
from core.app import AppMode
from core.scanner import ScanType
from qtlib.preferences import Preferences as PreferencesBase
from hscommon.util import tryint
from core.util import executable_folder
from os import path as op
def get_langnames():
tr = trans.trget("qtlib") # TODO migrate this to ui instead
return {
"cs": tr("Czech"),
"de": tr("German"),
"el": tr("Greek"),
"en": tr("English"),
"es": tr("Spanish"),
"fr": tr("French"),
"hy": tr("Armenian"),
"it": tr("Italian"),
"ja": tr("Japanese"),
"ko": tr("Korean"),
"ms": tr("Malay"),
"nl": tr("Dutch"),
"pl_PL": tr("Polish"),
"pt_BR": tr("Brazilian"),
"ru": tr("Russian"),
"tr": tr("Turkish"),
"uk": tr("Ukrainian"),
"vi": tr("Vietnamese"),
"zh_CN": tr("Chinese (Simplified)"),
}
def _normalize_for_serialization(v):
# QSettings doesn't consider set/tuple as "native" typs for serialization, so if we don't
# change them into a list, we get a weird serialized QVariant value which isn't a very
# "portable" value.
if isinstance(v, (set, tuple)):
v = list(v)
if isinstance(v, list):
v = [_normalize_for_serialization(item) for item in v]
return v
def _adjust_after_deserialization(v):
# In some cases, when reading from prefs, we end up with strings that are supposed to be
# bool or int. Convert these.
if isinstance(v, list):
return [_adjust_after_deserialization(sub) for sub in v]
if isinstance(v, str):
# might be bool or int, try them
if v == "true":
return True
elif v == "false":
return False
else:
return tryint(v, v)
return v
def create_qsettings():
# Create a QSettings instance with the correct arguments.
config_location = op.join(executable_folder(), "settings.ini")
if op.isfile(config_location):
settings = QSettings(config_location, QSettings.IniFormat)
settings.setValue("Portable", True)
elif ISWINDOWS:
# On windows use an ini file in the AppDataLocation instead of registry if possible as it
# makes it easier for a user to clear it out when there are issues.
locations = QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)
if locations:
settings = QSettings(op.join(locations[0], "settings.ini"), QSettings.IniFormat)
else:
settings = QSettings()
settings.setValue("Portable", False)
else:
settings = QSettings()
settings.setValue("Portable", False)
return settings
class PreferencesBase(QObject):
prefsChanged = pyqtSignal()
def __init__(self):
QObject.__init__(self)
self.reset()
self._settings = create_qsettings()
def _load_values(self, settings):
# Implemented in subclasses
pass
def get_rect(self, name, default=None):
r = self.get_value(name, default)
if r is not None:
return QRect(*r)
else:
return None
def get_value(self, name, default=None):
if self._settings.contains(name):
result = _adjust_after_deserialization(self._settings.value(name))
if result is not None:
return result
else:
# If result is None, but still present in self._settings, it usually means a value
# like "@Invalid".
return default
else:
return default
def load(self):
self.reset()
self._load_values(self._settings)
def reset(self):
# Implemented in subclasses
pass
def _save_values(self, settings):
# Implemented in subclasses
pass
def save(self):
self._save_values(self._settings)
self._settings.sync()
def set_rect(self, name, r):
# About QRect conversion:
# I think Qt supports putting basic structures like QRect directly in QSettings, but I prefer not
# to rely on it and stay with generic structures.
if isinstance(r, QRect):
rect_as_list = [r.x(), r.y(), r.width(), r.height()]
self.set_value(name, rect_as_list)
def set_value(self, name, value):
self._settings.setValue(name, _normalize_for_serialization(value))
def saveGeometry(self, name, widget):
# We save geometry under a 7-sized int array: first item is a flag
# for whether the widget is maximized, second item is a flag for whether
# the widget is docked, third item is a Qt::DockWidgetArea enum value,
# and the other 4 are (x, y, w, h).
m = 1 if widget.isMaximized() else 0
d = 1 if isinstance(widget, QDockWidget) and not widget.isFloating() else 0
area = widget.parent.dockWidgetArea(widget) if d else 0
r = widget.geometry()
rect_as_list = [r.x(), r.y(), r.width(), r.height()]
self.set_value(name, [m, d, area] + rect_as_list)
def restoreGeometry(self, name, widget):
geometry = self.get_value(name)
if geometry and len(geometry) == 7:
m, d, area, x, y, w, h = geometry
if m:
widget.setWindowState(Qt.WindowMaximized)
else:
r = QRect(x, y, w, h)
widget.setGeometry(r)
if isinstance(widget, QDockWidget):
# Inform of the previous dock state and the area used
return bool(d), area
return False, 0
class Preferences(PreferencesBase):

View File

@ -34,35 +34,13 @@ from hscommon import desktop, plat
from hscommon.trans import trget
from hscommon.plat import ISLINUX
from qtlib.util import horizontal_wrap, move_to_screen_center
from qtlib.preferences import get_langnames
from qt.preferences import get_langnames
from enum import Flag, auto
from .preferences import Preferences
tr = trget("ui")
SUPPORTED_LANGUAGES = [
"cs",
"de",
"el",
"en",
"es",
"fr",
"hy",
"it",
"ja",
"ko",
"ms",
"nl",
"pl_PL",
"pt_BR",
"ru",
"tr",
"uk",
"vi",
"zh_CN",
]
class Sections(Flag):
"""Filter blocks of preferences when reset or loaded"""
@ -78,8 +56,7 @@ class PreferencesDialogBase(QDialog):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
super().__init__(parent, flags, **kwargs)
self.app = app
all_languages = get_langnames()
self.supportedLanguages = sorted(SUPPORTED_LANGUAGES, key=lambda lang: all_languages[lang])
self.supportedLanguages = dict(sorted(get_langnames().items(), key=lambda item: item[1]))
self._setupUi()
self.filterHardnessSlider.valueChanged["int"].connect(self.filterHardnessLabel.setNum)
@ -148,8 +125,8 @@ class PreferencesDialogBase(QDialog):
layout = QVBoxLayout()
self.languageLabel = QLabel(tr("Language:"), self)
self.languageComboBox = QComboBox(self)
for lang in self.supportedLanguages:
self.languageComboBox.addItem(get_langnames()[lang])
for lang_code, lang_str in self.supportedLanguages.items():
self.languageComboBox.addItem(lang_str, userData=lang_code)
layout.addLayout(horizontal_wrap([self.languageLabel, self.languageComboBox, None]))
self._setupAddCheckbox(
"tabs_default_pos",
@ -337,10 +314,10 @@ use the modifier key to drag the floating window around"
self.result_table_ref_background_color.setColor(prefs.result_table_ref_background_color)
self.result_table_delta_foreground_color.setColor(prefs.result_table_delta_foreground_color)
try:
langindex = self.supportedLanguages.index(self.app.prefs.language)
except ValueError:
langindex = 0
self.languageComboBox.setCurrentIndex(langindex)
selected_lang = self.supportedLanguages[self.app.prefs.language]
except KeyError:
selected_lang = self.supportedLanguages["en"]
self.languageComboBox.setCurrentText(selected_lang)
if section & Sections.DEBUG:
setchecked(self.debugModeBox, prefs.debug_mode)
setchecked(self.profile_scan_box, prefs.profile_scan)
@ -373,17 +350,17 @@ use the modifier key to drag the floating window around"
prefs.use_native_dialogs = ischecked(self.use_native_dialogs)
if plat.ISWINDOWS:
prefs.use_dark_style = ischecked(self.use_dark_style)
lang = self.supportedLanguages[self.languageComboBox.currentIndex()]
oldlang = self.app.prefs.language
if oldlang not in self.supportedLanguages:
oldlang = "en"
if lang != oldlang:
lang_code = self.languageComboBox.currentData()
old_lang_code = self.app.prefs.language
if old_lang_code not in self.supportedLanguages.keys():
old_lang_code = "en"
if lang_code != old_lang_code:
QMessageBox.information(
self,
"",
tr("dupeGuru has to restart for language changes to take effect."),
)
self.app.prefs.language = lang
self.app.prefs.language = lang_code
self._save(prefs, ischecked)
def resetToDefaults(self, section_to_update):

4
qtlib/.gitignore vendored
View File

@ -1,4 +0,0 @@
__pycache__
*.pyc
*.mo
.DS_Store

View File

@ -1,20 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2009-10-16
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# 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 pyqtSignal, QTimer, QObject
class Application(QObject):
finishedLaunching = pyqtSignal()
def __init__(self):
QObject.__init__(self)
QTimer.singleShot(0, self.__launchTimerTimedOut)
def __launchTimerTimedOut(self):
self.finishedLaunching.emit()

View File

@ -1,178 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2009-05-03
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# 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, QSettings, QRect, QObject, pyqtSignal, QStandardPaths
from PyQt5.QtWidgets import QDockWidget
from hscommon.trans import trget
from hscommon.util import tryint
from hscommon.plat import ISWINDOWS
from core.util import executable_folder
from os import path as op
tr = trget("qtlib")
def get_langnames():
return {
"cs": tr("Czech"),
"de": tr("German"),
"el": tr("Greek"),
"en": tr("English"),
"es": tr("Spanish"),
"fr": tr("French"),
"hy": tr("Armenian"),
"it": tr("Italian"),
"ja": tr("Japanese"),
"ko": tr("Korean"),
"ms": tr("Malay"),
"nl": tr("Dutch"),
"pl_PL": tr("Polish"),
"pt_BR": tr("Brazilian"),
"ru": tr("Russian"),
"tr": tr("Turkish"),
"uk": tr("Ukrainian"),
"vi": tr("Vietnamese"),
"zh_CN": tr("Chinese (Simplified)"),
}
def normalize_for_serialization(v):
# QSettings doesn't consider set/tuple as "native" typs for serialization, so if we don't
# change them into a list, we get a weird serialized QVariant value which isn't a very
# "portable" value.
if isinstance(v, (set, tuple)):
v = list(v)
if isinstance(v, list):
v = [normalize_for_serialization(item) for item in v]
return v
def adjust_after_deserialization(v):
# In some cases, when reading from prefs, we end up with strings that are supposed to be
# bool or int. Convert these.
if isinstance(v, list):
return [adjust_after_deserialization(sub) for sub in v]
if isinstance(v, str):
# might be bool or int, try them
if v == "true":
return True
elif v == "false":
return False
else:
return tryint(v, v)
return v
def create_qsettings():
# Create a QSettings instance with the correct arguments.
config_location = op.join(executable_folder(), "settings.ini")
if op.isfile(config_location):
settings = QSettings(config_location, QSettings.IniFormat)
settings.setValue("Portable", True)
elif ISWINDOWS:
# On windows use an ini file in the AppDataLocation instead of registry if possible as it
# makes it easier for a user to clear it out when there are issues.
locations = QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)
if locations:
settings = QSettings(op.join(locations[0], "settings.ini"), QSettings.IniFormat)
else:
settings = QSettings()
settings.setValue("Portable", False)
else:
settings = QSettings()
settings.setValue("Portable", False)
return settings
# About QRect conversion:
# I think Qt supports putting basic structures like QRect directly in QSettings, but I prefer not
# to rely on it and stay with generic structures.
class Preferences(QObject):
prefsChanged = pyqtSignal()
def __init__(self):
QObject.__init__(self)
self.reset()
self._settings = create_qsettings()
def _load_values(self, settings):
# Implemented in subclasses
pass
def get_rect(self, name, default=None):
r = self.get_value(name, default)
if r is not None:
return QRect(*r)
else:
return None
def get_value(self, name, default=None):
if self._settings.contains(name):
result = adjust_after_deserialization(self._settings.value(name))
if result is not None:
return result
else:
# If result is None, but still present in self._settings, it usually means a value
# like "@Invalid".
return default
else:
return default
def load(self):
self.reset()
self._load_values(self._settings)
def reset(self):
# Implemented in subclasses
pass
def _save_values(self, settings):
# Implemented in subclasses
pass
def save(self):
self._save_values(self._settings)
self._settings.sync()
def set_rect(self, name, r):
if isinstance(r, QRect):
rect_as_list = [r.x(), r.y(), r.width(), r.height()]
self.set_value(name, rect_as_list)
def set_value(self, name, value):
self._settings.setValue(name, normalize_for_serialization(value))
def saveGeometry(self, name, widget):
# We save geometry under a 7-sized int array: first item is a flag
# for whether the widget is maximized, second item is a flag for whether
# the widget is docked, third item is a Qt::DockWidgetArea enum value,
# and the other 4 are (x, y, w, h).
m = 1 if widget.isMaximized() else 0
d = 1 if isinstance(widget, QDockWidget) and not widget.isFloating() else 0
area = widget.parent.dockWidgetArea(widget) if d else 0
r = widget.geometry()
rect_as_list = [r.x(), r.y(), r.width(), r.height()]
self.set_value(name, [m, d, area] + rect_as_list)
def restoreGeometry(self, name, widget):
geometry = self.get_value(name)
if geometry and len(geometry) == 7:
m, d, area, x, y, w, h = geometry
if m:
widget.setWindowState(Qt.WindowMaximized)
else:
r = QRect(x, y, w, h)
widget.setGeometry(r)
if isinstance(widget, QDockWidget):
# Inform of the previous dock state and the area used
return bool(d), area
return False, 0

4
run.py
View File

@ -16,7 +16,7 @@ from PyQt5.QtWidgets import QApplication
from hscommon.trans import install_gettext_trans_under_qt
from qtlib.error_report_dialog import install_excepthook
from qtlib.util import setup_qt_logging
from qtlib.preferences import create_qsettings
from qt.preferences import create_qsettings
from qt import dg_rc # noqa: F401
from qt.platform import BASE_PATH
from core import __version__, __appname__
@ -74,7 +74,7 @@ def main():
app.setWindowIcon(QIcon(QPixmap(f":/{DupeGuru.LOGO_NAME}")))
global dgapp
dgapp = DupeGuru()
install_excepthook("https://github.com/hsoft/dupeguru/issues")
install_excepthook("https://github.com/arsenetar/dupeguru/issues")
result = app.exec()
# I was getting weird crashes when quitting under Windows, and manually deleting main app
# references with gc.collect() in between seems to fix the problem.