This commit is contained in:
2025-11-24 00:15:32 +08:00
parent 9f7667a475
commit 0eeeb54784
256 changed files with 49494 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from studioqt.utils import *
from studioqt.icon import Icon
from studioqt.menu import Menu
from studioqt.color import Color
from studioqt.pixmap import Pixmap
from studioqt.stylesheet import StyleSheet
from studioqt.decorators import showWaitCursor
from studioqt.decorators import showArrowCursor
from studioqt.imagesequence import ImageSequence

View File

@@ -0,0 +1,78 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from studiovendor.Qt import QtGui
COLORS = [
{"name": "red", "color": "rgb(255, 115, 100)"},
{"name": "orange", "color": "rgb(255, 150, 100)"},
{"name": "yellow", "color": "rgb(255, 210, 103)"},
{"name": "green", "color": "rgb(140, 220, 140)"},
{"name": "blue", "color": "rgb(110, 175, 255)"},
{"name": "purple", "color": "rgb(160, 120, 255)"},
{"name": "pink", "color": "rgb(230, 130, 180)"},
{"name": "grey", "color": "rgb(125, 125, 140)"},
]
COLORS_INDEXED = {c["name"]: c for c in COLORS}
class Color(QtGui.QColor):
@classmethod
def fromColor(cls, color):
"""
:type color: QtGui.QColor
"""
color = ('rgba(%d, %d, %d, %d)' % color.getRgb())
return cls.fromString(color)
@classmethod
def fromString(cls, c):
"""
:type c: str
"""
a = 255
c = c.replace(";", "")
if not c.startswith("rgb"):
c = COLORS_INDEXED.get(c)
if c:
c = c.get("color")
else:
return cls(0, 0, 0, 255)
try:
r, g, b, a = c.replace("rgb(", "").replace("rgba(", "").replace(")", "").split(",")
except ValueError:
r, g, b = c.replace("rgb(", "").replace(")", "").split(",")
return cls(int(r), int(g), int(b), int(a))
def __eq__(self, other):
if isinstance(other, Color):
return self.toString() == other.toString()
else:
return QtGui.QColor.__eq__(self, other)
def toString(self):
"""
:type: str
"""
return 'rgba(%d, %d, %d, %d)' % self.getRgb()
def isDark(self):
"""
:type: bool
"""
return self.red() < 125 and self.green() < 125 and self.blue() < 125

View File

@@ -0,0 +1,47 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from studiovendor.Qt import QtGui
from studiovendor.Qt import QtCore
from studiovendor.Qt import QtWidgets
def showWaitCursor(fn):
def wrapped(*args, **kwargs):
cursor = QtGui.QCursor(QtCore.Qt.WaitCursor)
QtWidgets.QApplication.setOverrideCursor(cursor)
try:
return fn(*args, **kwargs)
finally:
QtWidgets.QApplication.restoreOverrideCursor()
wrapped.__name__ = fn.__name__
wrapped.__doc__ = fn.__doc__
return wrapped
def showArrowCursor(fn):
def wrapped(*args, **kwargs):
cursor = QtGui.QCursor(QtCore.Qt.ArrowCursor)
QtWidgets.QApplication.setOverrideCursor(cursor)
try:
return fn(*args, **kwargs)
finally:
QtWidgets.QApplication.restoreOverrideCursor()
wrapped.__name__ = fn.__name__
wrapped.__doc__ = fn.__doc__
return wrapped

View File

@@ -0,0 +1,185 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import copy
from studiovendor.Qt import QtGui
from studiovendor.Qt import QtCore
import studioqt
class Icon(QtGui.QIcon):
@classmethod
def fa(cls, path, **kwargs):
"""
Create a new icon with the given path, options and state.
Example:
icon = studioqt.Icon.fa(
path,
color="rgb(255,255,255)"
color_disabled="rgba(0,200,200,20)",
)
:type path: str
:type kwargs: dict
:rtype: studioqt.Icon
"""
color = kwargs.get('color', QtGui.QColor(0, 0, 0))
pixmap = studioqt.Pixmap(path)
pixmap.setColor(color)
valid_options = [
'active',
'selected',
'disabled',
'on',
'off',
'on_active',
'on_selected',
'on_disabled',
'off_active',
'off_selected',
'off_disabled',
'color',
'color_on',
'color_off',
'color_active',
'color_selected',
'color_disabled',
'color_on_selected',
'color_on_active',
'color_on_disabled',
'color_off_selected',
'color_off_active',
'color_off_disabled',
]
default = {
"on_active": kwargs.get("active", studioqt.Pixmap(path)),
"off_active": kwargs.get("active", studioqt.Pixmap(path)),
"on_disabled": kwargs.get("disabled", studioqt.Pixmap(path)),
"off_disabled": kwargs.get("disabled", studioqt.Pixmap(path)),
"on_selected": kwargs.get("selected", studioqt.Pixmap(path)),
"off_selected": kwargs.get("selected", studioqt.Pixmap(path)),
"color_on_active": kwargs.get("color_active", color),
"color_off_active": kwargs.get("color_active", color),
"color_on_disabled": kwargs.get("color_disabled", color),
"color_off_disabled": kwargs.get("color_disabled", color),
"color_on_selected": kwargs.get("color_selected", color),
"color_off_selected": kwargs.get("color_selected", color),
}
default.update(kwargs)
kwargs = copy.copy(default)
for option in valid_options:
if 'color' in option:
kwargs[option] = kwargs.get(option, color)
else:
svg_path = kwargs.get(option, path)
kwargs[option] = studioqt.Pixmap(svg_path)
options = {
QtGui.QIcon.On: {
QtGui.QIcon.Normal: (kwargs['color_on'], kwargs['on']),
QtGui.QIcon.Active: (kwargs['color_on_active'], kwargs['on_active']),
QtGui.QIcon.Disabled: (kwargs['color_on_disabled'], kwargs['on_disabled']),
QtGui.QIcon.Selected: (kwargs['color_on_selected'], kwargs['on_selected'])
},
QtGui.QIcon.Off: {
QtGui.QIcon.Normal: (kwargs['color_off'], kwargs['off']),
QtGui.QIcon.Active: (kwargs['color_off_active'], kwargs['off_active']),
QtGui.QIcon.Disabled: (kwargs['color_off_disabled'], kwargs['off_disabled']),
QtGui.QIcon.Selected: (kwargs['color_off_selected'], kwargs['off_selected'])
}
}
icon = cls(pixmap)
for state in options:
for mode in options[state]:
color, pixmap = options[state][mode]
pixmap = studioqt.Pixmap(pixmap)
pixmap.setColor(color)
icon.addPixmap(pixmap, mode, state)
return icon
def setColor(self, color, size=None):
"""
:type color: QtGui.QColor
:rtype: None
"""
icon = self
size = size or icon.actualSize(QtCore.QSize(256, 256))
pixmap = icon.pixmap(size)
if not self.isNull():
painter = QtGui.QPainter(pixmap)
painter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceIn)
painter.setBrush(color)
painter.setPen(color)
painter.drawRect(pixmap.rect())
painter.end()
icon = QtGui.QIcon(pixmap)
self.swap(icon)
if self._badgeEnabled:
self.updateBadge()
def __init__(self, *args, **kwargs):
super(Icon, self).__init__(*args, **kwargs)
self._badgeColor = None
self._badgeEnabled = False
def badgeColor(self):
return self._badgeColor
def setBadgeColor(self, color):
self._badgeColor = color
def badgeEnabled(self):
return self._badgeEnabled
def setBadgeEnabled(self, enabled):
self._badgeEnabled = enabled
def updateBadge(self):
"""
"""
color = self.badgeColor() or QtGui.QColor(240, 240, 100)
size = self.actualSize(QtCore.QSize(256, 256))
pixmap = self.pixmap(size)
painter = QtGui.QPainter(pixmap)
pen = QtGui.QPen(color)
pen.setWidth(0)
painter.setPen(pen)
painter.setBrush(color)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.drawEllipse(0, 0, size.height()/3.0, size.width()/3.0)
painter.end()
icon = QtGui.QIcon(pixmap)
self.swap(icon)

View File

@@ -0,0 +1,230 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import re
import os
from studiovendor.Qt import QtGui
from studiovendor.Qt import QtCore
__all__ = ['ImageSequence', 'ImageSequenceWidget']
class ImageSequence(QtCore.QObject):
DEFAULT_FPS = 24
frameChanged = QtCore.Signal(int)
def __init__(self, path, *args):
QtCore.QObject.__init__(self, *args)
self._timer = None
self._frame = 0
self._frames = []
self._dirname = None
self._paused = False
if path:
self.setPath(path)
def firstFrame(self):
"""
Get the path to the first frame.
:rtype: str
"""
if self._frames:
return self._frames[0]
return ""
def setPath(self, path):
"""
Set a single frame or a directory to an image sequence.
:type path: str
"""
if os.path.isfile(path):
self._frame = 0
self._frames = [path]
elif os.path.isdir(path):
self.setDirname(path)
def setDirname(self, dirname):
"""
Set the location to the image sequence.
:type dirname: str
:rtype: None
"""
def naturalSortItems(items):
"""
Sort the given list in the way that humans expect.
"""
convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
items.sort(key=alphanum_key)
self._dirname = dirname
if os.path.isdir(dirname):
self._frames = [dirname + "/" + filename for filename in os.listdir(dirname)]
naturalSortItems(self._frames)
def dirname(self):
"""
Return the location to the image sequence.
:rtype: str
"""
return self._dirname
def reset(self):
"""
Stop and reset the current frame to 0.
:rtype: None
"""
if not self._timer:
self._timer = QtCore.QTimer(self.parent())
self._timer.setSingleShot(False)
self._timer.timeout.connect(self._frameChanged)
if not self._paused:
self._frame = 0
self._timer.stop()
def pause(self):
"""
ImageSequence will enter Paused state.
:rtype: None
"""
self._paused = True
self._timer.stop()
def resume(self):
"""
ImageSequence will enter Playing state.
:rtype: None
"""
if self._paused:
self._paused = False
self._timer.start()
def stop(self):
"""
Stops the movie. ImageSequence enters NotRunning state.
:rtype: None
"""
self._timer.stop()
def start(self):
"""
Starts the movie. ImageSequence will enter Running state
:rtype: None
"""
self.reset()
if self._timer:
import studiolibrary
fps = studiolibrary.config.get("playbackFrameRate", self.DEFAULT_FPS)
self._timer.start(1000.0 / fps)
def frames(self):
"""
Return all the filenames in the image sequence.
:rtype: list[str]
"""
return self._frames
def _frameChanged(self):
"""
Triggered when the current frame changes.
:rtype: None
"""
if not self._frames:
return
frame = self._frame
frame += 1
self.jumpToFrame(frame)
def percent(self):
"""
Return the current frame position as a percentage.
:rtype: None
"""
if len(self._frames) == self._frame + 1:
_percent = 1
else:
_percent = float((len(self._frames) + self._frame)) / len(self._frames) - 1
return _percent
def frameCount(self):
"""
Return the number of frames.
:rtype: int
"""
return len(self._frames)
def currentIcon(self):
"""
Returns the current frame as a QIcon.
:rtype: QtGui.QIcon
"""
return QtGui.QIcon(self.currentFilename())
def currentPixmap(self):
"""
Return the current frame as a QPixmap.
:rtype: QtGui.QPixmap
"""
return QtGui.QPixmap(self.currentFilename())
def currentFilename(self):
"""
Return the current file name.
:rtype: str or None
"""
try:
return self._frames[self.currentFrameNumber()]
except IndexError:
pass
def currentFrameNumber(self):
"""
Return the current frame.
:rtype: int or None
"""
return self._frame
def jumpToFrame(self, frame):
"""
Set the current frame.
:rtype: int or None
"""
if frame >= self.frameCount():
frame = 0
self._frame = frame
self.frameChanged.emit(frame)

View File

@@ -0,0 +1,81 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from studiovendor import six
from studiovendor.Qt import QtWidgets
class Menu(QtWidgets.QMenu):
def __init__(self, *args):
QtWidgets.QMenu.__init__(self, *args)
def findAction(self, text):
"""
Return the action that contains the given text.
:type text: str
:rtype: QtWidgets.QAction
"""
for child in self.children():
action = None
if isinstance(child, QtWidgets.QMenu):
action = child.menuAction()
elif isinstance(child, QtWidgets.QAction):
action = child
if action and action.text().lower() == text.lower():
return action
def insertAction(self, before, *args):
"""
Add support for finding the before action by the given string.
:type before: str or QtWidget.QAction
:type args: list
:rtype: QtWidgets.QAction
"""
if isinstance(before, six.string_types):
before = self.findAction(before)
return QtWidgets.QMenu.insertAction(self, before, *args)
def insertMenu(self, before, menu):
"""
Add support for finding the before action by the given string.
:type before: str or QtWidget.QAction
:type menu: QtWidgets.QMenu
:rtype: QtWidgets.QAction
"""
if isinstance(before, six.string_types):
before = self.findAction(before)
QtWidgets.QMenu.insertMenu(self, before, menu)
def insertSeparator(self, before):
"""
Add support for finding the before action by the given string.
:type before: str or QtWidget.QAction
:rtype: QtWidgets.QAction
"""
if isinstance(before, six.string_types):
before = self.findAction(before)
return QtWidgets.QMenu.insertSeparator(self, before)

View File

@@ -0,0 +1,40 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
from studiovendor import six
from studiovendor.Qt import QtGui
import studioqt
class Pixmap(QtGui.QPixmap):
def __init__(self, *args):
QtGui.QPixmap.__init__(self, *args)
self._color = None
def setColor(self, color):
"""
:type color: QtGui.QColor
:rtype: None
"""
if isinstance(color, six.string_types):
color = studioqt.Color.fromString(color)
if not self.isNull():
painter = QtGui.QPainter(self)
painter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceIn)
painter.setBrush(color)
painter.setPen(color)
painter.drawRect(self.rect())
painter.end()

View File

@@ -0,0 +1,102 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import studioqt
class StyleSheet(object):
@classmethod
def fromPath(cls, path, **kwargs):
"""
:type path: str
:rtype: str
"""
styleSheet = cls()
data = styleSheet.read(path)
data = StyleSheet.format(data, **kwargs)
styleSheet.setData(data)
return styleSheet
@classmethod
def fromText(cls, text, options=None):
"""
:type text: str
:rtype: str
"""
styleSheet = cls()
data = StyleSheet.format(text, options=options)
styleSheet.setData(data)
return styleSheet
def __init__(self):
self._data = ""
def setData(self, data):
"""
:type data: str
"""
self._data = data
def data(self):
"""
:rtype: str
"""
return self._data
@staticmethod
def read(path):
"""
:type path: str
:rtype: str
"""
data = ""
if os.path.isfile(path):
with open(path, "r") as f:
data = f.read()
return data
@staticmethod
def format(data=None, options=None, dpi=1):
"""
:type data:
:type options: dict
:rtype: str
"""
if options is not None:
keys = list(options.keys())
keys.sort(key=len, reverse=True)
for key in keys:
data = data.replace(key, options[key])
reDpi = re.compile("[0-9]+px")
newData = []
for line in data.split("\n"):
dpi_ = reDpi.search(line)
if dpi_:
new = dpi_.group().replace("px", "")
val = int(new)
if val > 0:
val = int(val * dpi)
line = line.replace(dpi_.group(), str(val) + "px")
newData.append(line)
data = "\n".join(newData)
return data

View File

@@ -0,0 +1,230 @@
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import inspect
import logging
import contextlib
from studiovendor.Qt import QtGui
from studiovendor.Qt import QtCore
from studiovendor.Qt import QtCompat
from studiovendor.Qt import QtWidgets
import studioqt
__all__ = [
"app",
"fadeIn",
"fadeOut",
"loadUi",
"installFonts",
"isAltModifier",
"isShiftModifier",
"isControlModifier",
]
logger = logging.getLogger(__name__)
@contextlib.contextmanager
def app():
"""
.. code-block:: python
import studioqt
with studioqt.app():
widget = QWidget(None)
widget.show()
:rtype: None
"""
app_ = None
isAppRunning = bool(QtWidgets.QApplication.instance())
if not isAppRunning:
app_ = QtWidgets.QApplication(sys.argv)
yield None
if not isAppRunning:
sys.exit(app_.exec_())
def uiPath(cls):
"""
Return the ui path for the given widget class.
:type cls: type
:rtype: str
"""
name = cls.__name__
path = inspect.getfile(cls)
dirname = os.path.dirname(path)
path = dirname + "/resource/ui/" + name + ".ui"
if not os.path.exists(path):
path = dirname + "/ui/" + name + ".ui"
if not os.path.exists(path):
path = dirname + "/" + name + ".ui"
return path
def loadUi(widget, path=None, cls=None):
"""
.. code-block:: python
import studioqt
class Widget(QtWidgets.QWidget):
def __init__(self)
super(Widget, self).__init__()
studioqt.loadUi(self)
with studioqt.app():
widget = Widget()
widget.show()
:type widget: QWidget or QDialog
:type path: str
:type cls: object
:rtype: None
"""
if cls:
path = uiPath(cls)
elif not path:
path = uiPath(widget.__class__)
cwd = os.getcwd()
try:
os.chdir(os.path.dirname(path))
widget.ui = QtCompat.loadUi(path, widget)
finally:
os.chdir(cwd)
def isModifier():
"""
Return True if either the alt key or control key is down.
:rtype: bool
"""
return isAltModifier() or isControlModifier()
def isAltModifier():
"""
Return True if the alt key is down.
:rtype: bool
"""
modifiers = QtWidgets.QApplication.keyboardModifiers()
return modifiers == QtCore.Qt.AltModifier
def isControlModifier():
"""
Return True if the control key is down.
:rtype: bool
"""
modifiers = QtWidgets.QApplication.keyboardModifiers()
return modifiers == QtCore.Qt.ControlModifier
def isShiftModifier():
"""
Return True if the shift key is down.
:rtype: bool
"""
modifiers = QtWidgets.QApplication.keyboardModifiers()
return modifiers == QtCore.Qt.ShiftModifier
def fadeIn(widget, duration=200, onFinished=None):
"""
Fade in the given widget using the opacity effect.
:type widget: QtWidget.QWidgets
:type duration: int
:type onFinished: func
:rtype: QtCore.QPropertyAnimation
"""
widget._fadeInEffect_ = QtWidgets.QGraphicsOpacityEffect()
widget.setGraphicsEffect(widget._fadeInEffect_)
animation = QtCore.QPropertyAnimation(widget._fadeInEffect_, b"opacity")
animation.setDuration(duration)
animation.setStartValue(0.0)
animation.setEndValue(1.0)
animation.setEasingCurve(QtCore.QEasingCurve.InOutCubic)
animation.start()
if onFinished:
animation.finished.connect(onFinished)
widget._fadeIn_ = animation
return animation
def fadeOut(widget, duration=200, onFinished=None):
"""
Fade out the given widget using the opacity effect.
:type widget: QtWidget.QWidgets
:type duration: int
:type onFinished: func
:rtype: QtCore.QPropertyAnimation
"""
widget._fadeOutEffect_ = QtWidgets.QGraphicsOpacityEffect()
widget.setGraphicsEffect(widget._fadeOutEffect_)
animation = QtCore.QPropertyAnimation(widget._fadeOutEffect_, b"opacity")
animation.setDuration(duration)
animation.setStartValue(1.0)
animation.setEndValue(0.0)
animation.setEasingCurve(QtCore.QEasingCurve.InOutCubic)
animation.start()
if onFinished:
animation.finished.connect(onFinished)
widget._fadeOut_ = animation
return animation
def installFonts(path):
"""
Install all the fonts in the given directory path.
:type path: str
"""
path = os.path.abspath(path)
fontDatabase = QtGui.QFontDatabase()
for filename in os.listdir(path):
if filename.endswith(".ttf"):
filename = os.path.join(path, filename)
result = fontDatabase.addApplicationFont(filename)
if result > 0:
logger.debug("Added font %s", filename)
else:
logger.debug("Cannot add font %s", filename)