WSJT-X/qdarkstyle/__init__.py

457 lines
13 KiB
Python
Raw Normal View History

2018-02-07 13:34:34 -05:00
#!/usr/bin/env python
2014-01-02 09:57:14 -05:00
# -*- coding: utf-8 -*-
2018-02-07 13:34:34 -05:00
"""QDarkStyle is a dark stylesheet for Python and Qt applications.
This module provides a function to transparently load the stylesheets
with the correct rc file.
2018-02-07 13:34:34 -05:00
First, start importing our module
.. code-block:: python
import qdarkstyle
Then you can get stylesheet provided by QDarkStyle for various Qt wrappers
as shown bellow
.. code-block:: python
# PySide
dark_stylesheet = qdarkstyle.load_stylesheet_pyside()
# PySide 2
dark_stylesheet = qdarkstyle.load_stylesheet_pyside2()
2018-02-07 13:34:34 -05:00
# PyQt4
dark_stylesheet = qdarkstyle.load_stylesheet_pyqt()
# PyQt5
dark_stylesheet = qdarkstyle.load_stylesheet_pyqt5()
Or from environment variables provided for QtPy or PyQtGraph, see
.. code-block:: python
# QtPy
dark_stylesheet = qdarkstyle.load_stylesheet_from_environment()
# PyQtGraph
dark_stylesheet = qdarkstyle.load_stylesheet_from_environment(is_pyqtgraph)
Finally, set your QApplication with it
.. code-block:: python
app.setStyleSheet(dark_stylesheet)
Enjoy!
"""
2018-02-07 13:34:34 -05:00
2018-10-28 13:24:26 -04:00
importlib_error = ""
try:
import importlib
except ImportError:
importlib_error = "Not available in Python 2.7"
2014-05-16 17:34:59 -04:00
import logging
import os
2018-10-25 15:44:08 -04:00
import platform
import sys
import warnings
__version__ = "2.6.1"
QT_BINDINGS = ['PyQt4', 'PyQt5', 'PySide', 'PySide2']
"""list: values of all Qt bindings to import."""
2018-10-28 13:24:26 -04:00
QT_ABSTRACTIONS = ['qtpy', 'pyqtgraph', 'Qt']
"""list: values of all Qt abstraction layers to import."""
QT4_IMPORT_API = ['QtCore', 'QtGui']
"""list: which subpackage to import for Qt4 API."""
QT5_IMPORT_API = ['QtCore', 'QtGui', 'QtWidgets']
"""list: which subpackage to import for Qt5 API."""
QT_API_VALUES = ['pyqt', 'pyqt5', 'pyside', 'pyside2']
"""list: values for QT_API environment variable used by QtPy."""
QT_LIB_VALUES = ['PyQt', 'PyQt5', 'PySide', 'PySide2']
"""list: values for PYQTGRAPH_QT_LIB environment variable used by PyQtGraph."""
QT_BINDING = 'Not set or nonexistent'
"""str: Qt binding in use."""
QT_ABSTRACTION = 'Not set or nonexistent'
"""str: Qt abstraction layer in use."""
2014-05-16 17:34:59 -04:00
def _logger():
return logging.getLogger('qdarkstyle')
def _qt_wrapper_import(qt_api):
"""
Check if Qt API defined can be imported.
:param qt_api: Qt API string to test import
:return load function fot given qt_api, otherwise empty string
"""
qt_wrapper = ''
loader = ""
try:
if qt_api == 'PyQt' or qt_api == 'pyqt':
import PyQt4
qt_wrapper = 'PyQt4'
loader = load_stylesheet_pyqt()
elif qt_api == 'PyQt5' or qt_api == 'pyqt5':
import PyQt5
qt_wrapper = 'PyQt5'
loader = load_stylesheet_pyqt5()
elif qt_api == 'PySide' or qt_api == 'pyside':
import PySide
qt_wrapper = 'PySide'
loader = load_stylesheet_pyside()
elif qt_api == 'PySide2' or qt_api == 'pyside2':
import PySide2
qt_wrapper = 'PySide2'
loader = load_stylesheet_pyside2()
except ImportError as err:
2018-02-21 14:54:40 -05:00
_logger().error("Impossible import Qt wrapper.\n %s", str(err))
else:
2018-02-21 14:54:40 -05:00
_logger().info("Using Qt wrapper = %s ", qt_wrapper)
QT_BINDING = qt_wrapper
finally:
return loader
def load_stylesheet_from_environment(is_pyqtgraph=False):
"""
Load the stylesheet from QT_API (or PYQTGRAPH_QT_LIB) environment variable.
:param is_pyqtgraph: True if it is to be set using PYQTGRAPH_QT_LIB
:raise KeyError: if QT_API/PYQTGRAPH_QT_LIB does not exist
:return the stylesheet string
"""
warnings.warn(
"load_stylesheet_from_environment() will be deprecated in version 3,"
"use load_stylesheet()",
PendingDeprecationWarning
)
qt_api = ''
pyqtgraph_qt_lib = ''
loader = ""
# Get values from QT_API
try:
qt_api = os.environ['QT_API']
except KeyError as err:
# Log this error just if using QT_API
if not is_pyqtgraph:
_logger().error("QT_API does not exist, do os.environ['QT_API']= "
2018-02-21 14:54:40 -05:00
"and choose one option from %s", QT_API_VALUES)
else:
if not is_pyqtgraph:
if qt_api in QT_API_VALUES:
QT_ABSTRACTION = "qtpy"
2018-02-21 14:54:40 -05:00
_logger().info("Found QT_API='%s'", qt_api)
loader = _qt_wrapper_import(qt_api)
else:
# Raise this error because the function need this key/value
raise KeyError("QT_API=%s is unknown, please use a value "
2018-02-21 14:54:40 -05:00
"from %s",
(qt_api, QT_API_VALUES))
# Get values from PYQTGRAPH_QT_LIB
try:
pyqtgraph_qt_lib = os.environ['PYQTGRAPH_QT_LIB']
except KeyError as err:
# Log this error just if using PYQTGRAPH_QT_LIB
if is_pyqtgraph:
_logger().error("PYQTGRAP_QT_API does not exist, do "
"os.environ['PYQTGRAPH_QT_LIB']= "
2018-02-21 14:54:40 -05:00
"and choose one option from %s",
QT_LIB_VALUES)
else:
if is_pyqtgraph:
if pyqtgraph_qt_lib in QT_LIB_VALUES:
QT_ABSTRACTION = "pyqtgraph"
2018-02-21 14:54:40 -05:00
_logger().info("Found PYQTGRAPH_QT_LIB='%s'", pyqtgraph_qt_lib)
loader = _qt_wrapper_import(pyqtgraph_qt_lib)
else:
# Raise this error because the function need this key/value
raise KeyError("PYQTGRAPH_QT_LIB=%s is unknown, please use a "
2018-02-21 14:54:40 -05:00
"value from %s", (
pyqtgraph_qt_lib,
QT_LIB_VALUES))
# Just a warning if both are set but differs each other
if qt_api and pyqtgraph_qt_lib:
if qt_api != pyqtgraph_qt_lib.lower():
_logger().warning("Both QT_API=%s and PYQTGRAPH_QT_LIB=%s are set, "
"but with different values, this could cause "
2018-02-21 14:54:40 -05:00
"some issues if using them in the same project!",
qt_api, pyqtgraph_qt_lib)
return loader
def load_stylesheet(pyside=True):
"""
Load the stylesheet. Takes care of importing the rc module.
:param pyside: True to load the pyside rc file, False to load the PyQt rc file
:return the stylesheet string
"""
warnings.warn(
"load_stylesheet() will not receive pyside parameter in version 3. "
"Set QtPy environment variable to specify the Qt binding insteady.",
FutureWarning
)
# Smart import of the rc file
2018-08-15 19:36:06 -04:00
pyside_ver = None
if pyside:
2018-08-15 19:36:06 -04:00
# Detect the PySide version available
try:
import PySide
except ModuleNotFoundError:
import PySide2
pyside_ver = 2
else:
pyside_ver = 1
if pyside_ver == 1:
import qdarkstyle.pyside_style_rc
else:
import qdarkstyle.pyside2_style_rc
else:
2014-01-02 10:06:01 -05:00
import qdarkstyle.pyqt_style_rc
# Load the stylesheet content from resources
if not pyside:
from PyQt4.QtCore import QFile, QTextStream
else:
2018-08-15 19:36:06 -04:00
if pyside_ver == 1:
from PySide.QtCore import QFile, QTextStream
else:
from PySide2.QtCore import QFile, QTextStream
f = QFile(":qdarkstyle/style.qss")
if not f.exists():
2014-05-16 17:34:59 -04:00
_logger().error("Unable to load stylesheet, file not found in "
"resources")
return ""
else:
f.open(QFile.ReadOnly | QFile.Text)
ts = QTextStream(f)
2014-05-16 17:29:53 -04:00
stylesheet = ts.readAll()
if platform.system().lower() == 'darwin': # see issue #12 on github
mac_fix = '''
QDockWidget::title
{
background-color: #31363b;
text-align: center;
height: 12px;
2014-05-16 17:29:53 -04:00
}
'''
stylesheet += mac_fix
return stylesheet
def load_stylesheet_pyside():
"""
Load the stylesheet for use in a pyside application.
:return the stylesheet string
"""
warnings.warn(
"load_stylesheet_pyside() will be deprecated in version 3,"
"set QtPy environment variable to specify the Qt binding and "
"use load_stylesheet()",
PendingDeprecationWarning
)
return load_stylesheet(pyside=True)
def load_stylesheet_pyside2():
"""
Load the stylesheet for use in a pyside2 application.
:raise NotImplementedError: Because it is not supported yet
"""
warnings.warn(
"load_stylesheet_pyside2() will be deprecated in version 3,"
"set QtPy environment variable to specify the Qt binding and "
"use load_stylesheet()",
PendingDeprecationWarning
)
2018-08-15 19:36:06 -04:00
return load_stylesheet(pyside=True)
def load_stylesheet_pyqt():
"""
Load the stylesheet for use in a pyqt4 application.
:return the stylesheet string
"""
warnings.warn(
"load_stylesheet_pyqt() will be deprecated in version 3,"
"set QtPy environment variable to specify the Qt binding and "
"use load_stylesheet()",
PendingDeprecationWarning
)
return load_stylesheet(pyside=False)
def load_stylesheet_pyqt5():
"""
Load the stylesheet for use in a pyqt5 application.
:param pyside: True to load the pyside rc file, False to load the PyQt rc file
:return the stylesheet string
"""
warnings.warn(
"load_stylesheet_pyqt5() will be deprecated in version 3,"
"set QtPy environment variable to specify the Qt binding and "
"use load_stylesheet()",
PendingDeprecationWarning
)
# Smart import of the rc file
import qdarkstyle.pyqt5_style_rc
# Load the stylesheet content from resources
from PyQt5.QtCore import QFile, QTextStream
f = QFile(":qdarkstyle/style.qss")
if not f.exists():
_logger().error("Unable to load stylesheet, file not found in "
"resources")
return ""
else:
f.open(QFile.ReadOnly | QFile.Text)
ts = QTextStream(f)
stylesheet = ts.readAll()
if platform.system().lower() == 'darwin': # see issue #12 on github
mac_fix = '''
QDockWidget::title
{
background-color: #31363b;
2014-05-29 18:02:55 -04:00
text-align: center;
height: 12px;
}
'''
stylesheet += mac_fix
2015-04-06 05:37:11 -04:00
return stylesheet
2018-10-25 15:44:08 -04:00
def information():
"""Get system and runtime information."""
info = []
qt_api = ''
qt_lib = ''
2018-10-25 15:44:08 -04:00
qt_bin = ''
try:
qt_api = os.environ['QT_API']
except KeyError:
2018-10-25 15:44:08 -04:00
qt_api = 'Not set or nonexistent'
try:
from Qt import __binding__
2018-10-28 13:24:26 -04:00
except Exception:
# It should be (KeyError, ModuleNotFoundError, ImportError)
# but each python version have a different one, and not define others
2018-10-25 15:44:08 -04:00
qt_lib = 'Not set or nonexistent'
2018-10-28 13:24:26 -04:00
else:
qt_lib = __binding__
try:
2018-10-25 15:44:08 -04:00
qt_bin = os.environ['PYQTGRAPH_QT_LIB']
except KeyError:
2018-10-25 15:44:08 -04:00
qt_bin = 'Not set or nonexistent'
info.append('QDarkStyle: %s' % __version__)
2018-10-25 15:44:08 -04:00
info.append('OS: %s %s %s' % (platform.system(), platform.release(), platform.machine()))
info.append('Platform: %s' % sys.platform)
info.append('Python: %s' % '.'.join(str(e) for e in sys.version_info[:]))
info.append('Python API: %s' % sys.api_version)
2018-10-25 15:44:08 -04:00
info.append('Binding in use: %s' % QT_BINDING)
info.append('Abstraction in use: %s' % QT_ABSTRACTION)
2018-10-25 15:44:08 -04:00
info.append('qtpy (QT_API): %s' % qt_api)
info.append('pyqtgraph (PYQTGRAPH_QT_LIB): %s' % qt_lib)
info.append('Qt.py (__binding__): %s' % qt_bin)
return info
def qt_bindings():
"""Return a list of qt bindings available."""
2018-10-28 13:24:26 -04:00
if importlib_error:
print(importlib_error)
return []
bindings = QT_BINDINGS
for binding in bindings:
# check import
spec = importlib.util.find_spec(binding)
if spec is None:
# remove if not available
bindings.remove(binding)
return bindings
def qt_abstractions():
"""Return a list of qt abstraction layers available."""
2018-10-28 13:24:26 -04:00
if importlib_error:
print(importlib_error)
return []
abstractions = QT_ABSTRACTIONS
for abstraction in abstractions:
# check import
spec = importlib.util.find_spec(abstraction)
if spec is None:
# remove if not available
abstractions.remove(abstraction)
return abstractions
def import_qt_modules_from(use_binding='pyqt5', use_abstraction='qtpy'):
2018-10-25 15:44:08 -04:00
"""New approach to import modules using importlib."""
2018-10-28 13:24:26 -04:00
if importlib_error:
print(importlib_error)
return []
spec_binding = importlib.util.find_spec(use_binding)
spec_abstraction = importlib.util.find_spec(use_abstraction)
if spec_binding is None:
print("Cannot find Qt binding: ", use_binding)
else:
2018-10-25 15:44:08 -04:00
module = importlib.util.module_from_spec(spec_binding)
spec.loader.exec_module(module)
# Adding the module to sys.modules is optional.
sys.modules[name] = module
if spec_abstraction is None:
print("Cannot find Qt abstraction layer: ", use_abstraction)
else:
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Adding the module to sys.modules is optional.
2018-10-25 15:44:08 -04:00
sys.modules[name] = module