コード例 #1
0
    def set_font(self, font: QFont = None):
        qfd = QFontDatabase()

        if font:
            info = QFontInfo(font)
            if info.styleHint() != QFont.Monospace:
                self.logger.warning("font: Please use monospaced font! "
                                    f"Unsupported font {info.family}.")
                font = qfd.systemFont(QFontDatabase.FixedFont)
        elif "Menlo" in qfd.families():
            font = QFont("Menlo")
            info = QFontInfo(font)
        else:
            font = qfd.systemFont(QFontDatabase.FixedFont)
            info = QFontInfo(font)

        font.setPointSize(12)
        self.font = font
        metrics = QFontMetrics(font)
        self.char_width = metrics.horizontalAdvance("A")
        self.char_height = metrics.height()
        self.line_height = int(self.char_height * 1.2)

        self.logger.info(f"font: Font {info.family()} selected, character "
                         f"size {self.char_width}x{self.char_height}.")

        self.row_len = int(self._width / self.char_width)
        self.col_len = int(self._height / self.line_height)
コード例 #2
0
def get_fonts(fdb: QFontDatabase):
    fonts = fdb.families()
    fonts_final = []
    for font in fonts:
        try:
            ImageFont.truetype('c:/windows/fonts/{}.ttf'.format(font))
            fonts_final.append(font)
        except OSError:
            ...

    return fonts_final
コード例 #3
0
ファイル: label2d.py プロジェクト: Yongcheng123/ChimeraX
def label_listfonts(session):
    '''Report available fonts.'''
    has_graphics = session.main_view.render is not None
    if not has_graphics:
        from chimerax.core.errors import LimitationError
        raise LimitationError(
            "Unable to do list fonts without being able to render images")
    from PyQt5.QtGui import QFontDatabase
    fdb = QFontDatabase()
    fnames = list(fdb.families())
    fnames.sort()
    session.logger.info('%d fonts available:\n%s' %
                        (len(fnames), '\n'.join(fnames)))
コード例 #4
0
ファイル: QT1.py プロジェクト: kudos09/python_study
 def __init__(self, parent=None):
     super(WaterQualityView, self).__init__(parent)
     self.scrollarea = None
     self.model = None
     self.setFocusPolicy(Qt.StrongFocus)
     self.selectedRow = -1
     self.flowfont = self.font()
     size = self.font().pointSize()
     if platform.system() == "Windows":
         fontDb = QFontDatabase()
         for face in [face.lower() for face in fontDb.families()]:
             if face.find("unicode"):
                 self.flowfont = QFont(face, size)
                 break
         else:
             self.flowfont = QFont("symbol", size)
             WaterQualityView.FLOWCHARS = (chr(0xAC), chr(0xAE), chr(0xDE))
コード例 #5
0
 def printFonts(self, filter_str):
     result = []
     
     db = QFontDatabase()
     fonts = QFontDatabase.families(db)
     
     for font in fonts:
         print(font, '|', filter_str)
         
         if filter_str == None:
             print("A")
             result.append(str(font))
         
         elif filter_str in font:
             print("B")
             result.append(font)
         
     return result
コード例 #6
0
ファイル: lexer.py プロジェクト: derula/strainer
 def __init__(self, parent):
     super().__init__(parent)
     # QScintilla only uses the primary font family rather than searching through alternatives.
     # So we need to do this manually...
     db = QFontDatabase()
     fonts = set(db.families(QFontDatabase.WritingSystem.Latin))
     for font_name in self._FONTS:
         if font_name in fonts:
             font = QFont(font_name, 10)
             break
     else:
         font = db.systemFont(QFontDatabase.FixedFont)
     self.setDefaultFont(font)
     for style, (color, font_kwargs) in self._STYLE_SETTINGS.items():
         self.setColor(QColor(color), style)
         self.setFont(QFont(font.family(), font.pointSize(), **font_kwargs),
                      style)
     self.getStyling = partial(parent.SendScintilla,
                               QsciScintilla.SCI_GETSTYLEAT)
コード例 #7
0
ファイル: fontscombobox.py プロジェクト: iwandi/krita
    def __init__(self, editor, parent=None):
        super(FontsComboBox, self).__init__(parent)

        self.editor = editor

        _fontDataBase = QFontDatabase()

        self.addItems(_fontDataBase.families())
        self.setCurrentIndex(self.findText(self.editor.font))

        com = QCompleter()
        com.setCaseSensitivity(Qt.CaseInsensitive)
        com.setCompletionMode(QCompleter.PopupCompletion)

        # Style sheet to set false on combobox-popup
        self.setStyleSheet("QComboBox { combobox-popup: 0; }")
        self.setMaxVisibleItems(10)
        self.setCompleter(com)
        self.currentIndexChanged.connect(self._currentIndexChanged)
コード例 #8
0
 def loadFonts(self):
     database = QFontDatabase()
     for family in database.families():
         self.fontBox.insertItem(999999, family)
コード例 #9
0
class FontAssets(GWAssets):
    """
    A utility for discovering and loading any font assets associated with the application.

    font_specs = FontAssets("C:/app/assets/fonts")
    """
    def __init__(self, asset_path: Union[Path, str]) -> None:
        super().__init__(asset_path)

        self.gui_font_db = QFontDatabase()
        self.load_new_fonts()
        self.gui_font = qApp.font()
        self.gui_font_fixed = QFontDatabase.systemFont(QFontDatabase.FixedFont)

    def apply_theme(self):
        # No themes for fonts
        pass

    def font_asset_list(self):
        """Scan the font assets folder for all the subfolders (each assumed to be a font family)."""
        if self.available_font_families:
            return self.available_font_families
        for child in self.asset_path.iterdir():
            if child.is_dir():
                self.available_font_families.append(child.name)
        return self.available_font_families

    def load_new_fonts(self):
        """Load any fonts that the system doesn't already have installed."""
        new_fonts = []
        for family in self.font_asset_list():
            font_dir: Path = self.asset_path / family
            if font_dir.is_dir:
                if family not in self.gui_font_db.families():
                    for child in font_dir.iterdir():
                        if child.is_file() and child.suffix in [
                                ".ttf", ".otf"
                        ]:
                            new_fonts.append(child)
        new_font: Path
        for new_font in new_fonts:
            LOG.diagnostic(
                f"Loading font asset: {new_font.relative_to(self.asset_path)}")
            id = self.gui_font_db.addApplicationFont(new_font)
            if id < 0:
                LOG.error(
                    f"Failed to add font: {new_font.relative_to(self.asset_path)}"
                )
        return

    def basic_font_metrics(self, font: QFont) -> BasicFontMetrics:
        """
        Calculates some commonly needed font metrics and returns them in a
        BasicFontMetrics tuple.
        """
        qMetric = QFontMetrics(font)
        info = BasicFontMetrics.make([
            font.pointSizeF(),
            qMetric.height(),
            qMetric.ascent(),
            qMetric.boundingRect("N").height(),
            qMetric.boundingRect("N").width(),
        ])

        LOG.diagnostic("GUI Font Family: %s" % font.family())
        LOG.diagnostic("GUI Font Point Size: %.2f" % info.points)
        LOG.diagnostic("GUI Font Pixel Size: %d" % info.pixels)
        LOG.diagnostic("GUI Base Icon Size: %d" % info.icon_size)
        LOG.diagnostic("Text 'N' Height: %d" % info.n_height)
        LOG.diagnostic("Text 'N' Width: %d" % info.n_width)
        return info

    def get_text_width(self, font: QFont, proposed_text):
        """Calculates the width needed to contain a given piece of text."""
        return int(ceil(
            QFontMetrics(font).boundingRect(proposed_text).width()))
コード例 #10
0
ファイル: p07_QFont.py プロジェクト: tanmeihua-tmh/ClassCodes
# coding = utf-8
import sys

from PyQt5.QtGui import QFont, QFontDatabase, QPalette, QColor
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton

app = QApplication(sys.argv)
widget = QWidget()
widget.setWindowTitle('调色板与字体')
widget.setGeometry(300, 300, 400, 300)
db = QFontDatabase()
# fonts = db.families(QFontDatabase.SimplifiedChinese)
fonts = db.families(QFontDatabase.Any)
for f in fonts:
    print(f)
font = QFont('Weibei SC', 30)
widget.setFont(font)

button1 = QPushButton(parent=widget, text='窗体字体')
button1.setGeometry(10, 10, 200, 80)
button1.setFont(font)

pal = QPalette()
pal.setColor(QPalette.Background, QColor(255, 0, 0))
# QPalette.ToolTipText作用于QTooltip,而不是窗体的tooltip属性
# pal.setColor(QPalette.ToolTipText,  QColor(255, 0, 255))
widget.setPalette(pal)
# 提示信息的颜色可以使用样式表(很多文本类的属性都可以)
widget.setToolTip('<font color=red>提示颜色</font>')

widget.show()
コード例 #11
0
ファイル: theme.py プロジェクト: triptych/novelWriter
class GuiTheme:
    def __init__(self, theParent):

        self.mainConf = nw.CONFIG
        self.theParent = theParent
        self.theIcons = GuiIcons(self.theParent)
        self.guiPalette = QPalette()
        self.guiPath = "gui"
        self.fontPath = "fonts"
        self.syntaxPath = "syntax"
        self.cssName = "style.qss"
        self.confName = "theme.conf"
        self.themeList = []
        self.syntaxList = []

        # Loaded Theme Settings

        ## Theme
        self.themeName = ""
        self.themeDescription = ""
        self.themeAuthor = ""
        self.themeCredit = ""
        self.themeUrl = ""
        self.themeLicense = ""
        self.themeLicenseUrl = ""

        ## GUI
        self.statNone = [120, 120, 120]
        self.statUnsaved = [200, 15, 39]
        self.statSaved = [2, 133, 37]
        self.helpText = [0, 0, 0]

        # Loaded Syntax Settings

        ## Main
        self.syntaxName = ""
        self.syntaxDescription = ""
        self.syntaxAuthor = ""
        self.syntaxCredit = ""
        self.syntaxUrl = ""
        self.syntaxLicense = ""
        self.syntaxLicenseUrl = ""

        ## Colours
        self.colBack = [255, 255, 255]
        self.colText = [0, 0, 0]
        self.colLink = [0, 0, 0]
        self.colHead = [0, 0, 0]
        self.colHeadH = [0, 0, 0]
        self.colEmph = [0, 0, 0]
        self.colDialN = [0, 0, 0]
        self.colDialD = [0, 0, 0]
        self.colDialS = [0, 0, 0]
        self.colHidden = [0, 0, 0]
        self.colKey = [0, 0, 0]
        self.colVal = [0, 0, 0]
        self.colSpell = [0, 0, 0]
        self.colTagErr = [0, 0, 0]
        self.colRepTag = [0, 0, 0]
        self.colMod = [0, 0, 0]

        # Changeable Settings
        self.guiTheme = None
        self.guiSyntax = None
        self.themeRoot = None
        self.themePath = None
        self.syntaxFile = None
        self.confFile = None
        self.cssFile = None
        self.guiFontDB = QFontDatabase()

        self.loadFonts()
        self.updateFont()
        self.updateTheme()
        self.theIcons.updateTheme()

        self.getIcon = self.theIcons.getIcon
        self.getPixmap = self.theIcons.getPixmap
        self.loadDecoration = self.theIcons.loadDecoration

        # Extract Other Info
        self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
        self.guiScale = qApp.primaryScreen().logicalDotsPerInchX() / 96.0
        self.mainConf.guiScale = self.guiScale
        logger.verbose("GUI DPI: %.1f" % self.guiDPI)
        logger.verbose("GUI Scale: %.2f" % self.guiScale)

        # Fonts
        self.guiFont = qApp.font()

        qMetric = QFontMetrics(self.guiFont)
        self.fontPointSize = self.guiFont.pointSizeF()
        self.fontPixelSize = int(round(qMetric.height()))
        self.baseIconSize = int(round(qMetric.ascent()))
        self.textNHeight = qMetric.boundingRect("N").height()
        self.textNWidth = qMetric.boundingRect("N").width()

        # Monospace Font
        self.guiFontFixed = QFont()
        self.guiFontFixed.setPointSizeF(0.95 * self.fontPointSize)
        self.guiFontFixed.setFamily(
            QFontDatabase.systemFont(QFontDatabase.FixedFont).family())

        logger.verbose("GUI Font Family: %s" % self.guiFont.family())
        logger.verbose("GUI Font Point Size: %.2f" % self.fontPointSize)
        logger.verbose("GUI Font Pixel Size: %d" % self.fontPixelSize)
        logger.verbose("GUI Base Icon Size: %d" % self.baseIconSize)
        logger.verbose("Text 'N' Height: %d" % self.textNHeight)
        logger.verbose("Text 'N' Width: %d" % self.textNWidth)

        # Internal Mapping
        self.makeAlert = self.theParent.makeAlert
        self.tr = partial(QCoreApplication.translate, "GuiTheme")

        return

    ##
    #  Methods
    ##

    def getTextWidth(self, theText, theFont=None):
        """Returns the width needed to contain a given piece of text.
        """
        if isinstance(theFont, QFont):
            qMetrics = QFontMetrics(theFont)
        else:
            qMetrics = QFontMetrics(self.guiFont)
        return int(ceil(qMetrics.boundingRect(theText).width()))

    ##
    #  Actions
    ##

    def loadFonts(self):
        """Add the fonts in the assets fonts folder to the app.
        """
        logger.debug("Loading additional fonts")

        ttfList = []
        fontAssets = os.path.join(self.mainConf.assetPath, self.fontPath)
        for fontFam in os.listdir(fontAssets):
            fontDir = os.path.join(fontAssets, fontFam)
            if os.path.isdir(fontDir):
                logger.verbose("Found font: %s" % fontFam)
                if fontFam not in self.guiFontDB.families():
                    for fontFile in os.listdir(fontDir):
                        ttfFile = os.path.join(fontDir, fontFile)
                        if os.path.isfile(ttfFile) and fontFile.endswith(
                                ".ttf"):
                            ttfList.append(ttfFile)

        for ttfFile in ttfList:
            relPath = os.path.relpath(ttfFile, fontAssets)
            logger.verbose("Adding font: %s" % relPath)
            fontID = self.guiFontDB.addApplicationFont(ttfFile)
            if fontID < 0:
                logger.error("Failed to add font: %s" % relPath)

        return

    def updateFont(self):
        """Update the GUI's font style from settings.
        """
        theFont = QFont()
        if self.mainConf.guiFont not in self.guiFontDB.families():
            if self.mainConf.osWindows:
                if "Arial" in self.guiFontDB.families():
                    theFont.setFamily("Arial")
                else:
                    # On Windows, fall back to Cantarell provided by novelWriter
                    theFont.setFamily("Cantarell")
                theFont.setPointSize(10)
            else:
                theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
            self.mainConf.guiFont = theFont.family()
            self.mainConf.guiFontSize = theFont.pointSize()
        else:
            theFont.setFamily(self.mainConf.guiFont)
            theFont.setPointSize(self.mainConf.guiFontSize)

        qApp.setFont(theFont)

        return

    def updateTheme(self):
        """Update the GUI theme from theme files.
        """
        self.guiTheme = self.mainConf.guiTheme
        self.guiSyntax = self.mainConf.guiSyntax
        self.themeRoot = self.mainConf.themeRoot
        self.themePath = os.path.join(self.mainConf.themeRoot, self.guiPath,
                                      self.guiTheme)
        self.syntaxFile = os.path.join(self.themeRoot, self.syntaxPath,
                                       self.guiSyntax + ".conf")
        self.confFile = os.path.join(self.themePath, self.confName)
        self.cssFile = os.path.join(self.themePath, self.cssName)

        self.loadTheme()
        self.loadSyntax()

        # Update dependant colours
        backCol = qApp.palette().window().color()
        textCol = qApp.palette().windowText().color()

        backLCol = backCol.lightnessF()
        textLCol = textCol.lightnessF()

        if backLCol > textLCol:
            helpLCol = textLCol + 0.65 * (backLCol - textLCol)
        else:
            helpLCol = backLCol + 0.65 * (textLCol - backLCol)

        self.helpText = [int(255 * helpLCol)] * 3

        return True

    def loadTheme(self):
        """Load the currently specified GUI theme.
        """
        logger.debug("Loading theme files")
        logger.debug("System icon theme is '%s'" % str(QIcon.themeName()))

        # CSS File
        cssData = ""
        try:
            if os.path.isfile(self.cssFile):
                with open(self.cssFile, mode="r", encoding="utf8") as inFile:
                    cssData = inFile.read()
        except Exception:
            logger.error("Could not load theme css file")
            nw.logException()
            return False

        # Config File
        confParser = configparser.ConfigParser()
        try:
            with open(self.confFile, mode="r", encoding="utf8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load theme settings from: %s" %
                         self.confFile)
            nw.logException()
            return False

        ## Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.themeName = self._parseLine(confParser, cnfSec, "name", "")
            self.themeDescription = self._parseLine(confParser, cnfSec,
                                                    "description", "N/A")
            self.themeAuthor = self._parseLine(confParser, cnfSec, "author",
                                               "N/A")
            self.themeCredit = self._parseLine(confParser, cnfSec, "credit",
                                               "N/A")
            self.themeUrl = self._parseLine(confParser, cnfSec, "url", "")
            self.themeLicense = self._parseLine(confParser, cnfSec, "license",
                                                "N/A")
            self.themeLicenseUrl = self._parseLine(confParser, cnfSec,
                                                   "licenseurl", "")

        ## Palette
        cnfSec = "Palette"
        if confParser.has_section(cnfSec):
            self._setPalette(confParser, cnfSec, "window", QPalette.Window)
            self._setPalette(confParser, cnfSec, "windowtext",
                             QPalette.WindowText)
            self._setPalette(confParser, cnfSec, "base", QPalette.Base)
            self._setPalette(confParser, cnfSec, "alternatebase",
                             QPalette.AlternateBase)
            self._setPalette(confParser, cnfSec, "text", QPalette.Text)
            self._setPalette(confParser, cnfSec, "tooltipbase",
                             QPalette.ToolTipBase)
            self._setPalette(confParser, cnfSec, "tooltiptext",
                             QPalette.ToolTipText)
            self._setPalette(confParser, cnfSec, "button", QPalette.Button)
            self._setPalette(confParser, cnfSec, "buttontext",
                             QPalette.ButtonText)
            self._setPalette(confParser, cnfSec, "brighttext",
                             QPalette.BrightText)
            self._setPalette(confParser, cnfSec, "highlight",
                             QPalette.Highlight)
            self._setPalette(confParser, cnfSec, "highlightedtext",
                             QPalette.HighlightedText)
            self._setPalette(confParser, cnfSec, "link", QPalette.Link)
            self._setPalette(confParser, cnfSec, "linkvisited",
                             QPalette.LinkVisited)

        ## GUI
        cnfSec = "GUI"
        if confParser.has_section(cnfSec):
            self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
            self.statUnsaved = self._loadColour(confParser, cnfSec,
                                                "statusunsaved")
            self.statSaved = self._loadColour(confParser, cnfSec,
                                              "statussaved")

        # Apply Styles
        qApp.setStyleSheet(cssData)
        qApp.setPalette(self.guiPalette)

        logger.info("Loaded theme '%s'" % self.guiTheme)

        return True

    def loadSyntax(self):
        """Load the currently specified syntax highlighter theme.
        """
        logger.debug("Loading syntax theme files")

        confParser = configparser.ConfigParser()
        try:
            with open(self.syntaxFile, mode="r", encoding="utf8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load syntax colours from: %s" %
                         self.syntaxFile)
            nw.logException()
            return False

        ## Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.syntaxName = self._parseLine(confParser, cnfSec, "name", "")
            self.syntaxDescription = self._parseLine(confParser, cnfSec,
                                                     "description", "")
            self.syntaxAuthor = self._parseLine(confParser, cnfSec, "author",
                                                "")
            self.syntaxCredit = self._parseLine(confParser, cnfSec, "credit",
                                                "")
            self.syntaxUrl = self._parseLine(confParser, cnfSec, "url", "")
            self.syntaxLicense = self._parseLine(confParser, cnfSec, "license",
                                                 "")
            self.syntaxLicenseUrl = self._parseLine(confParser, cnfSec,
                                                    "licenseurl", "")

        ## Syntax
        cnfSec = "Syntax"
        if confParser.has_section(cnfSec):
            self.colBack = self._loadColour(confParser, cnfSec, "background")
            self.colText = self._loadColour(confParser, cnfSec, "text")
            self.colLink = self._loadColour(confParser, cnfSec, "link")
            self.colHead = self._loadColour(confParser, cnfSec, "headertext")
            self.colHeadH = self._loadColour(confParser, cnfSec, "headertag")
            self.colEmph = self._loadColour(confParser, cnfSec, "emphasis")
            self.colDialN = self._loadColour(confParser, cnfSec,
                                             "straightquotes")
            self.colDialD = self._loadColour(confParser, cnfSec,
                                             "doublequotes")
            self.colDialS = self._loadColour(confParser, cnfSec,
                                             "singlequotes")
            self.colHidden = self._loadColour(confParser, cnfSec, "hidden")
            self.colKey = self._loadColour(confParser, cnfSec, "keyword")
            self.colVal = self._loadColour(confParser, cnfSec, "value")
            self.colSpell = self._loadColour(confParser, cnfSec,
                                             "spellcheckline")
            self.colTagErr = self._loadColour(confParser, cnfSec, "tagerror")
            self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag")
            self.colMod = self._loadColour(confParser, cnfSec, "modifier")

        logger.info("Loaded syntax theme '%s'" % self.guiSyntax)

        return True

    def listThemes(self):
        """Scan the GUI themes folder and list all themes.
        """
        if self.themeList:
            return self.themeList

        confParser = configparser.ConfigParser()
        for themeDir in os.listdir(
                os.path.join(self.mainConf.themeRoot, self.guiPath)):
            themeConf = os.path.join(self.mainConf.themeRoot, self.guiPath,
                                     themeDir, self.confName)
            logger.verbose("Checking theme config for '%s'" % themeDir)
            try:
                with open(themeConf, mode="r", encoding="utf8") as inFile:
                    confParser.read_file(inFile)
            except Exception as e:
                self.makeAlert(
                    [self.tr("Could not load theme config file."),
                     str(e)], nwAlert.ERROR)
                continue
            themeName = ""
            if confParser.has_section("Main"):
                if confParser.has_option("Main", "name"):
                    themeName = confParser.get("Main", "name")
                    logger.verbose("Theme name is '%s'" % themeName)
            if themeName != "":
                self.themeList.append((themeDir, themeName))

        self.themeList = sorted(self.themeList, key=lambda x: x[1])

        return self.themeList

    def listSyntax(self):
        """Scan the syntax themes folder and list all themes.
        """
        if self.syntaxList:
            return self.syntaxList

        confParser = configparser.ConfigParser()
        syntaxDir = os.path.join(self.mainConf.themeRoot, self.syntaxPath)
        for syntaxFile in os.listdir(syntaxDir):
            syntaxPath = os.path.join(syntaxDir, syntaxFile)
            if not os.path.isfile(syntaxPath):
                continue
            logger.verbose("Checking theme syntax for '%s'" % syntaxFile)
            try:
                with open(syntaxPath, mode="r", encoding="utf8") as inFile:
                    confParser.read_file(inFile)
            except Exception as e:
                self.makeAlert(
                    [self.tr("Could not load syntax file."),
                     str(e)], nwAlert.ERROR)
                return []
            syntaxName = ""
            if confParser.has_section("Main"):
                if confParser.has_option("Main", "name"):
                    syntaxName = confParser.get("Main", "name")
            if len(syntaxFile) > 5 and syntaxName != "":
                self.syntaxList.append((syntaxFile[:-5], syntaxName))
                logger.verbose("Syntax name is '%s'" % syntaxName)

        self.syntaxList = sorted(self.syntaxList, key=lambda x: x[1])

        return self.syntaxList

    ##
    #  Internal Functions
    ##

    def _loadColour(self, confParser, cnfSec, cnfName):
        """Load a colour value from a config string.
        """
        if confParser.has_option(cnfSec, cnfName):
            inData = confParser.get(cnfSec, cnfName).split(",")
            outData = []
            try:
                outData.append(int(inData[0]))
                outData.append(int(inData[1]))
                outData.append(int(inData[2]))
            except Exception:
                logger.error(
                    "Could not load theme colours for '%s' from config file" %
                    cnfName)
                outData = [0, 0, 0]
        else:
            logger.warning(
                "Could not find theme colours for '%s' in config file" %
                cnfName)
            outData = [0, 0, 0]
        return outData

    def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
        """Set a palette colour value from a config string.
        """
        readCol = []
        if confParser.has_option(cnfSec, cnfName):
            inData = confParser.get(cnfSec, cnfName).split(",")
            try:
                readCol.append(int(inData[0]))
                readCol.append(int(inData[1]))
                readCol.append(int(inData[2]))
            except Exception:
                logger.error(
                    "Could not load theme colours for '%s' from config file" %
                    cnfName)
                return
        if len(readCol) == 3:
            self.guiPalette.setColor(paletteVal, QColor(*readCol))
        return

    def _parseLine(self, confParser, cnfSec, cnfName, cnfDefault):
        """Simple wrapper for the config parser to check that the entry
        exists before attempting to load it.
        """
        if confParser.has_section(cnfSec):
            if confParser.has_option(cnfSec, cnfName):
                return confParser.get(cnfSec, cnfName)
        return cnfDefault
コード例 #12
0
class GuiTheme:
    def __init__(self):

        self.mainConf = novelwriter.CONFIG
        self.iconCache = GuiIcons(self)

        # Loaded Theme Settings
        # =====================

        # Theme
        self.themeName = ""
        self.themeDescription = ""
        self.themeAuthor = ""
        self.themeCredit = ""
        self.themeUrl = ""
        self.themeLicense = ""
        self.themeLicenseUrl = ""

        # GUI
        self.statNone = [120, 120, 120]
        self.statUnsaved = [200, 15, 39]
        self.statSaved = [2, 133, 37]
        self.helpText = [0, 0, 0]

        # Loaded Syntax Settings
        # ======================

        # Main
        self.syntaxName = ""
        self.syntaxDescription = ""
        self.syntaxAuthor = ""
        self.syntaxCredit = ""
        self.syntaxUrl = ""
        self.syntaxLicense = ""
        self.syntaxLicenseUrl = ""

        # Colours
        self.colBack = [255, 255, 255]
        self.colText = [0, 0, 0]
        self.colLink = [0, 0, 0]
        self.colHead = [0, 0, 0]
        self.colHeadH = [0, 0, 0]
        self.colEmph = [0, 0, 0]
        self.colDialN = [0, 0, 0]
        self.colDialD = [0, 0, 0]
        self.colDialS = [0, 0, 0]
        self.colHidden = [0, 0, 0]
        self.colKey = [0, 0, 0]
        self.colVal = [0, 0, 0]
        self.colSpell = [0, 0, 0]
        self.colError = [0, 0, 0]
        self.colRepTag = [0, 0, 0]
        self.colMod = [0, 0, 0]

        # Changeable Settings
        self.guiTheme = None
        self.guiSyntax = None
        self.syntaxFile = None
        self.cssFile = None
        self.guiFontDB = QFontDatabase()

        # Class Setup
        # ===========

        self._guiPalette = QPalette()
        self._themeList = []
        self._syntaxList = []
        self._availThemes = {}
        self._availSyntax = {}

        self._listConf(self._availSyntax,
                       os.path.join(self.mainConf.dataPath, "syntax"))
        self._listConf(self._availSyntax,
                       os.path.join(self.mainConf.assetPath, "syntax"))
        self._listConf(self._availThemes,
                       os.path.join(self.mainConf.dataPath, "themes"))
        self._listConf(self._availThemes,
                       os.path.join(self.mainConf.assetPath, "themes"))

        self.updateFont()
        self.updateTheme()
        self.iconCache.updateTheme()

        # Icon Functions
        self.getIcon = self.iconCache.getIcon
        self.getPixmap = self.iconCache.getPixmap
        self.getItemIcon = self.iconCache.getItemIcon
        self.loadDecoration = self.iconCache.loadDecoration

        # Extract Other Info
        self.guiDPI = qApp.primaryScreen().logicalDotsPerInchX()
        self.guiScale = qApp.primaryScreen().logicalDotsPerInchX() / 96.0
        self.mainConf.guiScale = self.guiScale
        logger.verbose("GUI DPI: %.1f", self.guiDPI)
        logger.verbose("GUI Scale: %.2f", self.guiScale)

        # Fonts
        self.guiFont = qApp.font()

        qMetric = QFontMetrics(self.guiFont)
        self.fontPointSize = self.guiFont.pointSizeF()
        self.fontPixelSize = int(round(qMetric.height()))
        self.baseIconSize = int(round(qMetric.ascent()))
        self.textNHeight = qMetric.boundingRect("N").height()
        self.textNWidth = qMetric.boundingRect("N").width()

        # Monospace Font
        self.guiFontFixed = QFont()
        self.guiFontFixed.setPointSizeF(0.95 * self.fontPointSize)
        self.guiFontFixed.setFamily(
            QFontDatabase.systemFont(QFontDatabase.FixedFont).family())

        logger.verbose("GUI Font Family: %s", self.guiFont.family())
        logger.verbose("GUI Font Point Size: %.2f", self.fontPointSize)
        logger.verbose("GUI Font Pixel Size: %d", self.fontPixelSize)
        logger.verbose("GUI Base Icon Size: %d", self.baseIconSize)
        logger.verbose("Text 'N' Height: %d", self.textNHeight)
        logger.verbose("Text 'N' Width: %d", self.textNWidth)

        return

    ##
    #  Methods
    ##

    def getTextWidth(self, theText, theFont=None):
        """Returns the width needed to contain a given piece of text.
        """
        if isinstance(theFont, QFont):
            qMetrics = QFontMetrics(theFont)
        else:
            qMetrics = QFontMetrics(self.guiFont)
        return int(ceil(qMetrics.boundingRect(theText).width()))

    ##
    #  Actions
    ##

    def updateFont(self):
        """Update the GUI's font style from settings.
        """
        theFont = QFont()
        if self.mainConf.guiFont not in self.guiFontDB.families():
            if self.mainConf.osWindows and "Arial" in self.guiFontDB.families(
            ):
                # On Windows we default to Arial if possible
                theFont.setFamily("Arial")
                theFont.setPointSize(10)
            else:
                theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
            self.mainConf.guiFont = theFont.family()
            self.mainConf.guiFontSize = theFont.pointSize()
        else:
            theFont.setFamily(self.mainConf.guiFont)
            theFont.setPointSize(self.mainConf.guiFontSize)

        qApp.setFont(theFont)

        return

    def updateTheme(self):
        """Update the GUI theme from theme files.
        """
        self.guiTheme = self.mainConf.guiTheme
        self.guiSyntax = self.mainConf.guiSyntax

        self.themeFile = self._availThemes.get(self.guiTheme, None)
        if self.themeFile is None:
            logger.error("Could not find GUI theme '%s'", self.guiTheme)
        else:
            self.cssFile = self.themeFile[:-5] + ".css"
            self.loadTheme()

        self.syntaxFile = self._availSyntax.get(self.guiSyntax, None)
        if self.syntaxFile is None:
            logger.error("Could not find syntax theme '%s'", self.guiSyntax)
        else:
            self.loadSyntax()

        # Update dependant colours
        backCol = qApp.palette().window().color()
        textCol = qApp.palette().windowText().color()

        backLCol = backCol.lightnessF()
        textLCol = textCol.lightnessF()

        if backLCol > textLCol:
            helpLCol = textLCol + 0.65 * (backLCol - textLCol)
        else:
            helpLCol = backLCol + 0.65 * (textLCol - backLCol)

        self.helpText = [int(255 * helpLCol)] * 3

        return True

    def loadTheme(self):
        """Load the currently specified GUI theme.
        """
        logger.info("Loading GUI theme '%s'", self.guiTheme)

        # Config File
        confParser = NWConfigParser()
        try:
            with open(self.themeFile, mode="r", encoding="utf-8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load theme settings from: %s",
                         self.themeFile)
            logException()
            return False

        # Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.themeName = confParser.rdStr(cnfSec, "name", "")
            self.themeDescription = confParser.rdStr(cnfSec, "description",
                                                     "N/A")
            self.themeAuthor = confParser.rdStr(cnfSec, "author", "N/A")
            self.themeCredit = confParser.rdStr(cnfSec, "credit", "N/A")
            self.themeUrl = confParser.rdStr(cnfSec, "url", "")
            self.themeLicense = confParser.rdStr(cnfSec, "license", "N/A")
            self.themeLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")

        # Palette
        cnfSec = "Palette"
        if confParser.has_section(cnfSec):
            self._setPalette(confParser, cnfSec, "window", QPalette.Window)
            self._setPalette(confParser, cnfSec, "windowtext",
                             QPalette.WindowText)
            self._setPalette(confParser, cnfSec, "base", QPalette.Base)
            self._setPalette(confParser, cnfSec, "alternatebase",
                             QPalette.AlternateBase)
            self._setPalette(confParser, cnfSec, "text", QPalette.Text)
            self._setPalette(confParser, cnfSec, "tooltipbase",
                             QPalette.ToolTipBase)
            self._setPalette(confParser, cnfSec, "tooltiptext",
                             QPalette.ToolTipText)
            self._setPalette(confParser, cnfSec, "button", QPalette.Button)
            self._setPalette(confParser, cnfSec, "buttontext",
                             QPalette.ButtonText)
            self._setPalette(confParser, cnfSec, "brighttext",
                             QPalette.BrightText)
            self._setPalette(confParser, cnfSec, "highlight",
                             QPalette.Highlight)
            self._setPalette(confParser, cnfSec, "highlightedtext",
                             QPalette.HighlightedText)
            self._setPalette(confParser, cnfSec, "link", QPalette.Link)
            self._setPalette(confParser, cnfSec, "linkvisited",
                             QPalette.LinkVisited)

        # GUI
        cnfSec = "GUI"
        if confParser.has_section(cnfSec):
            self.statNone = self._loadColour(confParser, cnfSec, "statusnone")
            self.statUnsaved = self._loadColour(confParser, cnfSec,
                                                "statusunsaved")
            self.statSaved = self._loadColour(confParser, cnfSec,
                                              "statussaved")

        # CSS File
        cssData = readTextFile(self.cssFile)
        if cssData:
            qApp.setStyleSheet(cssData)

        # Apply Styles
        qApp.setPalette(self._guiPalette)

        return True

    def loadSyntax(self):
        """Load the currently specified syntax highlighter theme.
        """
        logger.info("Loading syntax theme '%s'", self.guiSyntax)

        confParser = NWConfigParser()
        try:
            with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile:
                confParser.read_file(inFile)
        except Exception:
            logger.error("Could not load syntax colours from: %s",
                         self.syntaxFile)
            logException()
            return False

        # Main
        cnfSec = "Main"
        if confParser.has_section(cnfSec):
            self.syntaxName = confParser.rdStr(cnfSec, "name", "")
            self.syntaxDescription = confParser.rdStr(cnfSec, "description",
                                                      "")
            self.syntaxAuthor = confParser.rdStr(cnfSec, "author", "")
            self.syntaxCredit = confParser.rdStr(cnfSec, "credit", "")
            self.syntaxUrl = confParser.rdStr(cnfSec, "url", "")
            self.syntaxLicense = confParser.rdStr(cnfSec, "license", "")
            self.syntaxLicenseUrl = confParser.rdStr(cnfSec, "licenseurl", "")

        # Syntax
        cnfSec = "Syntax"
        if confParser.has_section(cnfSec):
            self.colBack = self._loadColour(confParser, cnfSec, "background")
            self.colText = self._loadColour(confParser, cnfSec, "text")
            self.colLink = self._loadColour(confParser, cnfSec, "link")
            self.colHead = self._loadColour(confParser, cnfSec, "headertext")
            self.colHeadH = self._loadColour(confParser, cnfSec, "headertag")
            self.colEmph = self._loadColour(confParser, cnfSec, "emphasis")
            self.colDialN = self._loadColour(confParser, cnfSec,
                                             "straightquotes")
            self.colDialD = self._loadColour(confParser, cnfSec,
                                             "doublequotes")
            self.colDialS = self._loadColour(confParser, cnfSec,
                                             "singlequotes")
            self.colHidden = self._loadColour(confParser, cnfSec, "hidden")
            self.colKey = self._loadColour(confParser, cnfSec, "keyword")
            self.colVal = self._loadColour(confParser, cnfSec, "value")
            self.colSpell = self._loadColour(confParser, cnfSec,
                                             "spellcheckline")
            self.colError = self._loadColour(confParser, cnfSec, "errorline")
            self.colRepTag = self._loadColour(confParser, cnfSec, "replacetag")
            self.colMod = self._loadColour(confParser, cnfSec, "modifier")

        return True

    def listThemes(self):
        """Scan the GUI themes folder and list all themes.
        """
        if self._themeList:
            return self._themeList

        confParser = NWConfigParser()
        for themeKey, themePath in self._availThemes.items():
            logger.verbose("Checking theme config for '%s'", themeKey)
            themeName = _loadInternalName(confParser, themePath)
            if themeName:
                self._themeList.append((themeKey, themeName))

        self._themeList = sorted(self._themeList, key=lambda x: x[1])

        return self._themeList

    def listSyntax(self):
        """Scan the syntax themes folder and list all themes.
        """
        if self._syntaxList:
            return self._syntaxList

        confParser = NWConfigParser()
        for syntaxKey, syntaxPath in self._availSyntax.items():
            logger.verbose("Checking theme syntax for '%s'", syntaxKey)
            syntaxName = _loadInternalName(confParser, syntaxPath)
            if syntaxName:
                self._syntaxList.append((syntaxKey, syntaxName))

        self._syntaxList = sorted(self._syntaxList, key=lambda x: x[1])

        return self._syntaxList

    ##
    #  Internal Functions
    ##

    def _listConf(self, targetDict, checkDir):
        """Scan for syntax and gui themes and populate the dictionary.
        """
        if not os.path.isdir(checkDir):
            return

        for checkFile in os.listdir(checkDir):
            confPath = os.path.join(checkDir, checkFile)
            if os.path.isfile(confPath) and confPath.endswith(".conf"):
                targetDict[checkFile[:-5]] = confPath

        return

    def _loadColour(self, confParser, cnfSec, cnfName):
        """Load a colour value from a config string.
        """
        if confParser.has_option(cnfSec, cnfName):
            inData = confParser.get(cnfSec, cnfName).split(",")
            outData = []
            try:
                outData.append(int(inData[0]))
                outData.append(int(inData[1]))
                outData.append(int(inData[2]))
            except Exception:
                logger.error(
                    "Could not load theme colours for '%s' from config file",
                    cnfName)
                outData = [0, 0, 0]
        else:
            logger.warning(
                "Could not find theme colours for '%s' in config file",
                cnfName)
            outData = [0, 0, 0]
        return outData

    def _setPalette(self, confParser, cnfSec, cnfName, paletteVal):
        """Set a palette colour value from a config string.
        """
        readCol = []
        if confParser.has_option(cnfSec, cnfName):
            inData = confParser.get(cnfSec, cnfName).split(",")
            try:
                readCol.append(int(inData[0]))
                readCol.append(int(inData[1]))
                readCol.append(int(inData[2]))
            except Exception:
                logger.error(
                    "Could not load theme colours for '%s' from config file",
                    cnfName)
                return
        if len(readCol) == 3:
            self._guiPalette.setColor(paletteVal, QColor(*readCol))
        return
コード例 #13
0
ファイル: fontdialog.py プロジェクト: umlfri/umlfri2
class FontDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.__font_db = QFontDatabase()

        self.setWindowTitle(_("Select font"))

        layout = QVBoxLayout()

        content_layout = QHBoxLayout()

        font_list_layout = QVBoxLayout()

        font_list_layout.addWidget(QLabel(_("Font family:")))

        self.__font_search = UpDownPassingLineEdit()
        self.__font_search.textEdited.connect(self.__family_search_edited)

        font_list_layout.addWidget(self.__font_search)

        self.__font_list = QListWidget()
        self.__font_list.setFocusPolicy(Qt.NoFocus)

        for font in sorted(self.__font_db.families()):
            if self.__font_db.isSmoothlyScalable(font):
                self.__font_list.addItem(font)

        self.__font_list.currentTextChanged.connect(self.__family_changed)

        self.__font_search.destination_widget = self.__font_list

        font_list_layout.addWidget(self.__font_list)

        content_layout.addLayout(font_list_layout)

        style_layout = QVBoxLayout()
        style_layout.setAlignment(Qt.AlignCenter)

        self.__bold_widget = self.__create_style_button(
            "B", _("Bold"), QKeySequence.Bold, FontStyle.bold)
        style_layout.addWidget(self.__bold_widget)

        self.__italic_widget = self.__create_style_button(
            "I", _("Italic"), QKeySequence.Italic, FontStyle.italic)
        style_layout.addWidget(self.__italic_widget)

        self.__underline_widget = self.__create_style_button(
            "U", _("Underline"), QKeySequence.Underline, FontStyle.underline)
        style_layout.addWidget(self.__underline_widget)

        self.__strike_widget = self.__create_style_button(
            "S", _("Strike Out"), STRIKE_OUT, FontStyle.strike)
        style_layout.addWidget(self.__strike_widget)

        content_layout.addLayout(style_layout)

        size_layout = QVBoxLayout()
        size_layout.setAlignment(Qt.AlignHCenter)

        size_layout.addWidget(QLabel(_("Size (px):")))

        self.__size_edit = UpDownPassingLineEdit()
        self.__size_edit.textEdited.connect(self.__size_edited)
        self.__size_edit.setValidator(QIntValidator(5, 60))
        size_layout.addWidget(self.__size_edit)
        self.__size_edit.setSizePolicy(
            QSizePolicy.Preferred,
            self.__size_edit.sizePolicy().verticalPolicy())

        self.__size_slider = QSlider(Qt.Vertical)
        self.__size_slider.setFocusPolicy(Qt.NoFocus)
        self.__size_slider.setRange(5, 60)
        self.__size_slider.setTickInterval(5)
        self.__size_slider.setTickPosition(QSlider.TicksRight)
        self.__size_slider.valueChanged.connect(self.__size_changed)

        self.__size_edit.destination_widget = self.__size_slider

        size_layout.addWidget(self.__size_slider)

        content_layout.addLayout(size_layout)

        layout.addLayout(content_layout)

        self.__example = QLineEdit("AaBbYyZz")
        self.__example.setFixedHeight(80)
        layout.addWidget(self.__example)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        button_box.button(QDialogButtonBox.Ok).setText(_("Ok"))
        button_box.button(QDialogButtonBox.Cancel).setText(_("Cancel"))
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)

        layout.addWidget(button_box)
        self.setLayout(layout)

        self.__font = Fonts.default

    def __create_style_button(self, text, tooltip, keys, font_style):
        def toggled(value):
            self.__font = self.__font.change(font_style, value)
            self.__refresh_example_font()

        shortcut = QKeySequence(keys)
        widget = QPushButton(text)
        widget.setShortcut(shortcut)
        widget.setToolTip("{0} ({1})".format(tooltip, shortcut.toString()))
        font = widget.font()
        if font_style == FontStyle.bold:
            font.setBold(True)
        elif font_style == FontStyle.italic:
            font.setItalic(True)
        elif font_style == FontStyle.underline:
            font.setUnderline(True)
        elif font_style == FontStyle.strike:
            font.setStrikeOut(True)
        widget.setFont(font)
        widget.setCheckable(True)
        widget.toggled.connect(toggled)
        return widget

    @property
    def ufl_font(self):
        return self.__font

    @ufl_font.setter
    def ufl_font(self, value):
        self.__font = value

        for item in self.__font_list.findItems(value.family, Qt.MatchExactly):
            self.__font_list.setCurrentItem(item)
            break
        else:
            self.__font_list.clearSelection()

        self.__font_search.setText(value.family)

        self.__bold_widget.setChecked(FontStyle.bold in value.style)
        self.__italic_widget.setChecked(FontStyle.italic in value.style)
        self.__underline_widget.setChecked(FontStyle.underline in value.style)
        self.__strike_widget.setChecked(FontStyle.strike in value.style)

        self.__size_slider.setValue(value.size)
        self.__size_edit.setText(str(value.size))

        self.__refresh_example_font()

    def __refresh_example_font(self):
        qfont = QFont(self.__font.family)
        qfont.setPixelSize(self.__font.size)
        qfont.setBold(FontStyle.bold in self.__font.style)
        qfont.setItalic(FontStyle.italic in self.__font.style)
        qfont.setStrikeOut(FontStyle.strike in self.__font.style)
        qfont.setUnderline(FontStyle.underline in self.__font.style)

        self.__example.setFont(qfont)

    def __family_search_edited(self, name):
        selected_text = self.__font_list.currentItem().text()
        if selected_text.startswith(name):
            return

        for item in self.__font_list.findItems(name, Qt.MatchStartsWith):
            self.__font_list.setCurrentItem(item)
            break

    def __family_changed(self, value):
        if self.__font.family != value:
            if not self.__font_search.hasFocus():
                self.__font_search.setText(value)
            self.__font = self.__font.change_family(value)
            self.__refresh_example_font()

    def __size_edited(self, value):
        if not value or int(value) < 5:
            return
        int_value = int(value)
        if self.__font.size != int_value:
            self.__font = self.__font.change_size(int_value)

            self.__size_slider.setValue(int_value)

            self.__refresh_example_font()

    def __size_changed(self, value):
        if self.__font.size != value:
            if self.__size_edit.text() != str(value):
                self.__size_edit.setText(str(value))
                self.__size_edit.selectAll()
            self.__font = self.__font.change_size(value)
            self.__refresh_example_font()
コード例 #14
0
ファイル: Property.py プロジェクト: coyove/RouteMaster
    def __init__(self, parent: typing.Optional['QWidget']):
        super().__init__(parent=parent)
        self.scrollView = QScrollArea(self)
        self.scrollView.setWidgetResizable(True)
        self.scrollView.setHorizontalScrollBarPolicy(
            QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

        self.textAttr = QWidget(self)
        self.textAttr.setSizePolicy(QSizePolicy.Policy.Preferred,
                                    QSizePolicy.Policy.Preferred)
        self.textAttrBox = QVBoxLayout()
        self.textAttr.setLayout(self.textAttrBox)

        self.cascades = SvgBar(self)
        self.cascades.setVisible(False)
        self.cascades.onDelete = self.deleteCascade
        self.cascades.onDrag = self.resortCascade
        self.cascades.onCopy = self.onCopy
        self.textAttrBox.addWidget(self.cascades)

        self.svgId = QLabel('N/A', self)
        self.textAttrBox.addWidget(self._title(TR('Type')))
        self.textAttrBox.addWidget(self.svgId)

        self.startXOffsetLabel = self._title(TR('Overlay: Start X Percentage'))
        self.textAttrBox.addWidget(self.startXOffsetLabel)
        self.startXOffset = QSlider(self)
        self.startXOffset.setOrientation(QtCore.Qt.Orientation.Horizontal)
        self.startXOffset.setMinimum(0)
        self.startXOffset.setMaximum(3)
        self.startXOffset.valueChanged.connect(
            lambda e: self.offsetChanged('xo',
                                         self.startXOffset.value() * 0.25))
        self.textAttrBox.addWidget(self.startXOffset)

        self.textAttrBox.addWidget(self._title(TR('Text')))
        self.text = QTextEdit(self)
        self.text.textChanged.connect(self.textChanged)
        self.text.installEventFilter(self)
        self.textAttrBox.addWidget(self.text)

        self.textFont = QComboBox(self)
        fontFamilies = QFontDatabase()
        for s in fontFamilies.families():
            self.textFont.addItem(s)
        self.textFont.currentIndexChanged.connect(self.fontChanged)
        self.textFont.setEditable(True)

        self.textSize = QComboBox(self)
        for i in range(8, 150, 1):
            if i <= 32:
                self.textSize.addItem(str(i))
            elif i <= 80 and i % 2 == 0:
                self.textSize.addItem(str(i))
            elif i % 10 == 0:
                self.textSize.addItem(str(i))
        self.textSize.currentIndexChanged.connect(self.sizeChanged)
        self.textSize.setEditable(True)
        self._addBiBoxInTextAttrBox(self._title(TR("Font Family")),
                                    self.textFont,
                                    self._title(TR("Font Size")),
                                    self.textSize)

        self.textAlign = QComboBox(self)
        self.textPlace = QComboBox(self)
        for c in [self.textAlign, self.textPlace]:
            c.addItem(TR('Center'), 'c')
            c.addItem(TR('Top'), 't')
            c.addItem(TR('Bottom'), 'b')
            c.addItem(TR('Left'), 'l')
            c.addItem(TR('Right'), 'r')
        self.textAlign.currentIndexChanged.connect(self.alignChanged)
        self.textPlace.currentIndexChanged.connect(self.placeChanged)
        self._addBiBoxInTextAttrBox(self._title(TR("Alignment")),
                                    self.textAlign,
                                    self._title(TR("Placement")),
                                    self.textPlace)

        self.textX = QSpinBox(self)
        self.textY = QSpinBox(self)
        for c in [self.textX, self.textY]:
            c.setValue(0)
            c.setMinimum(-1e5)
            c.setMaximum(1e5)
        self.textX.valueChanged.connect(lambda e: self.offsetChanged('x', e))
        self.textY.valueChanged.connect(lambda e: self.offsetChanged('y', e))
        self._addBiBoxInTextAttrBox(self._title(TR("Offset X")), self.textX,
                                    self._title(TR("Offset Y")), self.textY)

        # self.setLayout(self.textAttrBox)
        self.scrollView.setWidget(self.textAttr)
        box = QVBoxLayout()
        box.addWidget(self.scrollView)
        box.setContentsMargins(0, 0, 0, 0)
        self.setLayout(box)

        self.show()
        self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding,
                           QSizePolicy.Policy.MinimumExpanding)

        self.updating = False