def send_message_in_process(msg, address=None, timeout=5): address = address or gui_socket_address() if isinstance(msg, str): msg = msg.encode('utf-8') s = QLocalSocket() qt_timeout = int(timeout * 1000) if address.startswith('\0'): ps = unix_socket(timeout) ps.connect(address) s.setSocketDescriptor(ps.detach()) else: s.connectToServer(address) if not s.waitForConnected(qt_timeout): raise OSError( f'Failed to connect to Listener at: {address} with error: {s.errorString()}' ) data = QByteArray(msg) while True: written = s.write(data) if not s.waitForBytesWritten(qt_timeout): raise OSError( f'Failed to write data to address: {s.serverName()} with error: {s.errorString()}' ) if written >= len(data): break data = data.right(len(data) - written)
def image_to_data(image): # {{{ ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) if not image.save(buf, CACHE_FORMAT): raise EncodeError('Failed to encode thumbnail') ret = ba.data() buf.close() return ret
def add_image(self, img, cache_key): ref = self.get_image(cache_key) if ref is not None: return ref fmt = img.format() image = QImage(img) if (image.depth() == 1 and img.colorTable().size() == 2 and img.colorTable().at(0) == QColor(Qt.GlobalColor.black).rgba() and img.colorTable().at(1) == QColor(Qt.GlobalColor.white).rgba()): if fmt == QImage.Format.Format_MonoLSB: image = image.convertToFormat(QImage.Format.Format_Mono) fmt = QImage.Format.Format_Mono else: if (fmt != QImage.Format.Format_RGB32 and fmt != QImage.Format.Format_ARGB32): image = image.convertToFormat(QImage.Format.Format_ARGB32) fmt = QImage.Format.Format_ARGB32 w = image.width() h = image.height() d = image.depth() if fmt == QImage.Format.Format_Mono: bytes_per_line = (w + 7) >> 3 data = image.constBits().asstring(bytes_per_line * h) return self.write_image(data, w, h, d, cache_key=cache_key) has_alpha = False soft_mask = None if fmt == QImage.Format.Format_ARGB32: tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4] sdata = bytearray(tmask) vals = set(sdata) vals.discard(255) # discard opaque pixels has_alpha = bool(vals) if has_alpha: # Blend image onto a white background as otherwise Qt will render # transparent pixels as black background = QImage(image.size(), QImage.Format.Format_ARGB32_Premultiplied) background.fill(Qt.GlobalColor.white) painter = QPainter(background) painter.drawImage(0, 0, image) painter.end() image = background ba = QByteArray() buf = QBuffer(ba) image.save(buf, 'jpeg', 94) data = ba.data() if has_alpha: soft_mask = self.write_image(tmask, w, h, 8) return self.write_image(data, w, h, 32, dct=True, soft_mask=soft_mask, cache_key=cache_key)
def to_png(bmp): from qt.core import QImage, QByteArray, QBuffer, QIODevice i = QImage() if not i.loadFromData(bmp): raise ValueError('Invalid image data') ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) i.save(buf, 'png') return ba.data()
def restore_state(self): try: geom = gprefs.get('jobs_dialog_geometry', None) if geom: QApplication.instance().safe_restore_geometry(self, QByteArray(geom)) state = gprefs.get('jobs view column layout3', None) if state is not None: self.jobs_view.horizontalHeader().restoreState(QByteArray(state)) except: pass idx = self.jobs_view.model().index(0, 0) if idx.isValid(): sm = self.jobs_view.selectionModel() sm.select(idx, QItemSelectionModel.SelectionFlag.ClearAndSelect|QItemSelectionModel.SelectionFlag.Rows)
def rasterize_svg(self, elem, width=0, height=0, format='PNG'): view_box = elem.get('viewBox', elem.get('viewbox', None)) sizes = None logger = self.oeb.logger if view_box is not None: try: box = [ float(x) for x in filter(None, re.split('[, ]', view_box)) ] sizes = [box[2] - box[0], box[3] - box[1]] except (TypeError, ValueError, IndexError): logger.warn( 'SVG image has invalid viewBox="%s", ignoring the viewBox' % view_box) else: for image in elem.xpath( 'descendant::*[local-name()="image" and ' '@height and contains(@height, "%")]'): logger.info( 'Found SVG image height in %, trying to convert...') try: h = float(image.get('height').replace('%', '')) / 100. image.set('height', str(h * sizes[1])) except: logger.exception( 'Failed to convert percentage height:', image.get('height')) data = QByteArray(xml2str(elem, with_tail=False)) svg = QSvgRenderer(data) size = svg.defaultSize() if size.width() == 100 and size.height() == 100 and sizes: size.setWidth(sizes[0]) size.setHeight(sizes[1]) if width or height: size.scale(int(width), int(height), Qt.AspectRatioMode.KeepAspectRatio) logger.info('Rasterizing %r to %dx%d' % (elem, size.width(), size.height())) image = QImage(size, QImage.Format.Format_ARGB32_Premultiplied) image.fill(QColor("white").rgb()) painter = QPainter(image) svg.render(painter) painter.end() array = QByteArray() buffer = QBuffer(array) buffer.open(QIODevice.OpenModeFlag.WriteOnly) image.save(buffer, format) return array.data()
def encode_jpeg(file_path, quality=80): from calibre.utils.speedups import ReadOnlyFileBuffer quality = max(0, min(100, int(quality))) exe = get_exe_path('cjpeg') cmd = [exe] + '-optimize -progressive -maxmemory 100M -quality'.split() + [str(quality)] img = QImage() if not img.load(file_path): raise ValueError('%s is not a valid image file' % file_path) ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) if not img.save(buf, 'PPM'): raise ValueError('Failed to export image to PPM') return run_optimizer(file_path, cmd, as_filter=True, input_data=ReadOnlyFileBuffer(ba.data()))
def __init__(self, db, book_id, regex, doc=None, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.regex.setText(regex) self.regex_valid() if not db or not book_id: button = self.button_box.addButton( QDialogButtonBox.StandardButton.Open) button.clicked.connect(self.open_clicked) elif not doc and not self.select_format(db, book_id): self.cancelled = True return if doc: self.preview.setPlainText(doc) self.cancelled = False self.button_box.accepted.connect(self.accept) self.regex.textChanged[native_string_type].connect(self.regex_valid) for src, slot in (('test', 'do'), ('previous', 'goto'), ('next', 'goto')): getattr(self, src).clicked.connect(getattr(self, '%s_%s' % (slot, src))) self.test.setDefault(True) self.match_locs = [] geom = gprefs.get('regex_builder_geometry', None) if geom is not None: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) self.finished.connect(self.save_state)
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) ua = 'calibre-editor-preview ' + __version__ ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_editor compile_editor() js = P('editor.js', data=True, allow_user_override=False) cparser = P('csscolorparser.js', data=True, allow_user_override=False) insert_scripts(ans, create_script('csscolorparser.js', cparser), create_script('editor.js', js), create_dark_mode_script(), ) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, False) s.setAttribute(QWebEngineSettings.WebAttribute.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) osname = 'windows' if iswindows else ('macos' if ismacos else 'linux') # DO NOT change the user agent as it is used to workaround # Qt bugs see workaround_qt_bug() in ajax.pyj ua = 'calibre-viewer {} {}'.format(__version__, osname) ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_viewer prints('Compiling viewer code...') compile_viewer() js = P('viewer.js', data=True, allow_user_override=False) translations_json = get_translations_data() or b'null' js = js.replace(b'__TRANSLATIONS_DATA__', translations_json, 1) if in_develop_mode: js = js.replace(b'__IN_DEVELOP_MODE__', b'1') insert_scripts(ans, create_script('viewer.js', js)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(QWebEngineSettings.WebAttribute.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
def load_html(path, view, codec='utf-8', mime_type=None, pre_load_callback=lambda x:None, path_is_html=False, force_as_html=False, loading_url=None): from qt.core import QUrl, QByteArray if mime_type is None: mime_type = guess_type(path)[0] if not mime_type: mime_type = 'text/html' if path_is_html: html = path else: with open(path, 'rb') as f: html = f.read().decode(codec, 'replace') html = cleanup_html(html) loading_url = loading_url or QUrl.fromLocalFile(path) pre_load_callback(loading_url) if force_as_html or load_as_html(html): view.setHtml(html, loading_url) else: view.setContent(QByteArray(html.encode(codec)), mime_type, loading_url) mf = view.page().mainFrame() elem = mf.findFirstElement('parsererror') if not elem.isNull(): return False return True
def image_and_format_from_data(data): ' Create an image object from the specified data which should be a bytestring and also return the format of the image ' ba = QByteArray(data) buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.ReadOnly) r = QImageReader(buf) fmt = bytes(r.format()).decode('utf-8') return r.read(), fmt
def icon_to_dbus_menu_icon(icon, size=32): if icon.isNull(): return None ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) icon.pixmap(32).save(buf, 'PNG') return dbus.ByteArray(ba)
def loadResource(self, rtype, qurl): if qurl.isLocalFile(): path = qurl.toLocalFile() try: with lopen(path, 'rb') as f: data = f.read() except OSError: if path.rpartition('.')[-1].lower() in {'jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'}: return QByteArray(bytearray.fromhex( '89504e470d0a1a0a0000000d49484452' '000000010000000108060000001f15c4' '890000000a49444154789c6300010000' '0500010d0a2db40000000049454e44ae' '426082')) else: return QByteArray(data) else: return QTextBrowser.loadResource(self, rtype, qurl)
def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level=9, jpeg_optimized=True, jpeg_progressive=False): ''' Serialize image to bytestring in the specified format. :param compression_quality: is for JPEG and goes from 0 to 100. 100 being lowest compression, highest image quality :param png_compression_level: is for PNG and goes from 0-9. 9 being highest compression. :param jpeg_optimized: Turns on the 'optimize' option for libjpeg which losslessly reduce file size :param jpeg_progressive: Turns on the 'progressive scan' option for libjpeg which allows JPEG images to be downloaded in streaming fashion ''' fmt = fmt.upper() ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) if fmt == 'GIF': w = QImageWriter(buf, b'PNG') w.setQuality(90) if not w.write(img): raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString()) return png_data_to_gif_data(ba.data()) is_jpeg = fmt in ('JPG', 'JPEG') w = QImageWriter(buf, fmt.encode('ascii')) if is_jpeg: if img.hasAlphaChannel(): img = blend_image(img) # QImageWriter only gained the following options in Qt 5.5 if jpeg_optimized: w.setOptimizedWrite(True) if jpeg_progressive: w.setProgressiveScanWrite(True) w.setQuality(compression_quality) elif fmt == 'PNG': cl = min(9, max(0, png_compression_level)) w.setQuality(10 * (9 - cl)) if not w.write(img): raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString()) return ba.data()
def __init__(self): profile = QWebEngineProfile(QApplication.instance()) profile.setHttpUserAgent('calibre-tester') insert_scripts(profile, create_script('test-rapydscript.js', js, on_subframes=False)) url_handler = UrlSchemeHandler(profile) profile.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) QWebEnginePage.__init__(self, profile, None) self.titleChanged.connect(self.title_changed) secure_webengine(self) self.setHtml('<p>initialize', QUrl(f'{FAKE_PROTOCOL}://{FAKE_HOST}/index.html')) self.working = True
def rasterize_external(self, elem, style, item, svgitem): width = style['width'] height = style['height'] width = (width / 72) * self.profile.dpi height = (height / 72) * self.profile.dpi data = QByteArray(svgitem.bytes_representation) svg = QSvgRenderer(data) size = svg.defaultSize() size.scale(width, height, Qt.AspectRatioMode.KeepAspectRatio) key = (svgitem.href, size.width(), size.height()) if key in self.images: href = self.images[key] else: logger = self.oeb.logger logger.info('Rasterizing %r to %dx%d' % (svgitem.href, size.width(), size.height())) image = QImage(size, QImage.Format.Format_ARGB32_Premultiplied) image.fill(QColor("white").rgb()) painter = QPainter(image) svg.render(painter) painter.end() array = QByteArray() buffer = QBuffer(array) buffer.open(QIODevice.OpenModeFlag.WriteOnly) image.save(buffer, 'PNG') data = array.data() manifest = self.oeb.manifest href = os.path.splitext(svgitem.href)[0] + '.png' id, href = manifest.generate(svgitem.id, href) manifest.add(id, href, PNG_MIME, data=data) self.images[key] = href elem.tag = XHTML('img') for attr in elem.attrib: if attr not in KEEP_ATTRS: del elem.attrib[attr] elem.attrib['src'] = item.relhref(href) if elem.text: elem.attrib['alt'] = elem.text elem.text = None for child in elem: elem.remove(child)
def load_fonts(self, lrf, load_substitutions=True): font_map = {} for font in lrf.font_map: fdata = QByteArray(lrf.font_map[font].data) id = QFontDatabase.addApplicationFontFromData(fdata) if id != -1: font_map[font] = [ str(i) for i in QFontDatabase.applicationFontFamilies(id) ][0] if load_substitutions: base = P('fonts/liberation/*.ttf') for f in glob.glob(base): QFontDatabase.addApplicationFont(f) self.font_loader = FontLoader(font_map, self.dpi)
def __init__(self, gui, row, toggle_shortcut): self.is_pane = gprefs.get('quickview_is_pane', False) if not self.is_pane: QDialog.__init__(self, gui, flags=Qt.WindowType.Widget) else: QDialog.__init__(self, gui) Ui_Quickview.__init__(self) self.setupUi(self) self.isClosed = False self.current_book = None self.closed_by_button = False if self.is_pane: self.main_grid_layout.setContentsMargins(0, 0, 0, 0) else: self.setWindowIcon(self.windowIcon()) self.books_table_column_widths = None try: self.books_table_column_widths = \ gprefs.get('quickview_dialog_books_table_widths', None) if not self.is_pane: geom = gprefs.get('quickview_dialog_geometry', None) if geom: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) except: pass self.view = gui.library_view self.db = self.view.model().db self.gui = gui self.is_closed = False self.current_book_id = None # the db id of the book used to fill the lh pane self.current_column = None # current logical column in books list self.current_key = None # current lookup key in books list self.last_search = None self.no_valid_items = False self.follow_library_view = True self.apply_vls.setCheckState( Qt.CheckState.Checked if gprefs['qv_respects_vls'] else Qt. CheckState.Unchecked) self.apply_vls.stateChanged.connect(self.vl_box_changed) self.fm = self.db.field_metadata self.items.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.items.currentTextChanged.connect(self.item_selected) self.items.setProperty('highlight_current_item', 150) self.items.itemDoubleClicked.connect(self.item_doubleclicked) self.items.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.items.customContextMenuRequested.connect( self.show_item_context_menu) focus_filter = WidgetFocusFilter(self.items) focus_filter.focus_entered_signal.connect(self.focus_entered) self.items.installEventFilter(focus_filter) self.tab_pressed_signal.connect(self.tab_pressed) return_filter = BooksTableFilter(self.books_table) return_filter.return_pressed_signal.connect(self.return_pressed) self.books_table.installEventFilter(return_filter) focus_filter = WidgetFocusFilter(self.books_table) focus_filter.focus_entered_signal.connect(self.focus_entered) self.books_table.installEventFilter(focus_filter) self.close_button.clicked.connect(self.close_button_clicked) self.refresh_button.clicked.connect(self.refill) self.tab_order_widgets = [ self.items, self.books_table, self.lock_qv, self.dock_button, self.refresh_button, self.close_button ] for idx, widget in enumerate(self.tab_order_widgets): widget.installEventFilter( WidgetTabFilter(widget, idx, self.tab_pressed_signal)) self.books_table.setSelectionBehavior( QAbstractItemView.SelectionBehavior.SelectRows) self.books_table.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.books_table.setProperty('highlight_current_item', 150) # Set up the books table columns self.add_columns_to_widget() self.books_table_header_height = self.books_table.height() self.books_table.cellDoubleClicked.connect(self.book_doubleclicked) self.books_table.currentCellChanged.connect( self.books_table_cell_changed) self.books_table.cellClicked.connect( self.books_table_set_search_string) self.books_table.cellActivated.connect( self.books_table_set_search_string) self.books_table.sortByColumn(0, Qt.SortOrder.AscendingOrder) # get the standard table row height. Do this here because calling # resizeRowsToContents can word wrap long cell contents, creating # double-high rows self.books_table.setRowCount(1) self.books_table.setItem(0, 0, TableItem()) self.books_table.resizeRowsToContents() self.books_table_row_height = self.books_table.rowHeight(0) self.books_table.setRowCount(0) # Add the data self.refresh(row) self.slave_timers = [QTimer(self), QTimer(self), QTimer(self)] self.view.clicked.connect( partial(self.delayed_slave, func=self.slave, dex=0)) self.view.selectionModel().currentColumnChanged.connect( partial(self.delayed_slave, func=self.column_slave, dex=1)) QCoreApplication.instance().aboutToQuit.connect(self.save_state) self.view.model().new_bookdisplay_data.connect( partial(self.delayed_slave, func=self.book_was_changed, dex=2)) self.close_button.setDefault(False) self.close_button_tooltip = _( 'The Quickview shortcut ({0}) shows/hides the Quickview panel') self.refresh_button.setIcon(QIcon.ic('view-refresh.png')) self.close_button.setIcon(self.style().standardIcon( QStyle.StandardPixmap.SP_DialogCloseButton)) if self.is_pane: self.dock_button.setText(_('Undock')) self.dock_button.setToolTip( _('Show the Quickview panel in its own floating window')) self.dock_button.setIcon(QIcon(I('arrow-up.png'))) # Remove the ampersands from the buttons because shortcuts exist. self.lock_qv.setText(_('Lock Quickview contents')) self.refresh_button.setText(_('Refresh')) self.gui.quickview_splitter.add_quickview_dialog(self) self.close_button.setVisible(False) else: self.dock_button.setToolTip( _('Embed the Quickview panel into the main calibre window')) self.dock_button.setIcon(QIcon(I('arrow-down.png'))) self.set_focus() self.books_table.horizontalHeader().sectionResized.connect( self.section_resized) self.dock_button.clicked.connect(self.show_as_pane_changed) self.view.model().search_done.connect(self.check_for_no_items) # Enable the refresh button only when QV is locked self.refresh_button.setEnabled(False) self.lock_qv.stateChanged.connect(self.lock_qv_changed) self.view_icon = QIcon(I('view.png')) self.view_plugin = self.gui.iactions['View'] self.edit_metadata_icon = QIcon(I('edit_input.png')) self.quickview_icon = QIcon(I('quickview.png')) self.select_book_icon = QIcon(I('library.png')) self.search_icon = QIcon(I('search.png')) self.books_table.setContextMenuPolicy( Qt.ContextMenuPolicy.CustomContextMenu) self.books_table.customContextMenuRequested.connect( self.show_context_menu) # Add the quickview toggle as a shortcut for the close button # Don't add it if it identical to the current &X shortcut because that # breaks &X if (not self.is_pane and toggle_shortcut and self.close_button.shortcut() != toggle_shortcut): toggle_sc = QShortcut(toggle_shortcut, self.close_button) toggle_sc.activated.connect(lambda: self.close_button_clicked()) toggle_sc.setEnabled(True) self.close_button.setToolTip( _('Alternate shortcut: ') + toggle_shortcut.toString())
def pixmap_to_data(pixmap): ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) pixmap.save(buf, 'PNG') return bytearray(ba.data())
def __init__(self, parent, text, mi=None, fm=None, color_field=None, icon_field_key=None, icon_rule_kind=None, doing_emblem=False, text_is_placeholder=False, dialog_is_st_editor=False, global_vars=None, all_functions=None, builtin_functions=None): QDialog.__init__(self, parent) Ui_TemplateDialog.__init__(self) self.setupUi(self) self.coloring = color_field is not None self.iconing = icon_field_key is not None self.embleming = doing_emblem self.dialog_is_st_editor = dialog_is_st_editor if global_vars is None: self.global_vars = {} else: self.global_vars = global_vars cols = [] if fm is not None: for key in sorted(displayable_columns(fm), key=lambda k: sort_key(fm[k]['name'] if k != color_row_key else 0)): if key == color_row_key and not self.coloring: continue from calibre.gui2.preferences.coloring import all_columns_string name = all_columns_string if key == color_row_key else fm[key]['name'] if name: cols.append((name, key)) self.color_layout.setVisible(False) self.icon_layout.setVisible(False) if self.coloring: self.color_layout.setVisible(True) for n1, k1 in cols: self.colored_field.addItem(n1 + (' (' + k1 + ')' if k1 != color_row_key else ''), k1) self.colored_field.setCurrentIndex(self.colored_field.findData(color_field)) elif self.iconing or self.embleming: self.icon_layout.setVisible(True) if self.embleming: self.icon_kind_label.setVisible(False) self.icon_kind.setVisible(False) self.icon_chooser_label.setVisible(False) self.icon_field.setVisible(False) for n1, k1 in cols: self.icon_field.addItem('{} ({})'.format(n1, k1), k1) self.icon_file_names = [] d = os.path.join(config_dir, 'cc_icons') if os.path.exists(d): for icon_file in os.listdir(d): icon_file = icu_lower(icon_file) if os.path.exists(os.path.join(d, icon_file)): if icon_file.endswith('.png'): self.icon_file_names.append(icon_file) self.icon_file_names.sort(key=sort_key) self.update_filename_box() if self.iconing: dex = 0 from calibre.gui2.preferences.coloring import icon_rule_kinds for i,tup in enumerate(icon_rule_kinds): txt,val = tup self.icon_kind.addItem(txt, userData=(val)) if val == icon_rule_kind: dex = i self.icon_kind.setCurrentIndex(dex) self.icon_field.setCurrentIndex(self.icon_field.findData(icon_field_key)) if dialog_is_st_editor: self.buttonBox.setVisible(False) else: self.new_doc_label.setVisible(False) self.new_doc.setVisible(False) self.template_name_label.setVisible(False) self.template_name.setVisible(False) if mi: if not isinstance(mi, list): mi = (mi, ) else: mi = Metadata(_('Title'), [_('Author')]) mi.author_sort = _('Author Sort') mi.series = ngettext('Series', 'Series', 1) mi.series_index = 3 mi.rating = 4.0 mi.tags = [_('Tag 1'), _('Tag 2')] mi.languages = ['eng'] mi.id = 1 if fm is not None: self.mi.set_all_user_metadata(fm.custom_field_metadata()) else: # No field metadata. Grab a copy from the current library so # that we can validate any custom column names. The values for # the columns will all be empty, which in some very unusual # cases might cause formatter errors. We can live with that. from calibre.gui2.ui import get_gui mi.set_all_user_metadata( get_gui().current_db.new_api.field_metadata.custom_field_metadata()) for col in mi.get_all_user_metadata(False): mi.set(col, (col,), 0) mi = (mi, ) self.mi = mi # Set up the display table self.table_column_widths = None try: self.table_column_widths = \ gprefs.get('template_editor_table_widths', None) except: pass tv = self.template_value tv.setRowCount(len(mi)) tv.setColumnCount(2) tv.setHorizontalHeaderLabels((_('Book title'), _('Template value'))) tv.horizontalHeader().setStretchLastSection(True) tv.horizontalHeader().sectionResized.connect(self.table_column_resized) # Set the height of the table h = tv.rowHeight(0) * min(len(mi), 5) h += 2 * tv.frameWidth() + tv.horizontalHeader().height() tv.setMinimumHeight(h) tv.setMaximumHeight(h) # Set the size of the title column if self.table_column_widths: tv.setColumnWidth(0, self.table_column_widths[0]) else: tv.setColumnWidth(0, tv.fontMetrics().averageCharWidth() * 10) # Use our own widget to get rid of elision. setTextElideMode() doesn't work for r in range(0, len(mi)): w = QLineEdit(tv) w.setReadOnly(True) tv.setCellWidget(r, 0, w) w = QLineEdit(tv) w.setReadOnly(True) tv.setCellWidget(r, 1, w) # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags()&(~Qt.WindowType.WindowContextHelpButtonHint)) self.setWindowIcon(icon) self.all_functions = all_functions if all_functions else formatter_functions().get_functions() self.builtins = (builtin_functions if builtin_functions else formatter_functions().get_builtins_and_aliases()) self.last_text = '' self.highlighter = TemplateHighlighter(self.textbox.document(), builtin_functions=self.builtins) self.textbox.cursorPositionChanged.connect(self.text_cursor_changed) self.textbox.textChanged.connect(self.textbox_changed) self.textbox.setTabStopWidth(10) self.source_code.setTabStopWidth(10) self.documentation.setReadOnly(True) self.source_code.setReadOnly(True) if text is not None: if text_is_placeholder: self.textbox.setPlaceholderText(text) self.textbox.clear() text = '' else: self.textbox.setPlainText(text) else: text = '' self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(_('&Cancel')) self.color_copy_button.clicked.connect(self.color_to_clipboard) self.filename_button.clicked.connect(self.filename_button_clicked) self.icon_copy_button.clicked.connect(self.icon_to_clipboard) try: with open(P('template-functions.json'), 'rb') as f: self.builtin_source_dict = json.load(f, encoding='utf-8') except: self.builtin_source_dict = {} func_names = sorted(self.all_functions) self.function.clear() self.function.addItem('') for f in func_names: self.function.addItem('{} -- {}'.format(f, self.function_type_string(f, longform=False)), f) self.function.setCurrentIndex(0) self.function.currentIndexChanged.connect(self.function_changed) self.display_values(text) self.rule = (None, '') tt = _('Template language tutorial') self.template_tutorial.setText( '<a href="%s">%s</a>' % ( localize_user_manual_link('https://manual.calibre-ebook.com/template_lang.html'), tt)) tt = _('Template function reference') self.template_func_reference.setText( '<a href="%s">%s</a>' % ( localize_user_manual_link('https://manual.calibre-ebook.com/generated/en/template_ref.html'), tt)) self.set_up_font_boxes() self.textbox.setFocus() # Now geometry try: geom = gprefs.get('template_editor_dialog_geometry', None) if geom is not None: QApplication.instance().safe_restore_geometry(self, QByteArray(geom)) except Exception: pass
def restore_state(self, prefs): data = prefs.get(f'{self.tts_client.name}-voice-table-state') if data is not None: self.voices.horizontalHeader().restoreState(QByteArray(data))
def restore_geometry_qt(self, value): self.restoreGeometry(QByteArray.fromBase64(value.encode('ascii')))
def __init__(self, parent, text, mi=None, fm=None, color_field=None, icon_field_key=None, icon_rule_kind=None, doing_emblem=False, text_is_placeholder=False, dialog_is_st_editor=False, global_vars=None, all_functions=None, builtin_functions=None): QDialog.__init__(self, parent) Ui_TemplateDialog.__init__(self) self.setupUi(self) self.coloring = color_field is not None self.iconing = icon_field_key is not None self.embleming = doing_emblem self.dialog_is_st_editor = dialog_is_st_editor if global_vars is None: self.global_vars = {} else: self.global_vars = global_vars cols = [] self.fm = fm if fm is not None: for key in sorted( displayable_columns(fm), key=lambda k: sort_key(fm[k]['name'] if k != color_row_key else 0)): if key == color_row_key and not self.coloring: continue from calibre.gui2.preferences.coloring import all_columns_string name = all_columns_string if key == color_row_key else fm[key][ 'name'] if name: cols.append((name, key)) self.color_layout.setVisible(False) self.icon_layout.setVisible(False) if self.coloring: self.color_layout.setVisible(True) for n1, k1 in cols: self.colored_field.addItem( n1 + (' (' + k1 + ')' if k1 != color_row_key else ''), k1) self.colored_field.setCurrentIndex( self.colored_field.findData(color_field)) elif self.iconing or self.embleming: self.icon_layout.setVisible(True) if self.embleming: self.icon_kind_label.setVisible(False) self.icon_kind.setVisible(False) self.icon_chooser_label.setVisible(False) self.icon_field.setVisible(False) for n1, k1 in cols: self.icon_field.addItem('{} ({})'.format(n1, k1), k1) self.icon_file_names = [] d = os.path.join(config_dir, 'cc_icons') if os.path.exists(d): for icon_file in os.listdir(d): icon_file = icu_lower(icon_file) if os.path.exists(os.path.join(d, icon_file)): if icon_file.endswith('.png'): self.icon_file_names.append(icon_file) self.icon_file_names.sort(key=sort_key) self.update_filename_box() if self.iconing: dex = 0 from calibre.gui2.preferences.coloring import icon_rule_kinds for i, tup in enumerate(icon_rule_kinds): txt, val = tup self.icon_kind.addItem(txt, userData=(val)) if val == icon_rule_kind: dex = i self.icon_kind.setCurrentIndex(dex) self.icon_field.setCurrentIndex( self.icon_field.findData(icon_field_key)) self.setup_saved_template_editor(not dialog_is_st_editor, dialog_is_st_editor) # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags() & (~Qt.WindowType.WindowContextHelpButtonHint)) self.setWindowIcon(icon) self.all_functions = all_functions if all_functions else formatter_functions( ).get_functions() self.builtins = (builtin_functions if builtin_functions else formatter_functions().get_builtins_and_aliases()) # Set up the display table self.table_column_widths = None try: self.table_column_widths = \ gprefs.get('template_editor_table_widths', None) except: pass self.set_mi(mi, fm) self.last_text = '' self.highlighter = TemplateHighlighter(self.textbox.document(), builtin_functions=self.builtins) self.textbox.cursorPositionChanged.connect(self.text_cursor_changed) self.textbox.textChanged.connect(self.textbox_changed) self.textbox.setFont(self.get_current_font()) self.textbox.setTabStopWidth(10) self.source_code.setTabStopWidth(10) self.documentation.setReadOnly(True) self.source_code.setReadOnly(True) if text is not None: if text_is_placeholder: self.textbox.setPlaceholderText(text) self.textbox.clear() text = '' else: self.textbox.setPlainText(text) else: text = '' self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText( _('&OK')) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText( _('&Cancel')) self.color_copy_button.clicked.connect(self.color_to_clipboard) self.filename_button.clicked.connect(self.filename_button_clicked) self.icon_copy_button.clicked.connect(self.icon_to_clipboard) try: with open(P('template-functions.json'), 'rb') as f: self.builtin_source_dict = json.load(f, encoding='utf-8') except: self.builtin_source_dict = {} func_names = sorted(self.all_functions) self.function.clear() self.function.addItem('') for f in func_names: self.function.addItem( '{} -- {}'.format( f, self.function_type_string(f, longform=False)), f) self.function.setCurrentIndex(0) self.function.currentIndexChanged.connect(self.function_changed) self.rule = (None, '') tt = _('Template language tutorial') self.template_tutorial.setText( '<a href="%s">%s</a>' % (localize_user_manual_link( 'https://manual.calibre-ebook.com/template_lang.html'), tt)) tt = _('Template function reference') self.template_func_reference.setText( '<a href="%s">%s</a>' % (localize_user_manual_link( 'https://manual.calibre-ebook.com/generated/en/template_ref.html' ), tt)) s = gprefs.get('template_editor_break_on_print', False) self.go_button.setEnabled(s) self.remove_all_button.setEnabled(s) self.set_all_button.setEnabled(s) self.toggle_button.setEnabled(s) self.breakpoint_line_box.setEnabled(s) self.breakpoint_line_box_label.setEnabled(s) self.break_box.setChecked(s) self.break_box.stateChanged.connect(self.break_box_changed) self.go_button.clicked.connect(self.go_button_pressed) self.textbox.setFocus() self.set_up_font_boxes() self.toggle_button.clicked.connect(self.toggle_button_pressed) self.remove_all_button.clicked.connect(self.remove_all_button_pressed) self.set_all_button.clicked.connect(self.set_all_button_pressed) self.load_button.clicked.connect(self.load_template_from_file) self.save_button.clicked.connect(self.save_template) self.textbox.setWordWrapMode(QTextOption.WordWrap) self.textbox.setContextMenuPolicy( Qt.ContextMenuPolicy.CustomContextMenu) self.textbox.customContextMenuRequested.connect(self.show_context_menu) # Now geometry try: geom = gprefs.get('template_editor_dialog_geometry', None) if geom is not None: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) except Exception: pass
def __init__(self, gui, view, id_, row_index): QDialog.__init__(self, gui, flags=Qt.WindowType.Window) Ui_MatchBooks.__init__(self) self.setupUi(self) self.isClosed = False self.books_table_column_widths = None try: self.books_table_column_widths = \ gprefs.get('match_books_dialog_books_table_widths', None) geom = gprefs.get('match_books_dialog_geometry', None) if geom: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) except: pass self.search_text.initialize('match_books_dialog') # Remove the help button from the window title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags() & (~Qt.WindowType.WindowContextHelpButtonHint)) self.setWindowIcon(icon) self.device_db = view.model().db self.library_db = gui.library_view.model().db self.view = view self.gui = gui self.current_device_book_id = id_ self.current_device_book_index = row_index self.current_library_book_id = None # Set up the books table columns self.books_table.setSelectionBehavior( QAbstractItemView.SelectionBehavior.SelectRows) self.books_table.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.books_table.setColumnCount(3) t = QTableWidgetItem(_('Title')) self.books_table.setHorizontalHeaderItem(0, t) t = QTableWidgetItem(_('Authors')) self.books_table.setHorizontalHeaderItem(1, t) t = QTableWidgetItem(ngettext("Series", 'Series', 1)) self.books_table.setHorizontalHeaderItem(2, t) self.books_table_header_height = self.books_table.height() self.books_table.cellDoubleClicked.connect(self.book_doubleclicked) self.books_table.cellClicked.connect(self.book_clicked) self.books_table.sortByColumn(0, Qt.SortOrder.AscendingOrder) # get the standard table row height. Do this here because calling # resizeRowsToContents can word wrap long cell contents, creating # double-high rows self.books_table.setRowCount(1) self.books_table.setItem(0, 0, TableItem('A', '')) self.books_table.resizeRowsToContents() self.books_table_row_height = self.books_table.rowHeight(0) self.books_table.setRowCount(0) self.search_button.clicked.connect(self.do_search) self.search_button.setDefault(False) self.search_text.lineEdit().returnPressed.connect(self.return_pressed) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.ignore_next_key = False search_text = self.device_db[self.current_device_book_id].title search_text = search_text.replace('(', '\\(').replace(')', '\\)') self.search_text.setText(search_text)
def __init__(self, parent, db, id_to_select, select_sort, select_link, find_aut_func, is_first_letter=False): QDialog.__init__(self, parent) Ui_EditAuthorsDialog.__init__(self) self.setupUi(self) # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags() & (~Qt.WindowType.WindowContextHelpButtonHint)) self.setWindowIcon(icon) try: self.table_column_widths = \ gprefs.get('manage_authors_table_widths', None) geom = gprefs.get('manage_authors_dialog_geometry', None) if geom: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) except Exception: pass self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText( _('&OK')) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText( _('&Cancel')) self.buttonBox.accepted.connect(self.accepted) self.apply_vl_checkbox.stateChanged.connect(self.use_vl_changed) # Set up the heading for sorting self.table.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.find_aut_func = find_aut_func self.table.resizeColumnsToContents() if self.table.columnWidth(2) < 200: self.table.setColumnWidth(2, 200) # set up the cellChanged signal only after the table is filled self.table.cellChanged.connect(self.cell_changed) self.recalc_author_sort.clicked.connect(self.do_recalc_author_sort) self.auth_sort_to_author.clicked.connect(self.do_auth_sort_to_author) # Capture clicks on the horizontal header to sort the table columns hh = self.table.horizontalHeader() hh.sectionResized.connect(self.table_column_resized) hh.setSectionsClickable(True) hh.sectionClicked.connect(self.do_sort) hh.setSortIndicatorShown(True) # set up the search & filter boxes self.find_box.initialize('manage_authors_search') le = self.find_box.lineEdit() ac = le.findChild(QAction, QT_HIDDEN_CLEAR_ACTION) if ac is not None: ac.triggered.connect(self.clear_find) le.returnPressed.connect(self.do_find) self.find_box.editTextChanged.connect(self.find_text_changed) self.find_button.clicked.connect(self.do_find) self.find_button.setDefault(True) self.filter_box.initialize('manage_authors_filter') le = self.filter_box.lineEdit() ac = le.findChild(QAction, QT_HIDDEN_CLEAR_ACTION) if ac is not None: ac.triggered.connect(self.clear_filter) self.filter_box.lineEdit().returnPressed.connect(self.do_filter) self.filter_button.clicked.connect(self.do_filter) self.not_found_label = l = QLabel(self.table) l.setFrameStyle(QFrame.Shape.StyledPanel) l.setAutoFillBackground(True) l.setText(_('No matches found')) l.setAlignment(Qt.AlignmentFlag.AlignVCenter) l.resize(l.sizeHint()) l.move(10, 2) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.ConnectionType.QueuedConnection) self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.table.customContextMenuRequested.connect(self.show_context_menu) # Fetch the data self.authors = {} self.original_authors = {} auts = db.new_api.author_data() self.completion_data = [] for id_, v in auts.items(): name = v['name'] name = name.replace('|', ',') self.completion_data.append(name) self.authors[id_] = { 'name': name, 'sort': v['sort'], 'link': v['link'] } self.original_authors[id_] = { 'name': name, 'sort': v['sort'], 'link': v['link'] } self.edited_icon = QIcon(I('modified.png')) self.empty_icon = QIcon() if prefs['use_primary_find_in_search']: self.string_contains = primary_contains else: self.string_contains = contains self.last_sorted_by = 'sort' self.author_order = 1 self.author_sort_order = 0 self.link_order = 1 self.show_table(id_to_select, select_sort, select_link, is_first_letter)
def __init__(self, parent, mi, op_label, op_value, locals_, line_number): super().__init__(parent) self.mi = mi self.setModal(True) l = QVBoxLayout(self) t = self.table = QTableWidget(self) t.setColumnCount(2) t.setHorizontalHeaderLabels((_('Name'), _('Value'))) t.setRowCount(2) l.addWidget(t) self.table_column_widths = None try: self.table_column_widths = \ gprefs.get('template_editor_break_table_widths', None) t.setColumnWidth(0, self.table_column_widths[0]) except: t.setColumnWidth(0, t.fontMetrics().averageCharWidth() * 20) t.horizontalHeader().sectionResized.connect(self.table_column_resized) t.horizontalHeader().setStretchLastSection(True) bb = QDialogButtonBox() b = bb.addButton(_('&Continue'), QDialogButtonBox.ButtonRole.AcceptRole) b.setIcon(QIcon(I('sync-right.png'))) b.setToolTip(_('Continue running the template')) b.setDefault(True) l.addWidget(bb) b = bb.addButton(_('&Stop'), QDialogButtonBox.ButtonRole.RejectRole) b.setIcon(QIcon(I('list_remove.png'))) b.setToolTip(_('Stop running the template')) l.addWidget(bb) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) self.setLayout(l) self.setWindowTitle( _('Break: line {0}, Book {1}').format(line_number, self.mi.title)) local_names = sorted(locals_.keys()) rows = len(local_names) self.table.setRowCount(rows + 2) self.table.setItem(0, 0, BreakReporterItem(op_label)) self.table.item(0, 0).setToolTip( _('The name of the template language operation')) self.table.setItem(0, 1, BreakReporterItem(op_value)) self.mi_combo = QComboBox() t.setCellWidget(1, 0, self.mi_combo) self.mi_combo.addItems(self.get_field_keys()) self.mi_combo.setToolTip('Choose a book metadata field to display') self.mi_combo.setCurrentIndex(-1) self.mi_combo.currentTextChanged.connect(self.get_field_value) for i, k in enumerate(local_names): itm = BreakReporterItem(k) itm.setToolTip(_('A variable in the template')) self.table.setItem(i + 2, 0, itm) itm = BreakReporterItem(locals_[k]) itm.setToolTip(_('The value of the variable')) self.table.setItem(i + 2, 1, itm) try: geom = gprefs.get('template_editor_break_geometry', None) if geom is not None: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) except Exception: pass
def __init__(self, window, cat_name, tag_to_match, get_book_ids, sorter, ttm_is_first_letter=False, category=None, fm=None): QDialog.__init__(self, window) Ui_TagListEditor.__init__(self) self.setupUi(self) self.verticalLayout_2.setAlignment(Qt.AlignmentFlag.AlignCenter) self.search_box.setMinimumContentsLength(25) # Put the category name into the title bar t = self.windowTitle() self.category_name = cat_name self.category = category self.setWindowTitle(t + ' (' + cat_name + ')') # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags() & (~Qt.WindowType.WindowContextHelpButtonHint)) self.setWindowIcon(icon) # Get saved geometry info try: self.table_column_widths = \ gprefs.get('tag_list_editor_table_widths', None) except: pass # initialization self.to_rename = {} self.to_delete = set() self.all_tags = {} self.original_names = {} self.ordered_tags = [] self.sorter = sorter self.get_book_ids = get_book_ids self.text_before_editing = '' # Capture clicks on the horizontal header to sort the table columns hh = self.table.horizontalHeader() hh.sectionResized.connect(self.table_column_resized) hh.setSectionsClickable(True) hh.sectionClicked.connect(self.do_sort) hh.setSortIndicatorShown(True) self.last_sorted_by = 'name' self.name_order = 0 self.count_order = 1 self.was_order = 1 self.edit_delegate = EditColumnDelegate(self.table) self.edit_delegate.editing_finished.connect(self.stop_editing) self.edit_delegate.editing_started.connect(self.start_editing) self.table.setItemDelegateForColumn(0, self.edit_delegate) if prefs['case_sensitive']: self.string_contains = contains else: self.string_contains = self.case_insensitive_compare self.delete_button.clicked.connect(self.delete_tags) self.table.delete_pressed.connect(self.delete_pressed) self.rename_button.clicked.connect(self.rename_tag) self.undo_button.clicked.connect(self.undo_edit) self.table.itemDoubleClicked.connect(self._rename_tag) self.table.itemChanged.connect(self.finish_editing) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText( _('&OK')) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText( _('&Cancel')) self.buttonBox.accepted.connect(self.accepted) self.search_box.initialize('tag_list_search_box_' + cat_name) le = self.search_box.lineEdit() ac = le.findChild(QAction, QT_HIDDEN_CLEAR_ACTION) if ac is not None: ac.triggered.connect(self.clear_search) self.search_box.textChanged.connect(self.search_text_changed) self.search_button.clicked.connect(self.do_search) self.search_button.setDefault(True) l = QLabel(self.table) self.not_found_label = l l.setFrameStyle(QFrame.Shape.StyledPanel) l.setAutoFillBackground(True) l.setText(_('No matches found')) l.setAlignment(Qt.AlignmentFlag.AlignVCenter) l.resize(l.sizeHint()) l.move(10, 0) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.ConnectionType.QueuedConnection) self.filter_box.initialize('tag_list_filter_box_' + cat_name) le = self.filter_box.lineEdit() ac = le.findChild(QAction, QT_HIDDEN_CLEAR_ACTION) if ac is not None: ac.triggered.connect(self.clear_filter) le.returnPressed.connect(self.do_filter) self.filter_button.clicked.connect(self.do_filter) self.apply_vl_checkbox.clicked.connect(self.vl_box_changed) self.table.setEditTriggers( QAbstractItemView.EditTrigger.EditKeyPressed) try: geom = gprefs.get('tag_list_editor_dialog_geometry', None) if geom is not None: QApplication.instance().safe_restore_geometry( self, QByteArray(geom)) else: self.resize(self.sizeHint() + QSize(150, 100)) except: pass self.is_enumerated = False if fm: if fm['datatype'] == 'enumeration': self.is_enumerated = True self.enum_permitted_values = fm.get('display', {}).get( 'enum_values', None) # Add the data self.search_item_row = -1 self.fill_in_table(None, tag_to_match, ttm_is_first_letter) self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.table.customContextMenuRequested.connect(self.show_context_menu)
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) ua = 'calibre-editor-preview ' + __version__ ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_editor compile_editor() js = P('editor.js', data=True, allow_user_override=False) cparser = P('csscolorparser.js', data=True, allow_user_override=False) dark_mode_css = P('dark_mode.css', data=True, allow_user_override=False).decode('utf-8') insert_scripts( ans, create_script('csscolorparser.js', cparser), create_script('editor.js', js), create_script('dark-mode.js', ''' (function() { var settings = JSON.parse(navigator.userAgent.split('|')[1]); var dark_css = CSS; function apply_body_colors(event) { if (document.documentElement) { if (settings.bg) document.documentElement.style.backgroundColor = settings.bg; if (settings.fg) document.documentElement.style.color = settings.fg; } if (document.body) { if (settings.bg) document.body.style.backgroundColor = settings.bg; if (settings.fg) document.body.style.color = settings.fg; } } function apply_css() { var css = ''; if (settings.link) css += 'html > body :link, html > body :link * { color: ' + settings.link + ' !important; }'; if (settings.is_dark_theme) { css += dark_css; } var style = document.createElement('style'); style.textContent = css; document.documentElement.appendChild(style); apply_body_colors(); } apply_body_colors(); document.addEventListener("DOMContentLoaded", apply_css); })(); '''.replace('CSS', json.dumps(dark_mode_css), 1), injection_point=QWebEngineScript.InjectionPoint. DocumentCreation)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute( QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, False) s.setAttribute( QWebEngineSettings.WebAttribute.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans