# -*- coding: utf-8 -*-
#
# Copyright © 2009-2010 CEA
# Pierre Raybaut
# Licensed under the terms of the CECILL License
# (see guidata/__init__.py for details)
"""
configtools
-----------
The ``guidata.configtools`` module provides configuration related tools.
"""
import gettext
import os
import os.path as osp
import sys
from guidata.utils import decode_fs_string, get_module_path
IMG_PATH = []
[docs]def get_module_data_path(modname, relpath=None):
"""Return module *modname* data path
Handles py2exe/cx_Freeze distributions"""
datapath = getattr(sys.modules[modname], "DATAPATH", "")
if not datapath:
datapath = get_module_path(modname)
parentdir = osp.normpath(osp.join(datapath, osp.pardir))
if osp.isfile(parentdir):
# Parent directory is not a directory but the 'library.zip' file:
# this is either a py2exe or a cx_Freeze distribution
datapath = osp.abspath(osp.join(osp.join(parentdir, osp.pardir), modname))
if relpath is not None:
datapath = osp.abspath(osp.join(datapath, relpath))
return datapath
[docs]def get_translation(modname, dirname=None):
"""Return translation callback for module *modname*"""
if dirname is None:
dirname = modname
# fixup environment var LANG in case it's unknown
if "LANG" not in os.environ:
import locale # Warning: 2to3 false alarm ('import' fixer)
lang = locale.getdefaultlocale()[0]
if lang is not None:
os.environ["LANG"] = lang
try:
modlocpath = get_module_locale_path(dirname)
try:
_trans = gettext.translation(modname, modlocpath, codeset="utf-8")
except TypeError:
# Python >= 3.11 (codeset argument has been removed)
_trans = gettext.translation(modname, modlocpath)
lgettext = _trans.gettext
def translate_gettext(x):
y = lgettext(x)
if isinstance(y, str):
return y
else:
return str(y, "utf-8")
return translate_gettext
except IOError as _e:
# print "Not using translations (%s)" % _e
def translate_dumb(x):
if not isinstance(x, str):
return str(x, "utf-8")
return x
return translate_dumb
[docs]def get_module_locale_path(modname):
"""Return module *modname* gettext translation path"""
localepath = getattr(sys.modules[modname], "LOCALEPATH", "")
if not localepath:
localepath = get_module_data_path(modname, relpath="locale")
return localepath
[docs]def add_image_path(path, subfolders=True):
"""Append image path (opt. with its subfolders) to global list IMG_PATH"""
if not isinstance(path, str):
path = decode_fs_string(path)
global IMG_PATH
IMG_PATH.append(path)
if subfolders:
for fileobj in os.listdir(path):
pth = osp.join(path, fileobj)
if osp.isdir(pth):
IMG_PATH.append(pth)
[docs]def add_image_module_path(modname, relpath, subfolders=True):
"""
Appends image data path relative to a module name.
Used to add module local data that resides in a module directory
but will be shipped under sys.prefix / share/ ...
modname must be the name of an already imported module as found in
sys.modules
"""
add_image_path(get_module_data_path(modname, relpath=relpath), subfolders)
[docs]def get_image_file_path(name, default="not_found.png"):
"""
Return the absolute path to image with specified name
name, default: filenames with extensions
"""
for pth in IMG_PATH:
full_path = osp.join(pth, name)
if osp.isfile(full_path):
return osp.abspath(full_path)
if default is not None:
try:
return get_image_file_path(default, None)
except RuntimeError:
raise RuntimeError("Image file %r not found" % name)
else:
raise RuntimeError()
ICON_CACHE = {}
[docs]def get_icon(name, default="not_found.png"):
"""
Construct a QIcon from the file with specified name
name, default: filenames with extensions
"""
try:
return ICON_CACHE[name]
except KeyError:
from qtpy import QtGui as QG
icon = QG.QIcon(get_image_file_path(name, default))
ICON_CACHE[name] = icon
return icon
[docs]def get_image_label(name, default="not_found.png"):
"""
Construct a QLabel from the file with specified name
name, default: filenames with extensions
"""
from qtpy import QtGui as QG
from qtpy import QtWidgets as QW
label = QW.QLabel()
pixmap = QG.QPixmap(get_image_file_path(name, default))
label.setPixmap(pixmap)
return label
[docs]def get_image_layout(imagename, text="", tooltip="", alignment=None):
"""
Construct a QHBoxLayout including image from the file with specified name,
left-aligned text [with specified tooltip]
Return (layout, label)
"""
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
if alignment is None:
alignment = QC.Qt.AlignLeft
layout = QW.QHBoxLayout()
if alignment in (QC.Qt.AlignCenter, QC.Qt.AlignRight):
layout.addStretch()
layout.addWidget(get_image_label(imagename))
label = QW.QLabel(text)
label.setToolTip(tooltip)
layout.addWidget(label)
if alignment in (QC.Qt.AlignCenter, QC.Qt.AlignLeft):
layout.addStretch()
return (layout, label)
[docs]def font_is_installed(font):
"""Check if font is installed"""
from qtpy import QtGui as QG
return [fam for fam in QG.QFontDatabase().families() if str(fam) == font]
MONOSPACE = [
"Cascadia Code PL",
"Cascadia Mono PL",
"Cascadia Code",
"Cascadia Mono",
"Consolas",
"Courier New",
"Bitstream Vera Sans Mono",
"Andale Mono",
"Liberation Mono",
"Monaco",
"Courier",
"monospace",
"Fixed",
"Terminal",
]
[docs]def get_family(families):
"""Return the first installed font family in family list"""
if not isinstance(families, list):
families = [families]
for family in families:
if font_is_installed(family):
return family
else:
print("Warning: None of the following fonts is installed: %r" % families)
return ""
[docs]def get_font(conf, section, option=""):
"""
Construct a QFont from the specified configuration file entry
conf: UserConfig instance
section [, option]: configuration entry
"""
from qtpy import QtGui as QG
if not option:
option = "font"
if "font" not in option:
option += "/font"
font = QG.QFont()
if conf.has_option(section, option + "/family/nt"):
families = conf.get(section, option + "/family/" + os.name)
elif conf.has_option(section, option + "/family"):
families = conf.get(section, option + "/family")
else:
families = None
if families is not None:
if not isinstance(families, list):
families = [families]
family = None
for family in families:
if font_is_installed(family):
break
font.setFamily(family)
if conf.has_option(section, option + "/size"):
font.setPointSize(conf.get(section, option + "/size"))
if conf.get(section, option + "/bold", False):
font.setWeight(QG.QFont.Bold)
else:
font.setWeight(QG.QFont.Normal)
return font
[docs]def get_pen(conf, section, option="", color="black", width=1, style="SolidLine"):
"""
Construct a QPen from the specified configuration file entry
conf: UserConfig instance
section [, option]: configuration entry
[color]: default color
[width]: default width
[style]: default style
"""
from qtpy import QtCore as QC
from qtpy import QtGui as QG
if "pen" not in option:
option += "/pen"
color = conf.get(section, option + "/color", color)
color = QG.QColor(color)
width = conf.get(section, option + "/width", width)
style_name = conf.get(section, option + "/style", style)
style = getattr(QC.Qt, style_name)
return QG.QPen(color, width, style)
[docs]def get_brush(conf, section, option="", color="black", alpha=1.0):
"""
Construct a QBrush from the specified configuration file entry
conf: UserConfig instance
section [, option]: configuration entry
[color]: default color
[alpha]: default alpha-channel
"""
from qtpy import QtGui as QG
if "brush" not in option:
option += "/brush"
color = conf.get(section, option + "/color", color)
color = QG.QColor(color)
alpha = conf.get(section, option + "/alphaF", alpha)
color.setAlphaF(alpha)
return QG.QBrush(color)