def __init__(self, parent = None): super().__init__(parent), self.mUi = Ui_NewMapDialog() self.mUi.setupUi(self) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # Restore previously used settings prefs = preferences.Preferences.instance() s = prefs.settings() orientation = s.value(ORIENTATION_KEY, 0) mapWidth = s.value(MAP_WIDTH_KEY, 100) mapHeight = s.value(MAP_HEIGHT_KEY, 100) tileWidth = s.value(TILE_WIDTH_KEY, 32) tileHeight = s.value(TILE_HEIGHT_KEY, 32) self.mUi.layerFormat.addItem(QCoreApplication.translate("PreferencesDialog", "XML")) self.mUi.layerFormat.addItem(QCoreApplication.translate("PreferencesDialog", "Base64 (uncompressed)")) self.mUi.layerFormat.addItem(QCoreApplication.translate("PreferencesDialog", "Base64 (gzip compressed)")) self.mUi.layerFormat.addItem(QCoreApplication.translate("PreferencesDialog", "Base64 (zlib compressed)")) self.mUi.layerFormat.addItem(QCoreApplication.translate("PreferencesDialog", "CSV")) self.mUi.renderOrder.addItem(QCoreApplication.translate("PreferencesDialog", "Right Down")) self.mUi.renderOrder.addItem(QCoreApplication.translate("PreferencesDialog", "Right Up")) self.mUi.renderOrder.addItem(QCoreApplication.translate("PreferencesDialog", "Left Down")) self.mUi.renderOrder.addItem(QCoreApplication.translate("PreferencesDialog", "Left Up")) self.mUi.orientation.addItem(self.tr("Orthogonal"), Map.Orientation.Orthogonal) self.mUi.orientation.addItem(self.tr("Isometric"), Map.Orientation.Isometric) self.mUi.orientation.addItem(self.tr("Isometric (Staggered)"), Map.Orientation.Staggered) self.mUi.orientation.addItem(self.tr("Hexagonal (Staggered)"), Map.Orientation.Hexagonal) self.mUi.orientation.setCurrentIndex(orientation) self.mUi.layerFormat.setCurrentIndex(prefs.layerDataFormat().value) self.mUi.renderOrder.setCurrentIndex(prefs.mapRenderOrder().value) self.mUi.mapWidth.setValue(mapWidth) self.mUi.mapHeight.setValue(mapHeight) self.mUi.tileWidth.setValue(tileWidth) self.mUi.tileHeight.setValue(tileHeight) # Make the font of the pixel size label smaller font = self.mUi.pixelSizeLabel.font() size = QFontInfo(font).pointSizeF() font.setPointSizeF(size - 1) self.mUi.pixelSizeLabel.setFont(font) self.mUi.mapWidth.valueChanged.connect(self.refreshPixelSize) self.mUi.mapHeight.valueChanged.connect(self.refreshPixelSize) self.mUi.tileWidth.valueChanged.connect(self.refreshPixelSize) self.mUi.tileHeight.valueChanged.connect(self.refreshPixelSize) self.mUi.orientation.currentIndexChanged.connect(self.refreshPixelSize) self.refreshPixelSize()
def doubleClickedCB(self, model_index): """Double click handler for the property table""" # Get translation object _ = get_app()._tr # Get data model and selection model = self.clip_properties_model.model row = model_index.row() selected_label = model.item(row, 0) self.selected_item = model.item(row, 1) if selected_label: cur_property = selected_label.data() property_type = cur_property[1]["type"] if property_type == "color": # Get current value of color red = cur_property[1]["red"]["value"] green = cur_property[1]["green"]["value"] blue = cur_property[1]["blue"]["value"] # Show color dialog currentColor = QColor(red, green, blue) log.debug("Launching ColorPicker for %s", currentColor.name()) ColorPicker( currentColor, parent=self, title=_("Select a Color"), callback=self.color_callback) return elif property_type == "font": # Get font from user current_font_name = cur_property[1].get("memo", "sans") current_font = QFont(current_font_name) font, ok = QFontDialog.getFont(current_font, caption=("Change Font")) # Update font if ok and font: fontinfo = QFontInfo(font) # TODO: pass font details to value_updated so we can set multiple values font_details = { "font_family": fontinfo.family(), "font_style": fontinfo.styleName(), "font_weight": fontinfo.weight(), "font_size_pixel": fontinfo.pixelSize() } self.clip_properties_model.value_updated(self.selected_item, value=fontinfo.family())
def _get_monospace_font_impl(small=False) -> QFont: begun = time.monotonic() multiplier = 0.8 if small else 1.0 min_font_size = min(7, QFont().pointSize()) preferred = [ 'Consolas', 'DejaVu Sans Mono', 'Monospace', 'Lucida Console', 'Monaco' ] for name in preferred: font = QFont(name) if QFontInfo(font).fixedPitch(): font.setPointSize( round(max(min_font_size, QFont().pointSize() * multiplier))) _logger.info('Selected monospace font (%.6f seconds): %r', time.monotonic() - begun, font.toString()) return font font = QFont() font.setStyleHint(QFont().Monospace) font.setFamily('monospace') _logger.info('Using fallback monospace font (%.6f seconds): %r', time.monotonic() - begun, font.toString()) return font
def exportSource(self): """ Public method performing the export. """ filename = self._getFileName(self.tr("RTF Files (*.rtf)")) if not filename: return try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() self.editor.recolor(0, -1) lex = self.editor.getLexer() tabSize = Preferences.getEditor("TabWidth") if tabSize == 0: tabSize = 4 wysiwyg = Preferences.getEditorExporter("RTF/WYSIWYG") if wysiwyg: if lex: defaultFont = lex.font(QsciScintilla.STYLE_DEFAULT) else: defaultFont = Preferences.getEditorOtherFonts( "DefaultFont") else: defaultFont = Preferences.getEditorExporter("RTF/Font") fontface = defaultFont.family() fontsize = QFontInfo(defaultFont).pointSize() << 1 if fontsize == 0: fontsize = 10 << 1 characterset = QsciScintilla.SC_CHARSET_DEFAULT tabs = Preferences.getEditorExporter("RTF/UseTabs") if lex: fgColour = lex.color(QsciScintilla.STYLE_DEFAULT) bgColour = lex.paper(QsciScintilla.STYLE_DEFAULT) else: fgColour = self.editor.color() bgColour = self.editor.paper() try: f = open(filename, "w", encoding="utf-8") styles = {} fonts = {} colors = {} lastStyle = "" f.write(self.RTF_HEADEROPEN + self.RTF_FONTDEFOPEN) fonts[0] = fontface fontCount = 1 f.write(self.RTF_FONTDEF.format(0, characterset, fontface)) colors[0] = fgColour colors[1] = bgColour colorCount = 2 if lex: istyle = 0 while istyle <= QsciScintilla.STYLE_MAX: if (istyle < QsciScintilla.STYLE_DEFAULT or istyle > QsciScintilla.STYLE_LASTPREDEFINED): if lex.description(istyle): font = lex.font(istyle) if wysiwyg: fontKey = None for key, value in list(fonts.items()): if value.lower() == \ font.family().lower(): fontKey = key break if fontKey is None: fonts[fontCount] = font.family() f.write( self.RTF_FONTDEF.format( fontCount, characterset, font.family())) fontKey = fontCount fontCount += 1 lastStyle = self.RTF_SETFONTFACE + \ "{0:d}".format(fontKey) else: lastStyle = self.RTF_SETFONTFACE + "0" if wysiwyg and QFontInfo(font).pointSize(): lastStyle += self.RTF_SETFONTSIZE + \ "{0:d}".format( QFontInfo(font).pointSize() << 1) else: lastStyle += self.RTF_SETFONTSIZE + \ "{0:d}".format(fontsize) sColour = lex.color(istyle) sColourKey = None for key, value in list(colors.items()): if value == sColour: sColourKey = key break if sColourKey is None: colors[colorCount] = sColour sColourKey = colorCount colorCount += 1 lastStyle += self.RTF_SETCOLOR + \ "{0:d}".format(sColourKey) sColour = lex.paper(istyle) sColourKey = None for key, value in list(colors.items()): if value == sColour: sColourKey = key break if sColourKey is None: colors[colorCount] = sColour sColourKey = colorCount colorCount += 1 lastStyle += self.RTF_SETBACKGROUND + \ "{0:d}".format(sColourKey) if font.bold(): lastStyle += self.RTF_BOLD_ON else: lastStyle += self.RTF_BOLD_OFF if font.italic(): lastStyle += self.RTF_ITALIC_ON else: lastStyle += self.RTF_ITALIC_OFF styles[istyle] = lastStyle else: styles[istyle] = \ self.RTF_SETFONTFACE + "0" + \ self.RTF_SETFONTSIZE + \ "{0:d}".format(fontsize) + \ self.RTF_SETCOLOR + "0" + \ self.RTF_SETBACKGROUND + "1" + \ self.RTF_BOLD_OFF + self.RTF_ITALIC_OFF istyle += 1 else: styles[0] = self.RTF_SETFONTFACE + "0" + \ self.RTF_SETFONTSIZE + \ "{0:d}".format(fontsize) + \ self.RTF_SETCOLOR + "0" + \ self.RTF_SETBACKGROUND + "1" + \ self.RTF_BOLD_OFF + self.RTF_ITALIC_OFF f.write(self.RTF_FONTDEFCLOSE + self.RTF_COLORDEFOPEN) for value in list(colors.values()): f.write( self.RTF_COLORDEF.format(value.red(), value.green(), value.blue())) f.write(self.RTF_COLORDEFCLOSE) f.write(self.RTF_INFOOPEN + self.RTF_COMMENT) f.write(time.strftime(self.RTF_CREATED)) f.write(self.RTF_INFOCLOSE) f.write(self.RTF_HEADERCLOSE + self.RTF_BODYOPEN + self.RTF_SETFONTFACE + "0" + self.RTF_SETFONTSIZE + "{0:d}".format(fontsize) + self.RTF_SETCOLOR + "0 ") lastStyle = self.RTF_SETFONTFACE + "0" + \ self.RTF_SETFONTSIZE + "{0:d}".format(fontsize) + \ self.RTF_SETCOLOR + "0" + \ self.RTF_SETBACKGROUND + "1" + \ self.RTF_BOLD_OFF + self.RTF_ITALIC_OFF lengthDoc = self.editor.length() prevCR = False column = 0 pos = 0 deltaStyle = "" styleCurrent = -1 utf8 = self.editor.isUtf8() utf8Ch = b"" utf8Len = 0 while pos < lengthDoc: ch = self.editor.byteAt(pos) style = self.editor.styleAt(pos) if style != styleCurrent: deltaStyle = self.__GetRTFStyleChange( lastStyle, styles[style]) if deltaStyle: f.write(deltaStyle) styleCurrent = style lastStyle = styles[style] if ch == b'{': f.write('\\{') elif ch == b'}': f.write('\\}') elif ch == b'\\': f.write('\\\\') elif ch == b'\t': if tabs: f.write(self.RTF_TAB) else: ts = tabSize - (column % tabSize) f.write(' ' * ts) column += ts - 1 elif ch == b'\n': if not prevCR: f.write(self.RTF_EOLN) column -= 1 elif ch == b'\r': f.write(self.RTF_EOLN) column -= 1 else: if ord(ch) > 0x7F and utf8: utf8Ch += ch if utf8Len == 0: if (utf8Ch[0] & 0xF0) == 0xF0: utf8Len = 4 elif (utf8Ch[0] & 0xE0) == 0xE0: utf8Len = 3 elif (utf8Ch[0] & 0xC0) == 0xC0: utf8Len = 2 column -= 1 # will be incremented again later elif len(utf8Ch) == utf8Len: ch = utf8Ch.decode('utf8') if ord(ch) <= 0xff: f.write("\\'{0:x}".format(ord(ch))) else: f.write("\\u{0:d}\\'{1:x}".format( ord(ch), ord(ch) & 0xFF)) utf8Ch = b"" utf8Len = 0 else: column -= 1 # will be incremented again later else: f.write(ch.decode()) column += 1 prevCR = ch == b'\r' pos += 1 f.write(self.RTF_BODYCLOSE) f.close() except IOError as err: QApplication.restoreOverrideCursor() E5MessageBox.critical( self.editor, self.tr("Export source"), self.tr("""<p>The source could not be exported to""" """ <b>{0}</b>.</p><p>Reason: {1}</p>""").format( filename, str(err))) finally: QApplication.restoreOverrideCursor()
def get_font_display_str(self): info = QFontInfo(self.font) info_str = f"{info.pointSize()}pt {info.family()}" return info_str
def fontPrev(self): # Update font preview f = self.fontComboBox.currentFont() self.font.setFamily(QFontInfo(f).family()) self.font.setPointSize(self.fontSize.value()) self.fontPreview.setFont(self.font)
def __init__(self): """ Initialise the page. """ super().__init__() self._project = None # Create the page's GUI. layout = QGridLayout() form = BetterForm() self._name_edit = QLineEdit( placeholderText="Application name", whatsThis="The name of the application. It will default to " "the base name of the application script without any " "extension.", textEdited=self._name_changed) form.addRow("Name", self._name_edit) self._script_edit = FilenameEditor( "Application Script", placeholderText="Application script", whatsThis="The name of the application's optional main script " "file.", textEdited=self._script_changed) form.addRow("Main script file", self._script_edit) self._entry_point_edit = QLineEdit( placeholderText="Entry point in application package", whatsThis="The name of the optional entry point in the " "application's package.", textEdited=self._entry_point_changed) form.addRow("Entry point", self._entry_point_edit) self._sys_path_edit = QLineEdit( placeholderText="Additional sys.path directories", whatsThis="A space separated list of additional directories, " "ZIP files and eggs to add to <tt>sys.path</tt>. Only " "set this if you want to allow external packages to " "be imported.", textEdited=self._sys_path_changed) form.addRow("sys.path", self._sys_path_edit) layout.addLayout(form, 0, 0) options_layout = BetterForm() self._console_edit = QCheckBox( "Use console (Windows)", whatsThis="Enable console output for Windows applications. " "Console output will be enabled automatically if no " "graphical PyQt modules are used.", stateChanged=self._console_changed) options_layout.addRow(self._console_edit) self._bundle_edit = QCheckBox( "Application bundle (macOS)", whatsThis="Build an application bundle on macOS. If it is not " "checked then the application will be built as a " "simple executable.", stateChanged=self._bundle_changed) options_layout.addRow(self._bundle_edit) layout.addLayout(options_layout, 0, 1) # Extra space is needed before the application package editor. layout.setRowMinimumHeight( 1, 1.4 * QFontInfo(QGuiApplication.font()).pixelSize()) self._package_edit = _ApplicationPackageEditor() self._package_edit.package_changed.connect(self._package_changed) package_edit_gb = QGroupBox(self._package_edit.title) package_edit_gb.setFlat(True) package_edit_gb.setLayout(self._package_edit) layout.addWidget(package_edit_gb, 2, 0, 1, 2) qmake = CollapsibleWidget("Additional qmake Configuration") self._qmake_edit = QPlainTextEdit( whatsThis="Any text entered here will be appended to the " "generated <tt>.pro</tt> that will be processed by " "<tt>qmake</tt>.", textChanged=self._qmake_changed) qmake.setWidget(self._qmake_edit) layout.addWidget(qmake, 3, 0, 1, 2) self.setLayout(layout)
def generate(self, tabSize=4, useTabs=False, wysiwyg=True, folding=False, onlyStylesUsed=False, titleFullPath=False): """ Public method to generate HTML for the source editor. @keyparam tabSize size of tabs (integer) @keyparam useTabs flag indicating the use of tab characters (boolean) @keyparam wysiwyg flag indicating colorization (boolean) @keyparam folding flag indicating usage of fold markers @keyparam onlyStylesUsed flag indicating to include only style definitions for styles used in the source (boolean) @keyparam titleFullPath flag indicating to include the full file path in the title tag (boolean) @return generated HTML text (string) """ self.editor.recolor(0, -1) lengthDoc = self.editor.length() styleIsUsed = {} if onlyStylesUsed: for index in range(QsciScintilla.STYLE_MAX + 1): styleIsUsed[index] = False # check the used styles pos = 0 while pos < lengthDoc: styleIsUsed[self.editor.styleAt(pos) & 0x7F] = True pos += 1 else: for index in range(QsciScintilla.STYLE_MAX + 1): styleIsUsed[index] = True styleIsUsed[QsciScintilla.STYLE_DEFAULT] = True html = \ '''<!DOCTYPE html PUBLIC "-//W3C//DTD''' \ ''' XHTML 1.0 Transitional//EN"\n''' \ ''' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">''' \ '''\n''' \ '''<html xmlns="http://www.w3.org/1999/xhtml">\n''' \ '''<head>\n''' if titleFullPath: html += '''<title>{0}</title>\n'''.format( self.editor.getFileName()) else: html += '''<title>{0}</title>\n'''.format( os.path.basename(self.editor.getFileName())) html += '''<meta name="Generator" content="eric6" />\n''' \ '''<meta http-equiv="Content-Type" ''' \ '''content="text/html; charset=utf-8" />\n''' if folding: html += \ '''<script language="JavaScript" type="text/javascript">\n''' \ '''<!--\n''' \ '''function symbol(id, sym) {\n''' \ ''' if (id.textContent == undefined) {\n''' \ ''' id.innerText = sym;\n''' \ ''' } else {\n''' \ ''' id.textContent = sym;\n''' \ ''' }\n''' \ '''}\n''' \ '''function toggle(id) {\n''' \ ''' var thislayer = document.getElementById('ln' + id);\n''' \ ''' id -= 1;\n''' \ ''' var togline = document.getElementById('hd' + id);\n''' \ ''' var togsym = document.getElementById('bt' + id);\n''' \ ''' if (thislayer.style.display == 'none') {\n''' \ ''' thislayer.style.display = 'block';\n''' \ ''' togline.style.textDecoration = 'none';\n''' \ ''' symbol(togsym, '- ');\n''' \ ''' } else {\n''' \ ''' thislayer.style.display = 'none';\n''' \ ''' togline.style.textDecoration = 'underline';\n''' \ ''' symbol(togsym, '+ ');\n''' \ ''' }\n''' \ '''}\n''' \ '''//-->\n''' \ '''</script>\n''' lex = self.editor.getLexer() if lex: bgColour = lex.paper(QsciScintilla.STYLE_DEFAULT).name() else: bgColour = self.editor.paper().name() html += '''<style type="text/css">\n''' if lex: istyle = 0 while istyle <= QsciScintilla.STYLE_MAX: if (istyle <= QsciScintilla.STYLE_DEFAULT or istyle > QsciScintilla.STYLE_LASTPREDEFINED) and \ styleIsUsed[istyle]: if lex.description(istyle) or \ istyle == QsciScintilla.STYLE_DEFAULT: font = lex.font(istyle) colour = lex.color(istyle) paper = lex.paper(istyle) if istyle == QsciScintilla.STYLE_DEFAULT: html += '''span {\n''' else: html += '''.S{0:d} {{\n'''.format(istyle) if font.italic(): html += ''' font-style: italic;\n''' if font.bold(): html += ''' font-weight: bold;\n''' if wysiwyg: html += ''' font-family: '{0}';\n'''.format( font.family()) html += ''' color: {0};\n'''.format(colour.name()) if istyle != QsciScintilla.STYLE_DEFAULT and \ bgColour != paper.name(): html += ''' background: {0};\n'''.format( paper.name()) html += ''' text-decoration: inherit;\n''' if wysiwyg: html += ''' font-size: {0:d}pt;\n'''.format( QFontInfo(font).pointSize()) html += '''}\n''' else: styleIsUsed[istyle] = False istyle += 1 else: colour = self.editor.color() paper = self.editor.paper() font = Preferences.getEditorOtherFonts("DefaultFont") html += '''.S0 {\n''' if font.italic(): html += ''' font-style: italic;\n''' if font.bold(): html += ''' font-weight: bold;\n''' if wysiwyg: html += ''' font-family: '{0}';\n'''.format(font.family()) html += ''' color: {0};\n'''.format(colour.name()) if bgColour != paper.name(): html += ''' background: {0};\n'''.format(paper.name()) html += ''' text-decoration: inherit;\n''' if wysiwyg: html += ''' font-size: {0:d}pt;\n'''.format( QFontInfo(font).pointSize()) html += '''}\n''' html += '''</style>\n''' html += '''</head>\n''' html += '''<body bgcolor="{0}">\n'''.format(bgColour) line = self.editor.lineAt(0) level = self.editor.foldLevelAt(line) - QsciScintilla.SC_FOLDLEVELBASE levelStack = [level] styleCurrent = self.editor.styleAt(0) inStyleSpan = False inFoldSpan = False # Global span for default attributes if wysiwyg: html += '''<span>''' else: html += '''<pre>''' if folding: if self.editor.foldFlagsAt(line) & \ QsciScintilla.SC_FOLDLEVELHEADERFLAG: html += '''<span id="hd{0:d}" onclick="toggle('{1:d}')">'''\ .format(line, line + 1) html += '''<span id="bt{0:d}">- </span>'''.format(line) inFoldSpan = True else: html += ''' ''' if styleIsUsed[styleCurrent]: html += '''<span class="S{0:0d}">'''.format(styleCurrent) inStyleSpan = True column = 0 pos = 0 utf8 = self.editor.isUtf8() utf8Ch = b"" utf8Len = 0 while pos < lengthDoc: ch = self.editor.byteAt(pos) style = self.editor.styleAt(pos) if style != styleCurrent: if inStyleSpan: html += '''</span>''' inStyleSpan = False if ch not in [b'\r', b'\n']: # no need of a span for the EOL if styleIsUsed[style]: html += '''<span class="S{0:d}">'''.format(style) inStyleSpan = True styleCurrent = style if ch == b' ': if wysiwyg: prevCh = b'' if column == 0: # at start of line, must put a # because regular space will be collapsed prevCh = b' ' while pos < lengthDoc and self.editor.byteAt(pos) == b' ': if prevCh != b' ': html += ' ' else: html += ''' ''' prevCh = self.editor.byteAt(pos) pos += 1 column += 1 pos -= 1 # the last incrementation will be done by the outer loop else: html += ' ' column += 1 elif ch == b'\t': ts = tabSize - (column % tabSize) if wysiwyg: html += ''' ''' * ts column += ts else: if useTabs: html += '\t' column += 1 else: html += ' ' * ts column += ts elif ch in [b'\r', b'\n']: if inStyleSpan: html += '''</span>''' inStyleSpan = False if inFoldSpan: html += '''</span>''' inFoldSpan = False if ch == b'\r' and self.editor.byteAt(pos + 1) == b'\n': pos += 1 # CR+LF line ending, skip the "extra" EOL char column = 0 if wysiwyg: html += '''<br />''' styleCurrent = self.editor.styleAt(pos + 1) if folding: line = self.editor.lineAt(pos + 1) newLevel = self.editor.foldLevelAt(line) if newLevel < level: while levelStack[-1] > newLevel: html += '''</span>''' levelStack.pop() html += '\n' # here to get clean code if newLevel > level: html += '''<span id="ln{0:d}">'''.format(line) levelStack.append(newLevel) if self.editor.foldFlagsAt(line) & \ QsciScintilla.SC_FOLDLEVELHEADERFLAG: html += \ '''<span id="hd{0:d}"''' \ ''' onclick="toggle('{1:d}')">''' \ .format(line, line + 1) html += '''<span id="bt{0:d}">- </span>'''.format(line) inFoldSpan = True else: html += ''' ''' level = newLevel else: html += '\n' if styleIsUsed[styleCurrent] and \ self.editor.byteAt(pos + 1) not in [b'\r', b'\n']: # We know it's the correct next style, # but no (empty) span for an empty line html += '''<span class="S{0:0d}">'''.format(styleCurrent) inStyleSpan = True else: if ch == b'<': html += '''<''' elif ch == b'>': html += '''>''' elif ch == b'&': html += '''&''' else: if ord(ch) > 127 and utf8: utf8Ch += ch if utf8Len == 0: if (utf8Ch[0] & 0xF0) == 0xF0: utf8Len = 4 elif (utf8Ch[0] & 0xE0) == 0xE0: utf8Len = 3 elif (utf8Ch[0] & 0xC0) == 0xC0: utf8Len = 2 column -= 1 # will be incremented again later elif len(utf8Ch) == utf8Len: ch = utf8Ch.decode('utf8') html += Utilities.html_encode(ch) utf8Ch = b"" utf8Len = 0 else: column -= 1 # will be incremented again later else: html += ch.decode() column += 1 pos += 1 if inStyleSpan: html += '''</span>''' if folding: while levelStack: html += '''</span>''' levelStack.pop() if wysiwyg: html += '''</span>''' else: html += '''</pre>''' html += '''</body>\n</html>\n''' return html
def get_default_font_family(self): font = QFont('Monospace') font.setStyleHint(QFont.Monospace) return QFontInfo(font).family()
def _doBuild(self, bldObj, isPreview=False, doConvert=True): """Rund the build with a specific build object. """ tStart = int(time()) # Get Settings fmtTitle = self.fmtTitle.text() fmtChapter = self.fmtChapter.text() fmtUnnumbered = self.fmtUnnumbered.text() fmtScene = self.fmtScene.text() fmtSection = self.fmtSection.text() buildLang = self.buildLang.currentData() hideScene = self.hideScene.isChecked() hideSection = self.hideSection.isChecked() textFont = self.textFont.text() textSize = self.textSize.value() lineHeight = self.lineHeight.value() justifyText = self.justifyText.isChecked() noStyling = self.noStyling.isChecked() incSynopsis = self.includeSynopsis.isChecked() incComments = self.includeComments.isChecked() incKeywords = self.includeKeywords.isChecked() novelFiles = self.novelFiles.isChecked() noteFiles = self.noteFiles.isChecked() ignoreFlag = self.ignoreFlag.isChecked() includeBody = self.includeBody.isChecked() replaceUCode = self.replaceUCode.isChecked() # The language lookup dict is reloaded if needed self.theProject.setProjectLang(buildLang) # Get font information fontInfo = QFontInfo(QFont(textFont, textSize)) textFixed = fontInfo.fixedPitch() isHtml = isinstance(bldObj, ToHtml) isOdt = isinstance(bldObj, ToOdt) bldObj.setTitleFormat(fmtTitle) bldObj.setChapterFormat(fmtChapter) bldObj.setUnNumberedFormat(fmtUnnumbered) bldObj.setSceneFormat(fmtScene, hideScene) bldObj.setSectionFormat(fmtSection, hideSection) bldObj.setFont(textFont, textSize, textFixed) bldObj.setJustify(justifyText) bldObj.setLineHeight(lineHeight) bldObj.setSynopsis(incSynopsis) bldObj.setComments(incComments) bldObj.setKeywords(incKeywords) bldObj.setBodyText(includeBody) if isHtml: bldObj.setStyles(not noStyling) bldObj.setReplaceUnicode(replaceUCode) if isOdt: bldObj.setColourHeaders(not noStyling) bldObj.setLanguage(buildLang) bldObj.initDocument() # Make sure the project and document is up to date self.mainGui.saveDocument() self.buildProgress.setMaximum(len(self.theProject.tree)) self.buildProgress.setValue(0) for nItt, tItem in enumerate(self.theProject.tree): noteRoot = noteFiles noteRoot &= tItem.itemType == nwItemType.ROOT noteRoot &= tItem.itemClass != nwItemClass.NOVEL noteRoot &= tItem.itemClass != nwItemClass.ARCHIVE try: if noteRoot: # Add headers for root folders of notes bldObj.addRootHeading(tItem.itemHandle) if doConvert: bldObj.doConvert() elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag): bldObj.setText(tItem.itemHandle) bldObj.doPreProcessing() bldObj.tokenizeText() bldObj.doHeaders() if doConvert: bldObj.doConvert() bldObj.doPostProcessing() except Exception: logger.error("Failed to build document '%s'", tItem.itemHandle) logException() if isPreview: self.docView.setText(( "Failed to generate preview. " "Document with title '%s' could not be parsed." ) % tItem.itemName) return False # Update progress bar, also for skipped items self.buildProgress.setValue(nItt+1) if isOdt: bldObj.closeDocument() tEnd = int(time()) logger.debug("Built project in %.3f ms", 1000*(tEnd - tStart)) if bldObj.errData: self.mainGui.makeAlert([ self.tr("There were problems when building the project:") ] + bldObj.errData, nwAlert.ERROR) return
def paint( self, painter: QPainter, option: QStyleOptionViewItem, idx: QModelIndex, ) -> None: font_family = idx.data(Qt.ItemDataRole.DisplayRole) font = QFont(option.font) font.setPointSize(QFontInfo(font).pointSize() * 3 // 2) font2 = QFont(font) font2.setFamily(font_family) if option.state & QStyle.StateFlag.State_Selected: painter.save() painter.setBrush(option.palette.highlight()) painter.setPen(Qt.PenStyle.NoPen) painter.drawRect(option.rect) painter.setPen(QPen(option.palette.highlightedText(), 0)) icon = self.bitmap if QFontDatabase().isSmoothlyScalable(font_family): icon = self.truetype actual_size = icon.actualSize(option.rect.size()) icon.paint( painter, option.rect, Qt.AlignmentFlag(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter), ) if option.direction == Qt.LayoutDirection.RightToLeft: option.rect.setRight(option.rect.right() - actual_size.width() - 4) else: option.rect.setLeft(option.rect.left() + actual_size.width() + 4) half1 = QRect(option.rect) half2 = QRect(option.rect) half1.setRight(half1.right() // 2) half2.setLeft(half1.right()) painter.drawText( half1, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeading | Qt.TextFlag.TextSingleLine, font_family, ) old = painter.font() painter.setFont(font2) painter.drawText( half2, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeading | Qt.TextFlag.TextSingleLine, self.sample_text, ) painter.setFont(old) if option.state & QStyle.StateFlag.State_Selected: painter.restore()
def reportCodeViewerProperties(self, font): wposition = self.pos() font_info = QFontInfo(font) print("---------- Code Viewer Report ----------") print("Size: width: %d, height: %d" % (self.width, self.height)) print("Top-Left position in screen: (%d, %d)" % (wposition.x(), wposition.y())) print("Top-Right position in screen: (%d, %d)" % (wposition.x() + self.width, wposition.y())) print("Bottom-Left position in screen: (%d, %d)" % (wposition.x(), wposition.y() + self.height)) print("Bottom-Right position in screen: (%d, %d)" % (wposition.x() + self.width, wposition.y() + self.height)) print("---------- Side Bar Report ----------") print("Size: width: %d, height: %d" % (self.listWidth, self.editorHeight)) print("Top-Left position in screen: (%d, %d)" % (wposition.x(), wposition.y())) print("Top-Right position in screen: (%d, %d)" % (wposition.x() + self.listWidth, wposition.y())) print("Bottom-Left position in screen: (%d, %d)" % (wposition.x(), wposition.y() + self.editorHeight)) print("Bottom-Right position in screen: (%d, %d)" % (wposition.x() + self.listWidth, wposition.y() + self.editorHeight)) print("---------- Code Editor Report ----------") print("Size: width: %d, height: %d" % (self.editorWidth, self.editorHeight)) print("Margins: %f pixels" % self.editor.document().documentMargin()) print("Top-Left position in screen: (%d, %d)" % (wposition.x() + self.listWidth, wposition.y())) print( "Top-Right position in screen: (%d, %d)" % (wposition.x() + self.listWidth + self.editorWidth, wposition.y())) print("Bottom-Left position in screen: (%d, %d)" % (wposition.x() + self.listWidth, wposition.y() + self.editorHeight)) print("Bottom-Right position in screen: (%d, %d)" % (wposition.x() + self.listWidth + self.editorWidth, wposition.y() + self.editorHeight)) print("Padding: left: %d, top: %d, bottom: %d, right: %d" % (self.padding_left, self.padding_top, self.padding_bottom, self.padding_right)) print("---------- Font report (font) ----------") print("font family: %s" % font.family()) print("font pixelSize: %f" % font.pixelSize()) print("font pointSize: %f" % font.pointSize()) print("font pointSizeF: %f" % font.pointSizeF()) print("QFont.pixelSize(): %f" % font.pixelSize()) print("---------- QFontInfo (font) ----------") print("family(): %s" % font_info.family()) print("pixelSize(): %f" % font_info.pixelSize()) print("pointSize(): %f" % font_info.pointSize()) print("pointSizeF(): %f" % font_info.pointSizeF()) print("QFontInfo.pixelSize(): %d" % font_info.pixelSize()) print("---------- QFontMetrics (font) ---------") print("QFontMetrics.lineSpacing(): %f" % QFontMetrics(font).lineSpacing()) print("QFontMetrics.leading(): %f" % QFontMetrics(font).leading()) print("QFontMetrics.height(): %f" % QFontMetrics(font).height()) print("---------- Eye tracking area ----------") x1 = wposition.x() + self.listWidth + self.padding_left y1 = wposition.y() + self.padding_top x2 = wposition.x( ) + self.listWidth + self.editorWidth - self.padding_right y2 = wposition.y() + self.editorHeight - self.padding_bottom self.editor.x_offset = x1 # For the eye tracker, as it uses the entire screen self.editor.y_offset = y1 # For the eye tracker, as it uses the entire screen self.editor.most_right_x = x2 self.editor.most_bottom_y = y2 print("Size: width: %d, height: %d" % (x2 - x1, y2 - y1)) print("Top-Left position in screen: (%d, %d)" % (x1, y1)) print("Top-Right position in screen: (%d, %d)" % (x2, y1)) print("Bottom-Left position in screen: (%d, %d)" % (x1, y2)) print("Bottom-Right position in screen: (%d, %d)" % (x2, y2)) print("x_offset = %d" % x1) print("y_offset = %d" % y1) print("---------- Status Bar Report ----------") print("Size: width: %d, height: %d" % (self.width, self.status_bar_height)) print("Top-Left position in screen: (%d, %d)" % (wposition.x(), wposition.y() + self.editorHeight)) print("Top-Right position in screen: (%d, %d)" % (wposition.x() + self.width, wposition.y() + self.editorHeight)) print("Bottom-Left position in screen: (%d, %d)" % (wposition.x(), wposition.y() + self.editorHeight + self.status_bar_height)) print("Bottom-Right position in screen: (%d, %d)" % (wposition.x() + self.width, wposition.y() + self.editorHeight + self.status_bar_height)) print("----------------------------------------")
def contextMenuEvent(self, event): """ Display context menu """ # Get property being acted on index = self.indexAt(event.pos()) if not index.isValid(): event.ignore() return # Get data model and selection idx = self.indexAt(event.pos()) row = idx.row() selected_label = idx.model().item(row, 0) selected_value = idx.model().item(row, 1) self.selected_item = selected_value frame_number = self.clip_properties_model.frame_number # Get translation object _ = get_app()._tr # If item selected if selected_label: # Get data from selected item cur_property = selected_label.data() property_name = cur_property[1]["name"] self.property_type = cur_property[1]["type"] points = cur_property[1]["points"] self.choices = cur_property[1]["choices"] property_key = cur_property[0] clip_id, item_type = selected_value.data() log.info("Context menu shown for %s (%s) for clip %s on frame %s" % (property_name, property_key, clip_id, frame_number)) log.info("Points: %s" % points) # Clear menu if models updated if self.menu_reset: self.choices = [] self.menu_reset = False # Handle parent effect options if property_key == "parent_effect_id" and not self.choices: clip_choices = [{ "name": "None", "value": "None", "selected": False, "icon": QIcon() }] # Instantiate the timeline timeline_instance = get_app().window.timeline_sync.timeline # Instantiate this effect effect = timeline_instance.GetClipEffect(clip_id) effect_json = json.loads(effect.Json()) # Loop through timeline's clips for clip_instance in timeline_instance.Clips(): clip_instance_id = clip_instance.Id() # Avoid parent a clip effect to it's own effect if (clip_instance_id != effect.ParentClipId()): # Clip's propertyJSON data clip_instance_data = Clip.get(id = clip_instance_id).data # Path to the clip file clip_instance_path = clip_instance_data["reader"]["path"] # Iterate through all clip files on the timeline for clip_number in range(self.files_model.rowCount()): clip_index = self.files_model.index(clip_number, 0) clip_name = clip_index.sibling(clip_number, 1).data() clip_path = os.path.join(clip_index.sibling(clip_number, 4).data(), clip_name) # Check if the timeline's clip file name matches the clip the user selected if (clip_path == clip_instance_path): # Generate the clip icon to show in the selection menu clip_instance_icon = clip_index.data(Qt.DecorationRole) effect_choices = [{"name": "None", "value": "None", "selected": False, "icon": QIcon()}] # Iterate through clip's effects for effect_data in clip_instance_data["effects"]: # Make sure the user can only set a parent effect of the same type as this effect if effect_data['class_name'] == effect_json['class_name']: effect_id = effect_data["id"] effect_name = effect_data['class_name'] effect_icon = QIcon(QPixmap(os.path.join(info.PATH, "effects", "icons", "%s.png" % effect_data['class_name'].lower()))) effect_choices.append({"name": effect_id, "value": effect_id, "selected": False, "icon": effect_icon}) self.choices.append({"name": _(clip_instance_data["title"]), "value": effect_choices, "selected": False, "icon": clip_instance_icon}) # Handle selected object options (ObjectDetection effect) if property_key == "selected_object_index" and not self.choices: # Get all visible object's indexes timeline_instance = get_app().window.timeline_sync.timeline # Instantiate the effect effect = timeline_instance.GetClipEffect(clip_id) # Get the indexes and IDs of the visible objects visible_objects = json.loads(effect.GetVisibleObjects(frame_number)) # Add visible objects as choices object_index_choices = [] for object_index in visible_objects["visible_objects_index"]: object_index_choices.append({ "name": str(object_index), "value": str(object_index), "selected": False, "icon": QIcon() }) self.choices.append({"name": _("Detected Objects"), "value": object_index_choices, "selected": False, "icon": None}) # Handle property to set the Tracked Object's child clip if property_key == "child_clip_id" and not self.choices: clip_choices = [{ "name": "None", "value": "None", "selected": False, "icon": QIcon() }] # Instantiate the timeline timeline_instance = get_app().window.timeline_sync.timeline current_effect = timeline_instance.GetClipEffect(clip_id) # Loop through timeline's clips for clip_instance in timeline_instance.Clips(): clip_instance_id = clip_instance.Id() # Avoid attach a clip to it's own object if (clip_instance_id != clip_id and (current_effect and clip_instance_id != current_effect.ParentClip().Id())): # Clip's propertyJSON data clip_instance_data = Clip.get(id = clip_instance_id).data # Path to the clip file clip_instance_path = clip_instance_data["reader"]["path"] # Iterate through all clip files on the timeline for clip_number in range(self.files_model.rowCount()): clip_index = self.files_model.index(clip_number, 0) clip_name = clip_index.sibling(clip_number, 1).data() clip_path = os.path.join(clip_index.sibling(clip_number, 4).data(), clip_name) # Check if the timeline's clip file name matches the clip the user selected if (clip_path == clip_instance_path): # Generate the clip icon to show in the selection menu clip_instance_icon = clip_index.data(Qt.DecorationRole) self.choices.append({"name": clip_instance_data["title"], "value": clip_instance_id, "selected": False, "icon": clip_instance_icon}) # Handle clip attach options if property_key == "parentObjectId" and not self.choices: # Add all Clips as choices - initialize with None tracked_choices = [{ "name": "None", "value": "None", "selected": False, "icon": QIcon() }] clip_choices = [{ "name": "None", "value": "None", "selected": False, "icon": QIcon() }] # Instantiate the timeline timeline_instance = get_app().window.timeline_sync.timeline # Loop through timeline's clips for clip_instance in timeline_instance.Clips(): clip_instance_id = clip_instance.Id() # Avoid attach a clip to it's own object if (clip_instance_id != clip_id): # Clip's propertyJSON data clip_instance_data = Clip.get(id = clip_instance_id).data # Path to the clip file clip_instance_path = clip_instance_data["reader"]["path"] # Iterate through all clip files on the timeline for clip_number in range(self.files_model.rowCount()): clip_index = self.files_model.index(clip_number, 0) clip_name = clip_index.sibling(clip_number, 1).data() clip_path = os.path.join(clip_index.sibling(clip_number, 4).data(), clip_name) # Check if the timeline's clip file name matches the clip the user selected if (clip_path == clip_instance_path): # Generate the clip icon to show in the selection menu clip_instance_icon = clip_index.data(Qt.DecorationRole) clip_choices.append({"name": clip_instance_data["title"], "value": clip_instance_id, "selected": False, "icon": clip_instance_icon}) # Get the pixmap of the clip icon icon_size = 72 icon_pixmap = clip_instance_icon.pixmap(icon_size, icon_size) # Add tracked objects to the selection menu tracked_objects = [] for effect in clip_instance_data["effects"]: # Check if effect has a tracked object if effect.get("has_tracked_object"): # Instantiate the effect effect_instance = timeline_instance.GetClipEffect(effect["id"]) # Get the visible object's ids visible_objects_id = json.loads(effect_instance.GetVisibleObjects(frame_number))["visible_objects_id"] for object_id in visible_objects_id: # Get the Tracked Object properties object_properties = json.loads(timeline_instance.GetTrackedObjectValues(object_id, 0)) x1 = object_properties['x1'] y1 = object_properties['y1'] x2 = object_properties['x2'] y2 = object_properties['y2'] # Get the tracked object's icon from the clip's icon tracked_object_icon = icon_pixmap.copy(QRect(x1*icon_size, y1*icon_size, (x2-x1)*icon_size, (y2-y1)*icon_size)).scaled(icon_size, icon_size) tracked_objects.append({"name": str(object_id), "value": str(object_id), "selected": False, "icon": QIcon(tracked_object_icon)}) tracked_choices.append({"name": clip_instance_data["title"], "value": tracked_objects, "selected": False, "icon": clip_instance_icon}) self.choices.append({"name": _("Tracked Objects"), "value": tracked_choices, "selected": False, "icon": None}) self.choices.append({"name": _("Clips"), "value": clip_choices, "selected": False, "icon": None}) # Handle reader type values if self.property_type == "reader" and not self.choices: # Add all files file_choices = [] for i in range(self.files_model.rowCount()): idx = self.files_model.index(i, 0) if not idx.isValid(): continue icon = idx.data(Qt.DecorationRole) name = idx.sibling(i, 1).data() path = os.path.join(idx.sibling(i, 4).data(), name) # Append file choice file_choices.append({"name": name, "value": path, "selected": False, "icon": icon }) # Add root file choice self.choices.append({"name": _("Files"), "value": file_choices, "selected": False, icon: None}) # Add all transitions trans_choices = [] for i in range(self.transition_model.rowCount()): idx = self.transition_model.index(i, 0) if not idx.isValid(): continue icon = idx.data(Qt.DecorationRole) name = idx.sibling(i, 1).data() path = idx.sibling(i, 3).data() # Append transition choice trans_choices.append({"name": name, "value": path, "selected": False, "icon": icon }) # Add root transitions choice self.choices.append({"name": _("Transitions"), "value": trans_choices, "selected": False}) # Handle reader type values if property_name == "Track" and self.property_type == "int" and not self.choices: # Populate all display track names all_tracks = get_app().project.get("layers") display_count = len(all_tracks) for track in reversed(sorted(all_tracks, key=itemgetter('number'))): # Append track choice track_name = track.get("label") or _("Track %s") % display_count self.choices.append({"name": track_name, "value": track.get("number"), "selected": False, "icon": None}) display_count -= 1 return elif self.property_type == "font": # Get font from user current_font_name = cur_property[1].get("memo", "sans") current_font = QFont(current_font_name) font, ok = QFontDialog.getFont(current_font, caption=("Change Font")) # Update font if ok and font: fontinfo = QFontInfo(font) self.clip_properties_model.value_updated(self.selected_item, value=fontinfo.family()) # Define bezier presets bezier_presets = [ (0.250, 0.100, 0.250, 1.000, _("Ease (Default)")), (0.420, 0.000, 1.000, 1.000, _("Ease In")), (0.000, 0.000, 0.580, 1.000, _("Ease Out")), (0.420, 0.000, 0.580, 1.000, _("Ease In/Out")), (0.550, 0.085, 0.680, 0.530, _("Ease In (Quad)")), (0.550, 0.055, 0.675, 0.190, _("Ease In (Cubic)")), (0.895, 0.030, 0.685, 0.220, _("Ease In (Quart)")), (0.755, 0.050, 0.855, 0.060, _("Ease In (Quint)")), (0.470, 0.000, 0.745, 0.715, _("Ease In (Sine)")), (0.950, 0.050, 0.795, 0.035, _("Ease In (Expo)")), (0.600, 0.040, 0.980, 0.335, _("Ease In (Circ)")), (0.600, -0.280, 0.735, 0.045, _("Ease In (Back)")), (0.250, 0.460, 0.450, 0.940, _("Ease Out (Quad)")), (0.215, 0.610, 0.355, 1.000, _("Ease Out (Cubic)")), (0.165, 0.840, 0.440, 1.000, _("Ease Out (Quart)")), (0.230, 1.000, 0.320, 1.000, _("Ease Out (Quint)")), (0.390, 0.575, 0.565, 1.000, _("Ease Out (Sine)")), (0.190, 1.000, 0.220, 1.000, _("Ease Out (Expo)")), (0.075, 0.820, 0.165, 1.000, _("Ease Out (Circ)")), (0.175, 0.885, 0.320, 1.275, _("Ease Out (Back)")), (0.455, 0.030, 0.515, 0.955, _("Ease In/Out (Quad)")), (0.645, 0.045, 0.355, 1.000, _("Ease In/Out (Cubic)")), (0.770, 0.000, 0.175, 1.000, _("Ease In/Out (Quart)")), (0.860, 0.000, 0.070, 1.000, _("Ease In/Out (Quint)")), (0.445, 0.050, 0.550, 0.950, _("Ease In/Out (Sine)")), (1.000, 0.000, 0.000, 1.000, _("Ease In/Out (Expo)")), (0.785, 0.135, 0.150, 0.860, _("Ease In/Out (Circ)")), (0.680, -0.550, 0.265, 1.550, _("Ease In/Out (Back)")) ] # Add menu options for keyframes menu = QMenu(self) if self.property_type == "color": Color_Action = menu.addAction(_("Select a Color")) Color_Action.triggered.connect(functools.partial(self.Color_Picker_Triggered, cur_property)) menu.addSeparator() if points > 1: # Menu items only for multiple points Bezier_Menu = menu.addMenu(self.bezier_icon, _("Bezier")) for bezier_preset in bezier_presets: preset_action = Bezier_Menu.addAction(bezier_preset[4]) preset_action.triggered.connect(functools.partial( self.Bezier_Action_Triggered, bezier_preset)) Linear_Action = menu.addAction(self.linear_icon, _("Linear")) Linear_Action.triggered.connect(self.Linear_Action_Triggered) Constant_Action = menu.addAction(self.constant_icon, _("Constant")) Constant_Action.triggered.connect(self.Constant_Action_Triggered) menu.addSeparator() if points >= 1: # Menu items for one or more points Insert_Action = menu.addAction(_("Insert Keyframe")) Insert_Action.triggered.connect(self.Insert_Action_Triggered) Remove_Action = menu.addAction(_("Remove Keyframe")) Remove_Action.triggered.connect(self.Remove_Action_Triggered) menu.popup(event.globalPos()) # Menu for choices if not self.choices: return for choice in self.choices: if type(choice["value"]) != list: # Just add root choice item Choice_Action = menu.addAction(_(choice["name"])) Choice_Action.setData(choice["value"]) Choice_Action.triggered.connect(self.Choice_Action_Triggered) continue # Add sub-choice items (for nested choice lists) # Divide into smaller QMenus (since large lists cover the entire screen) # For example: Transitions -> 1 -> sub items SubMenu = None if choice["icon"] is not None: SubMenuRoot = menu.addMenu(choice["icon"], choice["name"]) else: SubMenuRoot = menu.addMenu(choice["name"]) SubMenuSize = 25 SubMenuNumber = 0 if len(choice["value"]) > SubMenuSize: SubMenu = SubMenuRoot.addMenu(str(SubMenuNumber)) else: SubMenu = SubMenuRoot for i, sub_choice in enumerate(choice["value"], 1): # Divide SubMenu if it's item is a list if type(sub_choice["value"]) == list: SubSubMenu = SubMenu.addMenu(sub_choice["icon"], sub_choice["name"]) for sub_sub_choice in sub_choice["value"]: Choice_Action = SubSubMenu.addAction( sub_sub_choice["icon"], sub_sub_choice["name"]) Choice_Action.setData(sub_sub_choice["value"]) Choice_Action.triggered.connect(self.Choice_Action_Triggered) else: if i % SubMenuSize == 0: SubMenuNumber += 1 SubMenu = SubMenuRoot.addMenu(str(SubMenuNumber)) Choice_Action = SubMenu.addAction( sub_choice["icon"], _(sub_choice["name"])) Choice_Action.setData(sub_choice["value"]) Choice_Action.triggered.connect(self.Choice_Action_Triggered) # Show choice menuk menu.popup(event.globalPos())
class Ui_MarkWindow(QMainWindow): # 构造方法 def __init__(self): super(Ui_MarkWindow, self).__init__() self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint) # 只显示关闭按钮 self.setupUi(self) # 初始化窗体设置 # 自动生成的代码,用来对窗体进行设置 def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(578, 418) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") # 设置图片显示列表 self.listWidget = QtWidgets.QListWidget(self.centralwidget) self.listWidget.setGeometry(QtCore.QRect(0, 0, 141, 391)) self.listWidget.setObjectName("listWidget") # 设置加载图片按钮 self.pushButton = QtWidgets.QPushButton(self.centralwidget) self.pushButton.setGeometry(QtCore.QRect(150, 0, 75, 23)) self.pushButton.setObjectName("pushButton") self.groupBox = QtWidgets.QGroupBox(self.centralwidget) self.groupBox.setGeometry(QtCore.QRect(150, 30, 421, 151)) self.groupBox.setObjectName("groupBox") # 设置文字水印单选按钮 self.radioButton = QtWidgets.QRadioButton(self.groupBox) self.radioButton.setGeometry(QtCore.QRect(20, 20, 101, 16)) self.radioButton.setChecked(True) # 默认选中 self.radioButton.setObjectName("radioButton") self.label = QtWidgets.QLabel(self.groupBox) self.label.setGeometry(QtCore.QRect(30, 50, 54, 16)) self.label.setObjectName("label") # 设置要输入水印文字的文本框 self.lineEdit = QtWidgets.QLineEdit(self.groupBox) self.lineEdit.setGeometry(QtCore.QRect(90, 50, 241, 20)) self.lineEdit.setObjectName("lineEdit") # 设置“字体设置”按钮 self.pushButton_2 = QtWidgets.QPushButton(self.groupBox) self.pushButton_2.setGeometry(QtCore.QRect(340, 50, 75, 23)) self.pushButton_2.setObjectName("pushButton_2") # 设置图片水印单选按钮 self.radioButton_2 = QtWidgets.QRadioButton(self.groupBox) self.radioButton_2.setGeometry(QtCore.QRect(20, 80, 91, 16)) self.radioButton_2.setChecked(False) # 默认不选中 self.radioButton_2.setObjectName("radioButton_2") # 设置选择水印图片按钮 self.pushButton_3 = QtWidgets.QPushButton(self.groupBox) self.pushButton_3.setGeometry(QtCore.QRect(340, 110, 75, 23)) self.pushButton_3.setObjectName("pushButton_3") self.label_2 = QtWidgets.QLabel(self.groupBox) self.label_2.setGeometry(QtCore.QRect(30, 110, 54, 16)) self.label_2.setObjectName("label_2") # 设置显示水印图片路径的文本框 self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox) self.lineEdit_2.setGeometry(QtCore.QRect(90, 110, 241, 20)) self.lineEdit_2.setObjectName("lineEdit_2") self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget) self.groupBox_2.setGeometry(QtCore.QRect(150, 190, 421, 71)) self.groupBox_2.setObjectName("groupBox_2") self.label_3 = QtWidgets.QLabel(self.groupBox_2) self.label_3.setGeometry(QtCore.QRect(270, 31, 54, 21)) self.label_3.setObjectName("label_3") # 设置水印位置选择框 self.comboBox = QtWidgets.QComboBox(self.groupBox_2) self.comboBox.setGeometry(QtCore.QRect(330, 30, 71, 22)) self.comboBox.setObjectName("comboBox") self.comboBox.addItem('左上角') self.comboBox.addItem('右上角') self.comboBox.addItem('左下角') self.comboBox.addItem('右下角') self.comboBox.addItem('居中位置') self.comboBox.setCurrentIndex(0) # 设置默认选择第一项 self.label_4 = QtWidgets.QLabel(self.groupBox_2) self.label_4.setGeometry(QtCore.QRect(20, 30, 51, 21)) self.label_4.setObjectName("label_4") # 设置水印透明度的滑动条 self.horizontalSlider = QtWidgets.QSlider(self.groupBox_2) self.horizontalSlider.setGeometry(QtCore.QRect(70, 30, 181, 22)) self.horizontalSlider.setMinimum(1) self.horizontalSlider.setMaximum(10) self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider.setObjectName("horizontalSlider") self.groupBox_3 = QtWidgets.QGroupBox(self.centralwidget) self.groupBox_3.setGeometry(QtCore.QRect(150, 270, 421, 71)) self.groupBox_3.setObjectName("groupBox_3") self.label_6 = QtWidgets.QLabel(self.groupBox_3) self.label_6.setGeometry(QtCore.QRect(20, 30, 61, 21)) self.label_6.setObjectName("label_6") # 设置显示保存路径的文本框 self.lineEdit_3 = QtWidgets.QLineEdit(self.groupBox_3) self.lineEdit_3.setGeometry(QtCore.QRect(80, 30, 241, 20)) self.lineEdit_3.setObjectName("lineEdit_3") # 设置选择图片保存路径的按钮 self.pushButton_4 = QtWidgets.QPushButton(self.groupBox_3) self.pushButton_4.setGeometry(QtCore.QRect(330, 30, 75, 23)) self.pushButton_4.setObjectName("pushButton_4") #设置执行按钮 self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_5.setGeometry(QtCore.QRect(480, 350, 75, 23)) self.pushButton_5.setObjectName("pushButton_5") MainWindow.setCentralWidget(self.centralwidget) #设置状态栏 self.statusBar = QtWidgets.QStatusBar(MainWindow) self.statusBar.setObjectName("statusBar") self.statusBar.showMessage('准备就绪…… ') # 设置状态栏默认值 MainWindow.setStatusBar(self.statusBar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) # 自动生成的代码,用来设置窗体中控件的默认值 def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "图片批量添加水印")) self.pushButton.setText(_translate("MainWindow", "加载图片")) self.groupBox.setTitle(_translate("MainWindow", "水印设置")) self.radioButton.setText(_translate("MainWindow", "添加文字水印")) self.label.setText(_translate("MainWindow", "水印文字:")) self.pushButton_2.setText(_translate("MainWindow", "字体设置")) self.radioButton_2.setText(_translate("MainWindow", "添加图片水印")) self.pushButton_3.setText(_translate("MainWindow", "浏览")) self.label_2.setText(_translate("MainWindow", "水印图片:")) self.groupBox_2.setTitle(_translate("MainWindow", "透明度及位置设置")) self.label_3.setText(_translate("MainWindow", "水印位置:")) self.label_4.setText(_translate("MainWindow", "透明度:")) self.groupBox_3.setTitle(_translate("MainWindow", "路径设置")) self.label_6.setText(_translate("MainWindow", "保存位置:")) self.pushButton_4.setText(_translate("MainWindow", "浏览")) self.pushButton_5.setText(_translate("MainWindow", "执行")) # 关联“加载图片”按钮的方法 self.pushButton.clicked.connect(self.getFiles) # 关联“字体设置”按钮的方法 self.pushButton_2.clicked.connect(self.setFont) # 关联“选择图片”按钮的方法 self.pushButton_3.clicked.connect(self.setImg) # 关联“选择保存路径”按钮的方法 self.pushButton_4.clicked.connect(self.msg) # 关联“执行”按钮的方法 self.pushButton_5.clicked.connect(self.addMark) # 关联列表单击方法,用来预览选中的图片 self.listWidget.itemClicked.connect(self.itemClick) # 设置字体 def setFont(self): self.waterfont, ok = QFontDialog.getFont() # 显示字体对话框 if ok: # 判断是否选择了字体 self.lineEdit.setFont(self.waterfont) # 设置水印文字的字体 self.fontSize = QFontMetrics(self.waterfont) # 获取字体尺寸 self.fontInfo = QFontInfo(self.waterfont) # 获取字体信息 # 是否为图片 def isImg(self, file): file = file.lower() if file == '.jpg': return True elif file == '.png': return True elif file == '.jpeg': return True elif file == '.bmp': return True else: return False # 获取所有文件 def getFiles(self): try: # 选择图片文件夹路径 self.img_path = QFileDialog.getExistingDirectory( None, "选择图片文件夹路径", os.getcwd()) self.list = os.listdir(self.img_path) # 遍历选择的文件夹 num = 0 # 记录图片数量 self.listWidget.clear() # 清空列表项 for i in range(0, len(self.list)): # 遍历图片列表 filepath = os.path.join(self.img_path, self.list[i]) # 记录遍历到的文件名 if os.path.isfile(filepath): # 判断是否为文件 imgType = os.path.splitext(filepath)[1] # 获取扩展名 if self.isImg(imgType): # 判断是否为图片 num += 1 # 数量加1 self.item = QtWidgets.QListWidgetItem( self.listWidget) # 创建列表项 self.item.setText(self.list[i]) # 显示图片列表 self.statusBar.showMessage('共有图片 ' + str(num) + ' 张') # 显示图片总数 except Exception: QMessageBox.warning(None, '警告', '请选择一个有效路径……', QMessageBox.Ok) # 预览图片 def itemClick(self, item): os.startfile(self.img_path + '\\' + item.text()) # 选择水印图片 def setImg(self): try: # waterimg即为选择的水印图片,第二形参为对话框标题,第三个为对话框打开后默认的路径 self.waterimg = QFileDialog.getOpenFileName( None, '选择水印图片', 'C:\\', "图片文件(*.jpeg;*.png;*.jpg;*.bmp)") self.lineEdit_2.setText(self.waterimg[0]) # 显示选择的水印图片 except Exception as e: print(e) # 选择保存路径 def msg(self): try: # dir_path即为选择的文件夹的绝对路径,第二形参为对话框标题,第三个为对话框打开后默认的路径 self.dir_path = QFileDialog.getExistingDirectory( None, "选择路径", os.getcwd()) self.lineEdit_3.setText(self.dir_path) # 显示选择的保存路径 except Exception as e: print(e) # 文字水印 def textMark(self, img, newImgPath): try: im = Image.open(img).convert('RGBA') # 打开原始图片,并转换为RGBA newImg = Image.new('RGBA', im.size, (255, 255, 255, 0)) # 存储添加水印后的图片 # 创建字体,说明:默认使用楷体,如果需要使用其他字体,需要将字体文件复制到当前目录中 # 然后对下面第一个参数进行修改,可以使用self.fontInfo.family()动态获取字体名称,后面加扩展名即可 font = ImageFont.truetype('simkai.ttf', self.fontInfo.pointSize()) imagedraw = ImageDraw.Draw(newImg) # 创建绘制对象 imgwidth, imgheight = im.size # 记录图片大小 txtwidth = self.fontSize.maxWidth() * len( self.lineEdit.text()) # 获取字体宽度 txtheight = self.fontSize.height() # 获取字体高度 # 设置水印文字位置 if self.comboBox.currentText() == '左上角': position = (0, 0) elif self.comboBox.currentText() == '左下角': position = (0, imgheight - txtheight) elif self.comboBox.currentText() == '右上角': position = (imgwidth - txtwidth, 0) elif self.comboBox.currentText() == '右下角': position = (imgwidth - txtwidth, imgheight - txtheight) elif self.comboBox.currentText() == '居中位置': position = (imgwidth / 2, imgheight / 2) # 设置文本颜色 imagedraw.text(position, self.lineEdit.text(), font=font, fill="#FCA454") # 设置透明度 alpha = newImg.split()[3] alpha = ImageEnhance.Brightness(alpha).enhance( int(self.horizontalSlider.value()) / 10.0) newImg.putalpha(alpha) Image.alpha_composite(im, newImg).save(newImgPath) # 保存图片 except Exception: QMessageBox.warning(None, '错误', '图片格式有误,请重新选择……', QMessageBox.Ok) # 图片水印 def imgMark(self, img, newImgPath): im = Image.open(img) # 打开原始图片 mark = Image.open(self.lineEdit_2.text()) # 打开水印图片 rgbaim = im.convert('RGBA') # 将原始图片转换为RGBA rgbamark = mark.convert('RGBA') # 将水印图片转换为RGBA imgwidth, imgheight = rgbaim.size # 获取原始图片尺寸 nimgwidth, nimgheight = rgbamark.size # 获取水印图片尺寸 # 缩放水印图片 scale = 10 markscale = max(imgwidth / (scale * nimgwidth), imgheight / (scale * nimgheight)) newsize = (int(nimgwidth * markscale), int(nimgheight * markscale) ) # 计算新的尺寸大小 rgbamark = rgbamark.resize(newsize, resample=Image.ANTIALIAS) # 重新设置水印图片大小 nimgwidth, nimgheight = rgbamark.size # 获取水印图片缩放后的尺寸 # 计算水印位置 if self.comboBox.currentText() == '左上角': position = (0, 0) elif self.comboBox.currentText() == '左下角': position = (0, imgheight - nimgheight) elif self.comboBox.currentText() == '右上角': position = (imgwidth - nimgwidth, 0) elif self.comboBox.currentText() == '右下角': position = (imgwidth - nimgwidth, imgheight - nimgheight) elif self.comboBox.currentText() == '居中位置': position = (int(imgwidth / 2), int(imgheight / 2)) # 设置透明度:img.point(function)接受一个参数,且对图片中的每一个点执行这个函数,这个函数是一个匿名函数,使用lambda表达式来完成 # convert()函数,用于不同模式图像之间的转换,模式“L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。 # 在PIL中,从模式“RGB”转换为“L”模式是按照下面的公式转换的:L = R * 299/1000 + G * 587/1000+ B * 114/1000 rgbamarkpha = rgbamark.convert("L").point( lambda x: x / int(self.horizontalSlider.value())) rgbamark.putalpha(rgbamarkpha) # 水印位置 rgbaim.paste(rgbamark, position, rgbamarkpha) try: rgbaim.save(newImgPath) # 保存水印图片 except Exception: QMessageBox.warning(None, '错误', '请选择其他路径……', QMessageBox.Ok) # 添加水印 def addMark(self): if self.lineEdit_3.text() == '': # 判断是否选择了保存路径 QMessageBox.warning(None, '警告', '请选择保存路径', QMessageBox.Ok) return else: num = 0 # 记录处理图片数量 for i in range(0, self.listWidget.count()): # 遍历图片列表 # 设置原始图片路径(包括文件名) filepath = os.path.join(self.img_path, self.listWidget.item(i).text()) # 设置水印图片保存路径(包括文件名) newfilepath = os.path.join(self.lineEdit_3.text(), self.listWidget.item(i).text()) if self.radioButton.isChecked(): # 判断是否选择文字水印单选按钮 if self.lineEdit.text() == '': # 判断是否输入了水印文字 QMessageBox.warning(None, '警告', '请输入水印文字', QMessageBox.Ok) return else: self.textMark(filepath, newfilepath) # 调用textMark方法添加文字水印 num += 1 # 处理图片数量加1 else: if self.lineEdit_2.text() != '': # 判断水印图片不为空 self.imgMark(filepath, newfilepath) # 调用imgMark方法添加图片水印 num += 1 # 处理图片数量加1 else: QMessageBox.warning(None, '警告', '请选择水印图片', QMessageBox.Ok) self.statusBar.showMessage('任务完成,此次共处理 ' + str(num) + ' 张图片') # 显示处理图片总数
def exportSource(self): """ Public method performing the export. """ self.pr = PDFRender() filename = self._getFileName(self.tr("PDF Files (*.pdf)")) if not filename: return try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) QApplication.processEvents() self.editor.recolor(0, -1) lex = self.editor.getLexer() tabSize = self.editor.getEditorConfig("TabWidth") if tabSize == 0: tabSize = 4 # get magnification value to add to default screen font size self.pr.fontSize = Preferences.getEditorExporter( "PDF/Magnification") # set font family according to face name fontName = Preferences.getEditorExporter("PDF/Font") self.pr.fontSet = PDF_FONT_DEFAULT if fontName == "Courier": self.pr.fontSet = 0 elif fontName == "Helvetica": self.pr.fontSet = 1 elif fontName == "Times": self.pr.fontSet = 2 # page size: height, width, pageSize = Preferences.getEditorExporter("PDF/PageSize") try: pageDimensions = PDFpageSizes[pageSize] except KeyError: pageDimensions = PDFpageSizes["A4"] self.pr.pageHeight = pageDimensions[0] self.pr.pageWidth = pageDimensions[1] # page margins: left, right, top, bottom # < 0 to use PDF default values val = Preferences.getEditorExporter("PDF/MarginLeft") if val < 0: self.pr.pageMargins["left"] = PDF_MARGIN_DEFAULT else: self.pr.pageMargins["left"] = val val = Preferences.getEditorExporter("PDF/MarginRight") if val < 0: self.pr.pageMargins["right"] = PDF_MARGIN_DEFAULT else: self.pr.pageMargins["right"] = val val = Preferences.getEditorExporter("PDF/MarginTop") if val < 0: self.pr.pageMargins["top"] = PDF_MARGIN_DEFAULT else: self.pr.pageMargins["top"] = val val = Preferences.getEditorExporter("PDF/MarginBottom") if val < 0: self.pr.pageMargins["bottom"] = PDF_MARGIN_DEFAULT else: self.pr.pageMargins["bottom"] = val # collect all styles available for that 'language' # or the default style if no language is available... if lex: istyle = 0 while istyle <= QsciScintilla.STYLE_MAX: if (istyle <= QsciScintilla.STYLE_DEFAULT or istyle > QsciScintilla.STYLE_LASTPREDEFINED): if (lex.description(istyle) or istyle == QsciScintilla.STYLE_DEFAULT): style = PDFStyle() font = lex.font(istyle) if font.italic(): style.font |= 2 if font.bold(): style.font |= 1 colour = lex.color(istyle) style.fore = self.__getPDFRGB(colour) self.pr.style[istyle] = style # grab font size from default style if istyle == QsciScintilla.STYLE_DEFAULT: fontSize = QFontInfo(font).pointSize() if fontSize > 0: self.pr.fontSize += fontSize else: self.pr.fontSize = PDF_FONTSIZE_DEFAULT istyle += 1 else: style = PDFStyle() font = Preferences.getEditorOtherFonts("DefaultFont") if font.italic(): style.font |= 2 if font.bold(): style.font |= 1 colour = self.editor.color() style.fore = self.__getPDFRGB(colour) self.pr.style[0] = style self.pr.style[QsciScintilla.STYLE_DEFAULT] = style fontSize = QFontInfo(font).pointSize() if fontSize > 0: self.pr.fontSize += fontSize else: self.pr.fontSize = PDF_FONTSIZE_DEFAULT try: # save file in win ansi using cp1250 f = open(filename, "w", encoding="cp1250", errors="backslashreplace") # initialise PDF rendering ot = PDFObjectTracker(f) self.pr.oT = ot self.pr.startPDF() # do here all the writing lengthDoc = self.editor.length() if lengthDoc == 0: self.pr.nextLine() # enable zero length docs else: pos = 0 column = 0 utf8 = self.editor.isUtf8() utf8Ch = b"" utf8Len = 0 while pos < lengthDoc: ch = self.editor.byteAt(pos) style = self.editor.styleAt(pos) if ch == b'\t': # expand tabs ts = tabSize - (column % tabSize) column += ts self.pr.add(' ' * ts, style) elif ch == b'\r' or ch == b'\n': if (ch == b'\r' and self.editor.byteAt(pos + 1) == b'\n'): pos += 1 # close and begin a newline... self.pr.nextLine() column = 0 else: # write the character normally... if ord(ch) > 127 and utf8: utf8Ch += ch if utf8Len == 0: if (utf8Ch[0] & 0xF0) == 0xF0: utf8Len = 4 elif (utf8Ch[0] & 0xE0) == 0xE0: utf8Len = 3 elif (utf8Ch[0] & 0xC0) == 0xC0: utf8Len = 2 column -= 1 # will be incremented again later elif len(utf8Ch) == utf8Len: ch = utf8Ch.decode('utf8') self.pr.add(ch, style) utf8Ch = b"" utf8Len = 0 else: column -= 1 # will be incremented again later else: self.pr.add(ch.decode(), style) column += 1 pos += 1 # write required stuff and close the PDF file self.pr.endPDF() f.close() except IOError as err: QApplication.restoreOverrideCursor() E5MessageBox.critical( self.editor, self.tr("Export source"), self.tr("""<p>The source could not be exported to""" """ <b>{0}</b>.</p><p>Reason: {1}</p>""").format( filename, str(err))) finally: QApplication.restoreOverrideCursor()
def setFont(self): self.waterfont, ok = QFontDialog.getFont() # 显示字体对话框 if ok: # 判断是否选择了字体 self.lineEdit.setFont(self.waterfont) # 设置水印文字的字体 self.fontSize = QFontMetrics(self.waterfont) # 获取字体尺寸 self.fontInfo = QFontInfo(self.waterfont) # 获取字体信息
def isFixedPitch(font: QFont) -> bool: fi = QFontInfo(font) return fi.fixedPitch()