mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-03-09 21:24:36 +00:00
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:
parent
0a4e61edf5
commit
18359c3ea6
@ -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):
|
||||
|
@ -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
4
qtlib/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.mo
|
||||
.DS_Store
|
20
qtlib/app.py
20
qtlib/app.py
@ -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()
|
@ -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
4
run.py
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user