1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2024-11-15 03:59:01 +00:00
dupeguru/qt/util.py

158 lines
6.0 KiB
Python
Raw Normal View History

2019-09-10 00:54:28 +00:00
# Created By: Virgil Dupras
# Created On: 2011-02-01
# 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
import sys
import io
import os.path as op
import os
import logging
2022-07-07 03:40:36 +00:00
from typing import List, Union
2019-09-10 00:54:28 +00:00
from core.util import executable_folder
2019-09-10 00:54:28 +00:00
from hscommon.util import first
from hscommon.plat import ISWINDOWS
2019-09-10 00:54:28 +00:00
2022-07-07 03:40:36 +00:00
from PyQt6.QtCore import QStandardPaths, QSettings
from PyQt6.QtGui import QPixmap, QIcon, QGuiApplication, QAction
from PyQt6.QtWidgets import QSpacerItem, QSizePolicy, QHBoxLayout, QWidget
2019-09-10 00:54:28 +00:00
2022-07-07 03:40:36 +00:00
def move_to_screen_center(widget: QWidget) -> None:
2019-09-10 00:54:28 +00:00
frame = widget.frameGeometry()
if QGuiApplication.screenAt(frame.center()) is None:
# if center not on any screen use default screen
screen = QGuiApplication.screens()[0].availableGeometry()
else:
screen = QGuiApplication.screenAt(frame.center()).availableGeometry()
# moves to center of screen if partially off screen
if screen.contains(frame) is False:
# make sure the frame is not larger than screen
# resize does not seem to take frame size into account (move does)
widget.resize(frame.size().boundedTo(screen.size() - (frame.size() - widget.size())))
frame = widget.frameGeometry()
frame.moveCenter(screen.center())
widget.move(frame.topLeft())
2019-09-10 00:54:28 +00:00
2022-07-07 03:40:36 +00:00
def vertical_spacer(size: Union[int, None] = None) -> QSpacerItem:
2019-09-10 00:54:28 +00:00
if size:
2022-07-07 03:40:36 +00:00
return QSpacerItem(1, size, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
2019-09-10 00:54:28 +00:00
else:
2022-07-07 03:40:36 +00:00
return QSpacerItem(1, 1, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.MinimumExpanding)
2019-09-10 00:54:28 +00:00
2022-07-07 03:40:36 +00:00
def horizontal_spacer(size: Union[int, None] = None) -> QSpacerItem:
2019-09-10 00:54:28 +00:00
if size:
2022-07-07 03:40:36 +00:00
return QSpacerItem(size, 1, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
2019-09-10 00:54:28 +00:00
else:
2022-07-07 03:40:36 +00:00
return QSpacerItem(1, 1, QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Fixed)
2019-09-10 00:54:28 +00:00
2022-07-07 03:40:36 +00:00
def horizontal_wrap(widgets: List[Union[QWidget, int, None]]) -> QHBoxLayout:
2019-09-10 00:54:28 +00:00
"""Wrap all widgets in `widgets` in a horizontal layout.
If, instead of placing a widget in your list, you place an int or None, an horizontal spacer
with the width corresponding to the int will be placed (0 or None means an expanding spacer).
"""
layout = QHBoxLayout()
for widget in widgets:
if widget is None or isinstance(widget, int):
2021-08-24 05:12:23 +00:00
layout.addItem(horizontal_spacer(size=widget))
2019-09-10 00:54:28 +00:00
else:
layout.addWidget(widget)
return layout
2021-08-24 05:12:23 +00:00
def create_actions(actions, target):
# actions are list of (name, shortcut, icon, desc, func)
2019-09-10 00:54:28 +00:00
for name, shortcut, icon, desc, func in actions:
action = QAction(target)
if icon:
2022-07-07 03:40:36 +00:00
action.setIcon(QIcon(QPixmap(":/" + icon))) # TODO stop using qrc file path
2019-09-10 00:54:28 +00:00
if shortcut:
action.setShortcut(shortcut)
action.setText(desc)
action.triggered.connect(func)
setattr(target, name, action)
2021-08-24 05:12:23 +00:00
def set_accel_keys(menu):
2019-09-10 00:54:28 +00:00
actions = menu.actions()
titles = [a.text() for a in actions]
available_characters = {c.lower() for s in titles for c in s if c.isalpha()}
for action in actions:
text = action.text()
c = first(c for c in text if c.lower() in available_characters)
if c is None:
continue
i = text.index(c)
newtext = text[:i] + "&" + text[i:]
2019-09-10 00:54:28 +00:00
available_characters.remove(c.lower())
action.setText(newtext)
2022-07-07 03:40:36 +00:00
def get_appdata(portable: bool = False) -> str:
if portable:
return op.join(executable_folder(), "data")
else:
2022-07-07 03:40:36 +00:00
return QStandardPaths.standardLocations(QStandardPaths.StandardLocation.AppDataLocation)[0]
2019-09-10 00:54:28 +00:00
2019-09-10 00:54:28 +00:00
class SysWrapper(io.IOBase):
def write(self, s):
if s.strip(): # don't log empty stuff
2019-09-10 00:54:28 +00:00
logging.warning(s)
2021-08-24 05:12:23 +00:00
def setup_qt_logging(level=logging.WARNING, log_to_stdout=False):
2019-09-10 00:54:28 +00:00
# Under Qt, we log in "debug.log" in appdata. Moreover, when under cx_freeze, we have a
# problem because sys.stdout and sys.stderr are None, so we need to replace them with a
# wrapper that logs with the logging module.
2021-08-24 05:12:23 +00:00
appdata = get_appdata()
2019-09-10 00:54:28 +00:00
if not op.exists(appdata):
os.makedirs(appdata)
# Setup logging
2019-09-10 00:54:28 +00:00
# Have to use full configuration over basicConfig as FileHandler encoding was not being set.
filename = op.join(appdata, "debug.log") if not log_to_stdout else None
2019-09-10 00:54:28 +00:00
log = logging.getLogger()
handler = logging.FileHandler(filename, "a", "utf-8")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
2019-09-10 00:54:28 +00:00
handler.setFormatter(formatter)
log.addHandler(handler)
if sys.stderr is None: # happens under a cx_freeze environment
2019-09-10 00:54:28 +00:00
sys.stderr = SysWrapper()
if sys.stdout is None:
sys.stdout = SysWrapper()
2021-08-24 05:12:23 +00:00
def escape_amp(s):
2019-09-10 00:54:28 +00:00
# Returns `s` with escaped ampersand (& --> &&). QAction text needs to have & escaped because
# that character is used to define "accel keys".
return s.replace("&", "&&")
2022-07-07 03:40:36 +00:00
def create_qsettings() -> QSettings:
# Create a QSettings instance with the correct arguments.
config_location = op.join(executable_folder(), "settings.ini")
if op.isfile(config_location):
2022-07-07 03:40:36 +00:00
settings = QSettings(config_location, QSettings.Format.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.
2022-07-07 03:40:36 +00:00
locations = QStandardPaths.standardLocations(QStandardPaths.StandardLocation.AppDataLocation)
if locations:
2022-07-07 03:40:36 +00:00
settings = QSettings(op.join(locations[0], "settings.ini"), QSettings.Format.IniFormat)
else:
settings = QSettings()
settings.setValue("Portable", False)
else:
settings = QSettings()
settings.setValue("Portable", False)
return settings