def pixmap(cursor, num_lines=6, scale=0.8): """Return a QPixmap displaying the selected lines of the document. If the cursor has no selection, num_lines are drawn. By default the text is drawn 0.8 * the normal font size. You can change that by supplying the scale parameter. """ block = cursor.document().findBlock(cursor.selectionStart()) c2 = QTextCursor(block) if cursor.hasSelection(): c2.setPosition(cursor.selectionEnd(), QTextCursor.KeepAnchor) c2.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) else: c2.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor, num_lines) data = textformats.formatData('editor') doc = QTextDocument() font = QFont(data.font) font.setPointSizeF(font.pointSizeF() * scale) doc.setDefaultFont(font) doc.setPlainText(c2.selection().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlighter.highlight(doc, state=tokeniter.state(block)) size = doc.size().toSize() + QSize(8, -4) pix = QPixmap(size) pix.fill(data.baseColors['background']) doc.drawContents(QPainter(pix)) return pix
def set_gui(self, qfont: QFont): self.setText(qfont.family() + " " + _format_font_size(qfont.pointSizeF())) preview_font = QFont(qfont) preview_font.setPointSizeF(self.font().pointSizeF()) self.setFont(preview_font)
def setDefaultFont(self, font: QFont): super(NewLexer, self).setDefaultFont(font) m = 0 for v in lexer.__bases__[-1].__dict__.values(): if type(v) == int and v > m: m = v for i in range(m): style_font: QFont = self.font(i) style_font.setFamily(font.family()) style_font.setPointSizeF(font.pointSizeF()) self.setFont(style_font, i)
def get_optimal_font(family_font: str, w, h, text: str) -> QFont: font = QFont(family_font) font.setStyleHint(QFont.Courier, QFont.PreferAntialias) metrics = QFontMetrics(font) # SOURCE: https://github.com/gil9red/SimplePyScripts/blob/add91e36e1ee59b3956b9fafdcffc9f4ff10ed3d/qt__pyqt__pyside__pyqode/pyqt__QPainter__draw_table.py#L98 factor = w / metrics.boundingRect(0, 0, w, h, Qt.AlignCenter, text).width() if factor < 1 or factor > 1.25: font.setPointSizeF(font.pointSizeF() * factor) return font
def __init__(self, str, x, y, size, w, fontname): super().__init__() font = QFont(fontname) fontMetrics = QFontMetrics(font) scale = float(fontMetrics.width(str)) / w font.setPointSizeF(font.pointSizeF() / scale) self.setFont(font) self.setPos(x - w / 2, y - fontMetrics.height()) self.setPlainText(str)
def loadSettings(self): s = QSettings() s.beginGroup("log") font = QFont(s.value("fontfamily", "monospace", str)) font.setPointSizeF(s.value("fontsize", 9.0, float)) with qutil.signalsBlocked(self.fontChooser, self.fontSize): self.fontChooser.setCurrentFont(font) self.fontSize.setValue(font.pointSizeF()) self.showlog.setChecked(s.value("show_on_start", True, bool)) self.rawview.setChecked(s.value("rawview", True, bool)) self.hideauto.setChecked(s.value("hide_auto_engrave", False, bool))
def render__label_qfont(self, qfont: QFont): self.cfg.render.label_font = attr.evolve( self.cfg.render.label_font, # Font file selection family=qfont.family(), bold=qfont.bold(), italic=qfont.italic(), # Font size size=qfont.pointSizeF(), # QFont implementation details toString=qfont.toString(), )
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 wheelEvent(self, event): """ We catch wheelEvent if key modifier is CTRL to change font size. Note: this should be in a class specific for main textEditView (#TODO). """ if event.modifiers() & Qt.ControlModifier: # Get the wheel angle. d = event.angleDelta().y() / 120 # Update settings f = QFont() f.fromString(settings.textEditor["font"]) f.setPointSizeF(f.pointSizeF() + d) settings.textEditor["font"] = f.toString() # Update font to all textEditView. Drastically. for w in F.mainWindow().findChildren(textEditView, QRegExp(".*")): w.loadFontSettings() # We tell the world that we accepted this event event.accept() return QTextEdit.wheelEvent(self, event)
class TextShape(MarcShape): def __init__(self, host_widget, text, location, size, font_name, color, styles="", anchor_type=TextAnchorType.ANCHOR_BOTTOM_LEFT, include_descent_in_height=True): # size either refers to the point size (relative to the view height) # or to the max width and max height in view coordinates """ :param host_widget: the MarcPaintWidget in which this is being drawn :param text: The text to be written :param size: The size of the text to be written in view coordinates. Either a number, in which case this will be the height of the text, or a tuple of (width, height) in which case the text will be made as big as possible such that it fits in the bounding box. :param font_name: Font family name :param color: duh :param styles: "bold", "italic", or "bold italic" :param anchor_type: one of the TextAnchorTypes. Either determines the position of the draw location within the text if we are given a height only in the size parameter, or determines the position of the text in the bounding box if we are given a (width, height) tuple in the size parameter. Note that the location parameter defines the lower left corner of the bounding box. :param include_descent_in_height: determines whether we include the descent in the height of the letters, both for size and for anchoring purposes. """ super().__init__(host_widget) self.text = text self.font_name = font_name self.color = tuple(int(k * 255) for k in color) self.size = (float(size[0]), float(size[1])) if hasattr( size, "__len__") else float(size) self.view_location = location self.anchor_type = anchor_type self.include_descent_in_height = include_descent_in_height # These two attributes need to be recalculated at every draw self.position = None self.font = None self.styles = styles.lower() def set_font_and_position(self): goal_view_font_height = self.size[1] if isinstance( self.size, tuple) else self.size goal_window_font_height = goal_view_font_height / self.host_widget.get_view_height( ) * self.host_widget.height() goal_view_font_width = self.size[0] if isinstance( self.size, tuple) else float("inf") goal_window_font_width = goal_view_font_width / self.host_widget.get_view_height( ) * self.host_widget.height() self.font = QFont(self.font_name, goal_window_font_height) if "italic" in self.styles: self.font.setItalic(True) if "bold" in self.styles: self.font.setBold(True) font_met = QFontMetricsF(self.font) bounding_rect = font_met.tightBoundingRect(self.text) effective_height = bounding_rect.height() if self.include_descent_in_height \ else bounding_rect.height() - bounding_rect.bottom() resize_ratio = max(bounding_rect.width() / goal_window_font_width, effective_height / goal_window_font_height) self.font.setPointSizeF(self.font.pointSizeF() / resize_ratio) # at this point, we should have the font at the right size to fill the desired height or bounding box # recalculate its metrics font_met = QFontMetricsF(self.font) bounding_rect = font_met.tightBoundingRect(self.text) assert isinstance(bounding_rect, QRectF) self.position = self.host_widget.view_to_window(self.view_location) if self.include_descent_in_height: x_zeroing_adjustment, y_zeroing_adjustment = -bounding_rect.left( ), -bounding_rect.bottom() effective_height = bounding_rect.height() else: x_zeroing_adjustment, y_zeroing_adjustment = -bounding_rect.left( ), 0 effective_height = bounding_rect.height() - bounding_rect.bottom() if self.anchor_type in (TextAnchorType.ANCHOR_CENTER_TOP, TextAnchorType.ANCHOR_TOP_LEFT, TextAnchorType.ANCHOR_TOP_RIGHT): # top vertically if hasattr(self.size, "__len__"): y_anchoring_adjustment = effective_height - goal_window_font_height else: # point location y_anchoring_adjustment = effective_height elif self.anchor_type in (TextAnchorType.ANCHOR_CENTER, TextAnchorType.ANCHOR_CENTER_LEFT, TextAnchorType.ANCHOR_CENTER_RIGHT): # centered vertically if hasattr(self.size, "__len__"): y_anchoring_adjustment = (effective_height - goal_window_font_height) / 2 else: # point location y_anchoring_adjustment = effective_height / 2 else: # (default) bottom vertically y_anchoring_adjustment = 0 if self.anchor_type in (TextAnchorType.ANCHOR_TOP_RIGHT, TextAnchorType.ANCHOR_CENTER_RIGHT, TextAnchorType.ANCHOR_BOTTOM_RIGHT): # right horizontally if hasattr(self.size, "__len__"): x_anchoring_adjustment = -bounding_rect.width( ) + goal_window_font_width else: # point location x_anchoring_adjustment = -bounding_rect.width() elif self.anchor_type in (TextAnchorType.ANCHOR_CENTER_TOP, TextAnchorType.ANCHOR_CENTER, TextAnchorType.ANCHOR_CENTER_BOTTOM): # center horizontally if hasattr(self.size, "__len__"): x_anchoring_adjustment = (-bounding_rect.width() + goal_window_font_width) / 2 else: # point location x_anchoring_adjustment = -bounding_rect.width() / 2 else: # (default) left horizontally x_anchoring_adjustment = 0 self.position = self.position[0] + x_zeroing_adjustment + x_anchoring_adjustment, \ self.position[1] + y_zeroing_adjustment + y_anchoring_adjustment def paint(self): self.set_font_and_position() painter = QPainter(self.host_widget) painter.setPen(QColor(*self.color)) painter.setFont(self.font) painter.drawText(self.position[0], self.position[1], self.text) painter.end() # Resets the OpenGL states we need for drawing self.host_widget.initializeGL()
class TextFormatData(object): """Encapsulates all settings in the Fonts & Colors page for a scheme.""" def __init__(self, scheme): """Loads the data from scheme.""" self.font = None self.baseColors = {} self.defaultStyles = {} self.allStyles = {} self._inherits = {} self.load(scheme) def load(self, scheme): """Load the settings for the scheme. Called on init.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # load font defaultfont = "Lucida Console" if os.name == "nt" else "monospace" self.font = QFont(s.value("fontfamily", defaultfont, str)) self.font.setPointSizeF(s.value("fontsize", 10.0, float)) # load base colors s.beginGroup("basecolors") for name in baseColors: if s.contains(name): self.baseColors[name] = QColor(s.value(name, "", str)) else: self.baseColors[name] = baseColorDefaults[name]() s.endGroup() # get the list of supported styles from ly.colorize all_styles = ly.colorize.default_mapping() default_styles = set() for group, styles in all_styles: d = self._inherits[group] = {} for style in styles: if style.base: default_styles.add(style.base) d[style.name] = style.base default_scheme = ly.colorize.default_scheme # load default styles s.beginGroup("defaultstyles") for name in default_styles: self.defaultStyles[name] = f = QTextCharFormat() css = default_scheme[None].get(name) if css: css2fmt(css, f) s.beginGroup(name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() # load specific styles s.beginGroup("allstyles") for group, styles in all_styles: self.allStyles[group]= {} s.beginGroup(group) for style in styles: self.allStyles[group][style.name] = f = QTextCharFormat() css = default_scheme[group].get(style.name) if css: css2fmt(css, f) s.beginGroup(style.name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() s.endGroup() def save(self, scheme): """Save the settings to the scheme.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # save font s.setValue("fontfamily", self.font.family()) s.setValue("fontsize", self.font.pointSizeF()) # save base colors for name in baseColors: s.setValue("basecolors/"+name, self.baseColors[name].name()) # save default styles s.beginGroup("defaultstyles") for name in defaultStyles: s.beginGroup(name) self.saveTextFormat(self.defaultStyles[name], s) s.endGroup() s.endGroup() # save all specific styles s.beginGroup("allstyles") for group, styles in ly.colorize.default_mapping(): s.beginGroup(group) for style in styles: s.beginGroup(style.name) self.saveTextFormat(self.allStyles[group][style.name], s) s.endGroup() s.endGroup() s.endGroup() def textFormat(self, group, name): """Return a QTextCharFormat() for the specified group and name.""" inherit = self._inherits[group].get(name) f = QTextCharFormat(self.defaultStyles[inherit]) if inherit else QTextCharFormat() f.merge(self.allStyles[group][name]) return f def css_scheme(self): """Return a dictionary of css dictionaries representing this scheme. This can be fed to the ly.colorize.format_stylesheet() function. """ scheme = {} # base/default styles d = scheme[None] = {} for name, fmt in self.defaultStyles.items(): d[name] = fmt2css(fmt) # mode/group styles for mode, styles in self.allStyles.items(): d = scheme[mode] = {} for name, fmt in styles.items(): d[name] = fmt2css(fmt) return scheme def palette(self): """Return a basic palette with text, background, selection and selection background filled in.""" p = QApplication.palette() p.setColor(QPalette.Text, self.baseColors['text']) p.setColor(QPalette.Base, self.baseColors['background']) p.setColor(QPalette.HighlightedText, self.baseColors['selectiontext']) p.setColor(QPalette.Highlight, self.baseColors['selectionbackground']) return p def saveTextFormat(self, fmt, settings): """(Internal) Store one QTextCharFormat in the QSettings instance.""" if fmt.hasProperty(QTextFormat.FontWeight): settings.setValue('bold', fmt.fontWeight() >= 70) else: settings.remove('bold') if fmt.hasProperty(QTextFormat.FontItalic): settings.setValue('italic', fmt.fontItalic()) else: settings.remove('italic') if fmt.hasProperty(QTextFormat.TextUnderlineStyle): settings.setValue('underline', fmt.fontUnderline()) else: settings.remove('underline') if fmt.hasProperty(QTextFormat.ForegroundBrush): settings.setValue('textColor', fmt.foreground().color().name()) else: settings.remove('textColor') if fmt.hasProperty(QTextFormat.BackgroundBrush): settings.setValue('backgroundColor', fmt.background().color().name()) else: settings.remove('backgroundColor') if fmt.hasProperty(QTextFormat.TextUnderlineColor): settings.setValue('underlineColor', fmt.underlineColor().name()) else: settings.remove('underlineColor') def loadTextFormat(self, fmt, settings): """(Internal) Merge values from the QSettings instance into the QTextCharFormat.""" if settings.contains('bold'): fmt.setFontWeight(QFont.Bold if settings.value('bold', False, bool) else QFont.Normal) if settings.contains('italic'): fmt.setFontItalic(settings.value('italic', False, bool)) if settings.contains('underline'): fmt.setFontUnderline(settings.value('underline', False, bool)) if settings.contains('textColor'): fmt.setForeground(QColor(settings.value('textColor', '' , str))) if settings.contains('backgroundColor'): fmt.setBackground(QColor(settings.value('backgroundColor', '' , str))) if settings.contains('underlineColor'): fmt.setUnderlineColor(QColor(settings.value('underlineColor', '' , str)))
def drawLines(self, qp): pen = QPen(QColor('#eeeff7'), 2, Qt.SolidLine) qp.setPen(pen) font = QFont('Arial', 19) qp.setFont(font) # first - растояние первой линии от верха first = self.evh qp.drawLine(self.x(), first, self.width(), first) # Отрисовка главного заголовка настроек # Функция возвращающая верхнюю середину ширины окна для подзаголовка def getPosxTitle(name): wn = qp.fontMetrics().width(name) return (self.width() / 2) - (wn / 2) pos_x_title = getPosxTitle(self.listnames[0]) # Условие описания уменьшения названия при уменьшении высоты if self.height() < self.parent().baseSize().height(): fontsize = (first - font.pointSizeF()) / 1.6 if self.height() <= 550: fontsize = 14 font.setPointSizeF(fontsize) qp.setFont(font) pos_x_title = getPosxTitle(self.listnames[0]) qp.drawText(pos_x_title, first / 1.55, self.listnames[0]) # distance = 150 дистанция между остальными линиями distance = (self.height() - first) / 4 i = 0 '''leftIndent - маштабируемый отступ от левой стороны окна, для названий категорий настроек''' leftIndent = qp.viewport().width() / 9 while first < self.height() and i < len(self.listnames[1:]): first += distance i += 1 qp.setPen(QColor('#9fcdd0')) font.setPointSize(16) # ширина квадрата в котором написано название категории # widthrect = 240 widthrect = 240 # размер маленького шрифта smallfnt = 10 if self.height() < self.parent().baseSize().height(): if self.height() <= 550: font.setPointSize(13) widthrect = 220 smallfnt = 8 else: font.setPointSizeF(distance / font.pointSizeF() * 1.72) qp.setFont(font) # posvcen - вычисление маштабируемого отступа по выстое posvcen = lambda a: distance / a + qp.fontMetrics().height() if i == 2: qp.drawText( leftIndent - 5, first - posvcen(1.55), widthrect, distance / 2, Qt.TextWordWrap | Qt.AlignHCenter|Qt.AA_EnableHighDpiScaling, self.listnames[i]) qp.setPen(QColor('#eeeff7')) # font.setPointSize(smallfnt) font.setPointSize(smallfnt) font.setItalic(True) qp.setFont(font) qp.drawText( leftIndent, first - posvcen(2.15), 230, distance / 2, Qt.TextWordWrap | Qt.AlignHCenter|Qt.AA_EnableHighDpiScaling, self.strtolist) else: font.setItalic(False) qp.setFont(font) qp.drawText( leftIndent, first - posvcen(2), widthrect, distance / 2, Qt.TextWordWrap | Qt.AlignHCenter|Qt.AA_EnableHighDpiScaling, self.listnames[i]) # отрисовка линий if first >= self.height(): continue else: qp.setPen(pen) qp.drawLine(self.rect().x(), first, self.width(), first)
class TextFormatData(object): """Encapsulates all settings in the Fonts & Colors page for a scheme.""" def __init__(self, scheme): """Loads the data from scheme.""" self.font = None self.baseColors = {} self.defaultStyles = {} self.allStyles = {} self._inherits = {} self.load(scheme) def load(self, scheme): """Load the settings for the scheme. Called on init.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # load font defaultfont = "Lucida Console" if os.name == "nt" else "monospace" self.font = QFont(s.value("fontfamily", defaultfont, str)) self.font.setPointSizeF(s.value("fontsize", 10.0, float)) # load base colors s.beginGroup("basecolors") for name in baseColors: if s.contains(name): self.baseColors[name] = QColor(s.value(name, "", str)) else: self.baseColors[name] = baseColorDefaults[name]() s.endGroup() # get the list of supported styles from ly.colorize all_styles = ly.colorize.default_mapping() default_styles = set() for group, styles in all_styles: d = self._inherits[group] = {} for style in styles: if style.base: default_styles.add(style.base) d[style.name] = style.base default_scheme = ly.colorize.default_scheme # load default styles s.beginGroup("defaultstyles") for name in default_styles: self.defaultStyles[name] = f = QTextCharFormat() css = default_scheme[None].get(name) if css: css2fmt(css, f) s.beginGroup(name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() # load specific styles s.beginGroup("allstyles") for group, styles in all_styles: self.allStyles[group] = {} s.beginGroup(group) for style in styles: self.allStyles[group][style.name] = f = QTextCharFormat() css = default_scheme[group].get(style.name) if css: css2fmt(css, f) s.beginGroup(style.name) self.loadTextFormat(f, s) s.endGroup() s.endGroup() s.endGroup() def save(self, scheme): """Save the settings to the scheme.""" s = QSettings() s.beginGroup("fontscolors/" + scheme) # save font s.setValue("fontfamily", self.font.family()) s.setValue("fontsize", self.font.pointSizeF()) # save base colors for name in baseColors: s.setValue("basecolors/" + name, self.baseColors[name].name()) # save default styles s.beginGroup("defaultstyles") for name in defaultStyles: s.beginGroup(name) self.saveTextFormat(self.defaultStyles[name], s) s.endGroup() s.endGroup() # save all specific styles s.beginGroup("allstyles") for group, styles in ly.colorize.default_mapping(): s.beginGroup(group) for style in styles: s.beginGroup(style.name) self.saveTextFormat(self.allStyles[group][style.name], s) s.endGroup() s.endGroup() s.endGroup() def textFormat(self, group, name): """Return a QTextCharFormat() for the specified group and name.""" inherit = self._inherits[group].get(name) f = QTextCharFormat( self.defaultStyles[inherit]) if inherit else QTextCharFormat() f.merge(self.allStyles[group][name]) return f def css_scheme(self): """Return a dictionary of css dictionaries representing this scheme. This can be fed to the ly.colorize.format_stylesheet() function. """ scheme = {} # base/default styles d = scheme[None] = {} for name, fmt in self.defaultStyles.items(): d[name] = fmt2css(fmt) # mode/group styles for mode, styles in self.allStyles.items(): d = scheme[mode] = {} for name, fmt in styles.items(): d[name] = fmt2css(fmt) return scheme def palette(self): """Return a basic palette with text, background, selection and selection background filled in.""" p = QApplication.palette() p.setColor(QPalette.Text, self.baseColors['text']) p.setColor(QPalette.Base, self.baseColors['background']) p.setColor(QPalette.HighlightedText, self.baseColors['selectiontext']) p.setColor(QPalette.Highlight, self.baseColors['selectionbackground']) return p def saveTextFormat(self, fmt, settings): """(Internal) Store one QTextCharFormat in the QSettings instance.""" if fmt.hasProperty(QTextFormat.FontWeight): settings.setValue('bold', fmt.fontWeight() >= 70) else: settings.remove('bold') if fmt.hasProperty(QTextFormat.FontItalic): settings.setValue('italic', fmt.fontItalic()) else: settings.remove('italic') if fmt.hasProperty(QTextFormat.TextUnderlineStyle): settings.setValue('underline', fmt.fontUnderline()) else: settings.remove('underline') if fmt.hasProperty(QTextFormat.ForegroundBrush): settings.setValue('textColor', fmt.foreground().color().name()) else: settings.remove('textColor') if fmt.hasProperty(QTextFormat.BackgroundBrush): settings.setValue('backgroundColor', fmt.background().color().name()) else: settings.remove('backgroundColor') if fmt.hasProperty(QTextFormat.TextUnderlineColor): settings.setValue('underlineColor', fmt.underlineColor().name()) else: settings.remove('underlineColor') def loadTextFormat(self, fmt, settings): """(Internal) Merge values from the QSettings instance into the QTextCharFormat.""" if settings.contains('bold'): fmt.setFontWeight(QFont.Bold if settings. value('bold', False, bool) else QFont.Normal) if settings.contains('italic'): fmt.setFontItalic(settings.value('italic', False, bool)) if settings.contains('underline'): fmt.setFontUnderline(settings.value('underline', False, bool)) if settings.contains('textColor'): fmt.setForeground(QColor(settings.value('textColor', '', str))) if settings.contains('backgroundColor'): fmt.setBackground( QColor(settings.value('backgroundColor', '', str))) if settings.contains('underlineColor'): fmt.setUnderlineColor( QColor(settings.value('underlineColor', '', str)))
class CharMap(QWidget): """A widget displaying a table of characters.""" characterSelected = pyqtSignal(str) characterClicked = pyqtSignal(str) def __init__(self, parent=None): super(CharMap, self).__init__(parent) self._showToolTips = True self._showWhatsThis = True self._selected = -1 self._column_count = 32 self._square = 24 self._range = (0, 0) self._font = QFont() def setRange(self, first, last): self._range = (first, last) self._selected = -1 self.adjustSize() self.update() def range(self): return self._range def square(self): """Returns the width of one item (determined by font size).""" return self._square def select(self, charcode): """Selects the specified character (int or str).""" if not isinstance(charcode, int): charcode = ord(charcode) if not self._range[0] <= charcode <= self._range[1]: charcode = -1 if self._selected != charcode: self._selected = charcode self.characterSelected.emit(chr(charcode)) self.update() def character(self): """Returns the currently selected character, if any.""" if self._selected != -1: return chr(self._selected) def setDisplayFont(self, font): self._font.setFamily(font.family()) self.update() def displayFont(self): return QFont(self._font) def setDisplayFontSize(self, size): self._font.setPointSize(size) self._square = max(24, QFontMetrics(self._font).xHeight() * 3) self.adjustSize() self.update() def displayFontSize(self): return self._font.pointSize() def setDisplayFontSizeF(self, size): self._font.setPointSizeF(size) self._square = max(24, QFontMetrics(self._font).xHeight() * 3) self.adjustSize() self.update() def displayFontSizeF(self): return self._font.pointSizeF() def setColumnCount(self, count): """Sets how many columns should be used.""" count = max(1, count) self._column_count = count self.adjustSize() self.update() def columnCount(self): return self._column_count def sizeHint(self): return self.sizeForColumnCount(self._column_count) def paintEvent(self, ev): rect = ev.rect() s = self._square rows = range(rect.top() // s, rect.bottom() // s + 1) cols = range(rect.left() // s, rect.right() // s + 1) painter = QPainter(self) painter.setPen(QPen(self.palette().color(QPalette.Window))) painter.setFont(self._font) metrics = QFontMetrics(self._font) # draw characters on white tiles tile = self.palette().color(QPalette.Base) selected_tile = self.palette().color(QPalette.Highlight) selected_tile.setAlpha(96) selected_box = self.palette().color(QPalette.Highlight) text_pen = QPen(self.palette().text().color()) disabled_pen = QPen(self.palette().color(QPalette.Disabled, QPalette.Text)) selection_pen = QPen(selected_box) for row in rows: for col in cols: char = row * self._column_count + col + self._range[0] if char > self._range[1]: break printable = self.isprint(char) painter.setClipRect(col * s, row * s, s, s) if char == self._selected: painter.fillRect(col * s + 1, row * s + 1, s - 2, s - 2, selected_tile) painter.setPen(selection_pen) painter.drawRect(col * s, row * s, s - 1, s - 1) elif printable: painter.fillRect(col * s + 1, row * s + 1, s - 2, s - 2, tile) painter.setPen(text_pen if printable else disabled_pen) t = chr(char) x = col * s + s // 2 - metrics.width(t) // 2 y = row * s + 4 + metrics.ascent() painter.drawText(x, y, t) else: continue break def sizeForColumnCount(self, count): """Returns the size the widget would have in a certain column count. This can be used in e.g. a resizable scroll area. """ first, last = self._range rows = ((last - first) // count) + 1 return QSize(count, rows) * self._square def columnCountForWidth(self, width): """Returns the number of columns that would fit into the given width.""" return width // self._square def mousePressEvent(self, ev): charcode = self.charcodeAt(ev.pos()) if charcode != -1 and self.isprint(charcode): self.select(charcode) if ev.button() != Qt.RightButton: self.characterClicked.emit(chr(charcode)) def charcodeRect(self, charcode): """Returns the rectangular box around the given charcode, if any.""" if self._range[0] <= charcode <= self._range[1]: row, col = divmod(charcode - self._range[0], self._column_count) s = self._square return QRect(col * s, row * s, s, s) def charcodeAt(self, position): row = position.y() // self._square col = position.x() // self._square if col <= self._column_count: charcode = self._range[0] + row * self._column_count + col if charcode <= self._range[1]: return charcode return -1 def event(self, ev): if ev.type() == QEvent.ToolTip: if self._showToolTips: c = self.charcodeAt(ev.pos()) if c: text = self.getToolTipText(c) if text: rect = self.charcodeRect(c) QToolTip.showText(ev.globalPos(), text, self, rect) ev.accept() return True elif ev.type() == QEvent.QueryWhatsThis: if self._showWhatsThis: ev.accept() return True elif ev.type() == QEvent.WhatsThis: ev.accept() if self._showWhatsThis: c = self.charcodeAt(ev.pos()) text = self.getWhatsThisText(c) if c else None if text: QWhatsThis.showText(ev.globalPos(), text, self) else: QWhatsThis.leaveWhatsThisMode() return True return super(CharMap, self).event(ev) def getToolTipText(self, charcode): try: return unicodedata.name(chr(charcode)) except ValueError: pass def getWhatsThisText(self, charcode): try: name = unicodedata.name(chr(charcode)) except ValueError: return return whatsthis_html.format( self._font.family(), chr(charcode), name, charcode) def setShowToolTips(self, enabled): self._showToolTips = bool(enabled) def showToolTips(self): return self._showToolTips def setShowWhatsThis(self, enabled): self._showWhatsThis = bool(enabled) def showWhatsThis(self): return self._showWhatsThis def isprint(self, charcode): """Returns True if the given charcode is printable.""" return isprint(charcode)