def viewSource(self): self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.adjustSize() self.textEdit.move(self.geometry().center() - self.textEdit.rect().center()) self.textEdit.show() self.view.page().toHtml(self.textEdit.setPlainText)
class TextInput(QWidget): # used when input text inputChanged = Signal() okPressed = Signal() cancelPressed = Signal() def __init__(self, parent=None): super(TextInput, self).__init__(parent) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint) self.mainLayout = QVBoxLayout() self.textArea = QTextEdit(self) self.buttonArea = QWidget(self) self.buttonLayout = QHBoxLayout() self.cancelButton = QPushButton('Cancel', self) self.okButton = QPushButton('Ok', self) self.buttonLayout.addWidget(self.cancelButton) self.buttonLayout.addWidget(self.okButton) self.buttonArea.setLayout(self.buttonLayout) self.mainLayout.addWidget(self.textArea) self.mainLayout.addWidget(self.buttonArea) self.setLayout(self.mainLayout) self.textArea.textChanged.connect(self.textChanged_) self.okButton.clicked.connect(self.okButtonClicked) self.cancelButton.clicked.connect(self.cancelPressed) def getText(self): return self.textArea.toPlainText() def getFocus(self): self.setFocus() self.textArea.setFocus() def clearText(self): self.textArea.clear() # slots def textChanged_(self): self.inputChanged.emit() def cancelButtonClicked(self): self.cancelPressed.emit() def okButtonClicked(self): self.okPressed.emit()
def __init__(self, *args, **kwargs): super(TextFieldWidget, self).__init__(*args, **kwargs) widget = QTextEdit(parent=self) # widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) widget.textChanged.connect(self._on_emit_value_changed) self.set_widget(widget)
def _line_number_highlight(self): extra_selection = QTextEdit.ExtraSelection() selections = [extra_selection] if not self.isReadOnly(): selection = QTextEdit.ExtraSelection() if dcc.is_maya(): line_color = QColor(Qt.black) else: line_color = QColor(Qt.lightGray) selection.format.setBackground(line_color) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() selections.append(selection) self.setExtraSelections(selections)
class Editor(QMainWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.current_dir = os.getcwd() self.current_file = None self.init_ui() def init_ui(self): self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit) font = QFont("Menlo", 12) # TODO: Create a layout and change the line spacing #spacing = QFontMetrics(font).lineSpacing() self.text_edit.setFont(font) self.highlighter = Highlighter(self.text_edit.document(), DEFAULT_RULES, DEFAULT_STYLE) #print("Highlighter doc: {}".format(self.text_edit.document())) menu_bar = self.menuBar() m_file = menu_bar.addMenu("File") i_open = QAction("Open", self) i_open.setShortcut('Ctrl+O') i_open.triggered.connect(self.on_open) m_file.addAction(i_open) self.setGeometry(300, 300, 350, 300) self.setWindowTitle("tea") def on_open(self): file_path, other = QFileDialog.getOpenFileName(self, "Open File", self.current_dir) #print([a for a in dir(QFileDialog) if not a.startswith("_")]) print("File name: {}, Other: {}".format(file_path, other)) if file_path: dirname = os.path.basename(os.path.dirname(file_path)) filename = os.path.basename(file_path) self.setWindowTitle("{} - {}".format(filename, dirname)) with open(file_path) as f: text = f.read() #formatted = markup_python(text) #formatted = "<font color=red size=24>{}</font>".format(text) self.text_edit.setText(text)
def ui(self): super(InfoMessage, self).ui() self.setMaximumHeight(150) info_icon = resources.icon('info') self._expandable_frame = expandables.ExpandableFrame(icon=info_icon, parent=self) self._expandable_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) expandable_layout = layouts.HorizontalLayout(margins=(2, 2, 2, 2)) texts_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._description_text = QPlainTextEdit(parent=self) self._description_text.setReadOnly(True) self._description_text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self._description_text.setFocusPolicy(Qt.NoFocus) self._description_text.setFrameShape(QFrame.NoFrame) self._instructions_widget = QWidget() instructions_layout = layouts.VerticalLayout(spacing=2, margins=(0, 0, 0, 0)) self._instructions_widget.setLayout(instructions_layout) self._instructions_text = QTextEdit(parent=self) self._instructions_text.setReadOnly(True) self._instructions_text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self._instructions_text.setFocusPolicy(Qt.NoFocus) self._instructions_text.setFrameShape(QFrame.NoFrame) self._instructions_widget.setVisible(False) # self._instructions_text.insertHtml("<ul><li>text 1</li><li>text 2</li><li>text 3</li></ul> <br />") instructions_layout.addWidget(dividers.Divider('Instructions')) instructions_layout.addWidget(self._instructions_text) texts_layout.addWidget(self._description_text) texts_layout.addWidget(self._instructions_widget) content_layout = layouts.VerticalLayout() content_layout.addLayout(texts_layout) expandable_layout.addLayout(content_layout) self._expandable_frame.addLayout(expandable_layout) self.main_layout.addWidget(self._expandable_frame)
def __init__(self, font, textColor, parent=None): super(TextEditDialog, self).__init__(parent) self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint) self.resize(QtCore.QSize(400, 300)) self.layout = QVBoxLayout(self) self.layout.setContentsMargins(2, 2, 2, 2) self.te = QTextEdit() self._font = font self._font = QtGui.QFont(font) self.te.setFont(self._font) self.te.setTextColor(textColor) self.layout.addWidget(self.te) self.buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self) self.buttons.accepted.connect(self.onAccept) self.buttons.rejected.connect(self.onReject) self.layout.addWidget(self.buttons) self._result = None self.te.textChanged.connect( lambda color=textColor: self.te.setTextColor(color))
def init_ui(self): self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit) font = QFont("Menlo", 12) # TODO: Create a layout and change the line spacing #spacing = QFontMetrics(font).lineSpacing() self.text_edit.setFont(font) self.highlighter = Highlighter(self.text_edit.document(), DEFAULT_RULES, DEFAULT_STYLE) #print("Highlighter doc: {}".format(self.text_edit.document())) menu_bar = self.menuBar() m_file = menu_bar.addMenu("File") i_open = QAction("Open", self) i_open.setShortcut('Ctrl+O') i_open.triggered.connect(self.on_open) m_file.addAction(i_open) self.setGeometry(300, 300, 350, 300) self.setWindowTitle("tea")
def __init__(self, parent=None): super(TextInput, self).__init__(parent) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint) self.mainLayout = QVBoxLayout() self.textArea = QTextEdit(self) self.buttonArea = QWidget(self) self.buttonLayout = QHBoxLayout() self.cancelButton = QPushButton('Cancel', self) self.okButton = QPushButton('Ok', self) self.buttonLayout.addWidget(self.cancelButton) self.buttonLayout.addWidget(self.okButton) self.buttonArea.setLayout(self.buttonLayout) self.mainLayout.addWidget(self.textArea) self.mainLayout.addWidget(self.buttonArea) self.setLayout(self.mainLayout) self.textArea.textChanged.connect(self.textChanged_) self.okButton.clicked.connect(self.okButtonClicked) self.cancelButton.clicked.connect(self.cancelPressed)
def __init__(self, id_, items, parent=None): super().__init__(parent) itemLabel = QLabel(self.tr("Item: ")) descriptionLabel = QLabel(self.tr("Description: ")) imageFileLabel = QLabel(self.tr("Image file: ")) self.createButtons() self.itemText = QLabel() self.descriptionEditor = QTextEdit() self.imageFileEditor = QComboBox() self.imageFileEditor.setModel(items.relationModel(1)) self.imageFileEditor.setModelColumn( items.relationModel(1).fieldIndex("file")) self.mapper = QDataWidgetMapper(self) self.mapper.setModel(items) self.mapper.setSubmitPolicy(QDataWidgetMapper.ManualSubmit) self.mapper.setItemDelegate(QSqlRelationalDelegate(self.mapper)) self.mapper.addMapping(self.imageFileEditor, 1) self.mapper.addMapping(self.itemText, 2, b"text") self.mapper.addMapping(self.descriptionEditor, 3) self.mapper.setCurrentIndex(id_) self.descriptionEditor.textChanged.connect(self.enableButtons) self.imageFileEditor.currentIndexChanged.connect(self.enableButtons) formLayout = QFormLayout() formLayout.addRow(itemLabel, self.itemText) formLayout.addRow(imageFileLabel, self.imageFileEditor) formLayout.addRow(descriptionLabel, self.descriptionEditor) layout = QVBoxLayout() layout.addLayout(formLayout) layout.addWidget(self.buttonBox) self.setLayout(layout) self.itemId = id_ self.displayedImage = self.imageFileEditor.currentText() self.setWindowFlags(Qt.Window) self.enableButtons(False) self.setWindowTitle(self.itemText.text())
def show_info_popup(self, info=None): self._info = QTextEdit() self._info.setEnabled(False) self._info.setWindowFlags(Qt.Popup) self._info.show()
class PythonCompleter(QCompleter, object): def __init__(self): super(PythonCompleter, self).__init__() self._info = None self._file_path = None self._model_strings = list() self._reset_list = True self._string_model = QStringListModel(self._model_strings, self) self._refresh_completer = True self._sub_activated = False self._last_imports = None self._last_lines = None self._last_path = None self._current_defined_imports = None self._last_path_and_part = None self._current_sub_functions = None self._last_column = 0 self.setCompletionMode(self.PopupCompletion) self.setCaseSensitivity(Qt.CaseInsensitive) self.setModel(self._string_model) self.setWrapAround(False) self.activated.connect(self._on_insert_completion) def setWidget(self, widget): super(PythonCompleter, self).setWidget(widget) self.setParent(widget) def keyPressEvent(self): return def show_info_popup(self, info=None): self._info = QTextEdit() self._info.setEnabled(False) self._info.setWindowFlags(Qt.Popup) self._info.show() def get_imports(self, paths=None): imports = self._get_available_modules(paths=paths) imports.sort() return imports def get_sub_imports(self, path): """ Returns namespace in a module :param path: str :return: str """ defined = code.get_defined(path) defined.sort() return defined def clear_completer_list(self): self._string_model.setStringList([]) def text_under_cursor(self): cursor = self.widget().textCursor() cursor.select(cursor.LineUnderCursor) return cursor.selectedText() def set_filepath(self, file_path): if not file_path: return self._file_path = file_path def handle_text(self, text): """ Parses a single line of text :param text: str :return: bool """ if not text: return False cursor = self.widget().textCursor() column = cursor.columnNumber() - 1 if column < self._last_column: self._last_column = column return False self._last_column = column if column == 1: return False text = str(text) passed = self.handle_from_import(text, column) if passed: return True passed = self.handle_sub_import(text, column) if passed: return True passed = self.handle_import_load(text, cursor) if passed: return True return False def handle_import(self, text): m = re.search(r'(from|import)(?:\s+?)(\w*)', text) if m: # TODO: Find available modules in Python path pass def handle_sub_import(self, text, column): m = re.search(r'(from|import)(?:\s+?)(\w*.?\w*)\.(\w*)$', text) if m: if column < m.end(2): return False from_module = m.group(2) module_path = code.get_package_path_from_name(from_module) last_part = m.group(3) if module_path: defined = self.get_imports(module_path) self._string_model.setStringList(defined) self.setCompletionPrefix(last_part) self.popup().setCurrentIndex(self.completionModel().index( 0, 0)) return True return False def handle_import_load(self, text, cursor): column = cursor.columnNumber() - 1 text = text[:cursor.columnNumber()] m = re.search(r'\s*([a-zA-Z0-9._]+)\.([a-zA-Z0-9_]*)$', text) block_number = cursor.blockNumber() line_number = block_number + 1 all_text = self.widget().toPlainText() scope_text = all_text[:(cursor.position() - 1)] if m and m.group(2): scope_text = all_text[:(cursor.position() - len(m.group(2)) + 1)] if not m: return False assignment = m.group(1) if column < m.end(1): return False sub_m = re.search(r'(from|import)\s+(%s)' % assignment, text) if sub_m: return False path = None sub_part = None target = None text = self.widget().toPlainText() lines = fileio.get_text_lines(text) # Search for assignments assign_map = code.get_ast_assignment(scope_text, line_number - 1, assignment) if assign_map: if assignment in assign_map: target = assign_map[assignment] else: split_assignment = assignment.split('.') inc = 1 while assignment not in assign_map: sub_assignment = string.join(split_assignment[:(inc * -1)], '.') if sub_assignment in assign_map: target = assign_map[sub_assignment] break inc += 1 if inc > (len(split_assignment) - 1): break sub_part = string.join(split_assignment[inc:], '.') module_name = m.group(1) if target and len(target) == 2: if target[0] == 'import': module_name = target[1] if not target[0] == 'import': module_name = target[0] sub_part = target[1] # import from module if module_name: imports = None if lines == self.last_lines: imports = self.last_imports if not imports: imports = code.get_line_imports(lines) self._last_imports = imports self._last_lines = lines if module_name in imports: path = imports[module_name] if module_name not in imports: split_assignment = module_name.split('.') last_part = split_assignment[-1] if last_part in imports: path = imports[last_part] if path and not sub_part: test_text = '' defined = None if path == self.last_path: defined = self.current_defined_imports if len(m.groups()) > 0: test_text = m.group(2) if not defined: defined = self.get_imports(path) if defined: self._current_defined_imports = defined else: defined = self.get_sub_imports(path) custom_defined = self.custom_import_load( assign_map, module_name) if custom_defined: defined = custom_defined if defined: if test_text and test_text[0].islower(): defined.sort(key=str.swapcase) self._string_model.setStringList(defined) self.setCompletionPrefix(test_text) self.setCaseSensitivity(Qt.CaseInsensitive) self.popup().setCurrentIndex(self.completionModel().index( 0, 0)) return True # import from a class of a module if path and sub_part: sub_functions = None if self.last_path_and_part: if path == self.last_path_and_part[ 0] and sub_part == self.last_path_and_part[1]: sub_functions = self.current_sub_functions if not sub_functions: sub_functions = code.get_ast_class_sub_functions( path, sub_part) if sub_functions: self._current_sub_functions = sub_functions self._last_path_and_part = [path, sub_part] if not sub_functions: return False test_text = '' if len(m.groups()) > 0: test_text = m.group(2) if test_text and test_text[0].islower(): sub_functions.sort(key=str.swapcase) self._string_model.setStringList(sub_functions) self.setCompletionPrefix(test_text) self.setCaseSensitivity(Qt.CaseInsensitive) self.popup().setCurrentIndex(self.completionModel().index( 0, 0)) return True module_name = m.group(1) if module_name: custom_defined = self.custom_import_load(assign_map, module_name) test_text = '' if len(m.groups()) > 0: test_text = m.group(2) if test_text and test_text[0].islower(): custom_defined.sort(key=str.swapcase) self._string_model.setStringList(custom_defined) self.setCompletionPrefix(test_text) self.setCaseSensitivity(Qt.CaseInsensitive) self.popup().setCurrentIndex(self.completionModel().index(0, 0)) return True return False def handle_from_import(self, text, column): m = re.search( r'(from)(?:\s+?)(\w*.?\w*)(?:\s+?)(import)(?:\s+?)(\w+)?$', text) if m: if column < m.end(3): return False from_module = m.group(2) module_path = code.get_package_path_from_name(from_module) last_part = m.group(4) if not last_part: last_part = '' if module_path: defined = self.get_imports(module_path) self._string_model.setStringList(defined) self.setCompletionPrefix(last_part) self.popup().setCurrentIndex(self.completionModel().index( 0, 0)) return True return False def custom_import_load(self, assign_map, moduel_name): return def _get_available_modules(self, paths=None): imports = list() if not paths: paths = sys.path if paths: paths = python.force_list(paths) for path in paths: fix_path = path_utils.normalize_path(path) stuff_in_folder = folder_utils.get_files_and_folders(fix_path) for file_or_folder in stuff_in_folder: folder_path = path_utils.join_path(fix_path, file_or_folder) files = folder_utils.get_files_with_extension('py', folder_path, full_path=False) if '__init__.py' in files: imports.append(str(file_or_folder)) python_files = folder_utils.get_files_with_extension( 'py', fix_path, full_path=False) for python_file in python_files: if python_file.startswith('__'): continue python_file_name = python_file.split('.')[0] imports.append(str(python_file_name)) if imports: imports = list(set(imports)) return imports def _on_insert_completion(self, completion_string): widget = self.widget() cursor = widget.textCursor() if completion_string == self.completionPrefix(): return extra = len(self.completionPrefix()) cursor.movePosition(QTextCursor.Left, cursor.KeepAnchor, extra) cursor.removeSelectedText() cursor.insertText(completion_string) widget.setTextCursor(cursor)
class NameIt(base.BaseWidget, object): ACTIVE_RULE = None def __init__(self, naming_lib, data_file=None, parent=None): self._naming_lib = naming_lib self._data_file = None super(NameIt, self).__init__(parent=parent) self.set_data_file(data_file) def get_active_rule(self): """ Returns the current naming active rule """ return self._naming_lib.active_rule() def set_active_rule(self, name): """ Sets the current active rule :param name: str """ # First, we clean the status of the naming library self._naming_lib.remove_all_tokens() self._naming_lib.remove_all_rules() self._naming_lib.load_session() # Load rules from the naming manager rules = self._naming_lib.rules for rule in rules: expressions = rule.get_expression_tokens() self._naming_lib.add_rule(rule.name, rule.iterator_format, *expressions) # Load tokens from the naming manager tokens = self._naming_lib.tokens for token in tokens: tokens_keywords = token.get_values_as_keyword() self._naming_lib.add_token(token.name, **tokens_keywords) self._naming_lib.set_active_rule(name) def set_active_rule_iterator(self, iterator_format): active_rule = self.get_active_rule() if not active_rule: return def set_active_rule_auto_fix(self, auto_fix): active_rule = self.get_active_rule() if not active_rule: return self._naming_lib.set_rule_auto_fix(active_rule.name(), auto_fix) def solve(self, *args, **kwargs): if len(args) > 0 and len(kwargs) > 0: return self._naming_lib.solve(*args, **kwargs) else: if len(args) > 0: return self._naming_lib.solve(*args) else: return self._naming_lib.solve(**kwargs) def ui(self): super(NameIt, self).ui() tools_toolbar = toolbar.ToolBar('Main ToolBar', parent=self) tools_toolbar.setMovable(False) # tools_toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) self.main_layout.addWidget(tools_toolbar) refresh_btn = buttons.BaseToolButton(parent=tools_toolbar) refresh_btn.setIcon(resources.icon('refresh')) refresh_btn.clicked.connect(self._on_refresh) self._refresh_btn = tools_toolbar.addWidget(refresh_btn) self._refresh_btn.setEnabled(False) save_btn = buttons.BaseToolButton(parent=tools_toolbar) save_btn.setIcon(resources.icon('save')) save_btn.clicked.connect(self._on_save) self._save_btn = tools_toolbar.addWidget(save_btn) self._save_btn.setEnabled(False) if self._is_renamer_tool_available(): renamer_btn = buttons.BaseToolButton(parent=tools_toolbar) renamer_btn.setIcon(resources.icon('rename')) renamer_btn.clicked.connect(self._on_open_renamer_tool) tools_toolbar.addWidget(renamer_btn) self._name_file_line = directory.SelectFile(label_text='Naming File', parent=tools_toolbar) tools_toolbar.addWidget(self._name_file_line) base_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self.main_layout.addLayout(base_layout) panels_splitter = QSplitter(parent=self) panels_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) base_layout.addWidget(panels_splitter) left_panel_widget = QWidget() left_panel_layout = layouts.VerticalLayout(margins=(5, 0, 5, 0)) left_panel_widget.setLayout(left_panel_layout) panels_splitter.addWidget(left_panel_widget) # Tab Widget rules_tab = QWidget() tokens_tab = QWidget() templates_tab = QWidget() templates_tokens_tab = QWidget() self.tabs = tabs.BaseTabWidget() self.tabs.addTab(rules_tab, 'Rules') self.tabs.addTab(tokens_tab, 'Tokens') self.tabs.addTab(templates_tab, 'Templates') self.tabs.addTab(templates_tokens_tab, 'Templates Tokens') left_panel_layout.addWidget(self.tabs) # Rules Tab rules_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.rules_list = QListWidget() rules_main_layout.addWidget(self.rules_list) left_panel_buttons_layout_rules = layouts.HorizontalLayout(margins=(5, 5, 5, 0)) rules_main_layout.addLayout(left_panel_buttons_layout_rules) self.add_rule_btn = buttons.BaseButton(parent=self) self.remove_rule_btn = buttons.BaseButton(parent=self) self.add_rule_btn.setIcon(resources.icon('plus')) self.remove_rule_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_rules.addStretch() left_panel_buttons_layout_rules.addWidget(self.add_rule_btn) left_panel_buttons_layout_rules.addWidget(self.remove_rule_btn) rules_tab.setLayout(rules_main_layout) # Tokens Tab tokens_main_layout = layouts.VerticalLayout(margins=(5, 5, 5, 5)) tokens_main_layout.setSpacing(0) self.tokens_list = QListWidget() tokens_main_layout.addWidget(self.tokens_list) left_panel_buttons_layout_tokens = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) tokens_main_layout.addLayout(left_panel_buttons_layout_tokens) self.add_token_btn = buttons.BaseButton(parent=self) self.remove_token_btn = buttons.BaseButton(parent=self) self.add_token_btn.setIcon(resources.icon('plus')) self.remove_token_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_tokens.addStretch() left_panel_buttons_layout_tokens.addWidget(self.add_token_btn) left_panel_buttons_layout_tokens.addWidget(self.remove_token_btn) tokens_tab.setLayout(tokens_main_layout) # Templates Tab templates_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.templates_list = QListWidget() templates_main_layout.addWidget(self.templates_list) left_panel_buttons_layout_templates = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) templates_main_layout.addLayout(left_panel_buttons_layout_templates) self.add_template_btn = buttons.BaseButton(parent=self) self.remove_template_btn = buttons.BaseButton(parent=self) self.add_template_btn.setIcon(resources.icon('plus')) self.remove_template_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_templates.addStretch() left_panel_buttons_layout_templates.addWidget(self.add_template_btn) left_panel_buttons_layout_templates.addWidget(self.remove_template_btn) templates_tab.setLayout(templates_main_layout) # Template Tokens Tab templates_tokens_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.template_tokens_list = QListWidget() templates_tokens_main_layout.addWidget(self.template_tokens_list) left_panel_buttons_layout_templates_tokens = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) left_panel_buttons_layout_templates_tokens.setContentsMargins( 5, 5, 5, 0) templates_tokens_main_layout.addLayout( left_panel_buttons_layout_templates_tokens) self.add_template_token_btn = buttons.BaseButton(parent=self) self.remove_template_token_btn = buttons.BaseButton(parent=self) self.add_template_token_btn.setIcon(resources.icon('plus')) self.remove_template_token_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_templates_tokens.addStretch() left_panel_buttons_layout_templates_tokens.addWidget( self.add_template_token_btn) left_panel_buttons_layout_templates_tokens.addWidget( self.remove_template_token_btn) templates_tokens_tab.setLayout(templates_tokens_main_layout) # === PROPERTIES === # main_group = QGroupBox('Properties') panels_splitter.addWidget(main_group) self.group_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) main_group.setLayout(self.group_layout) # Rules Panel self.rules_widget = QWidget(self) rules_layout = layouts.GridLayout() self.rules_widget.setLayout(rules_layout) expression_lbl = label.BaseLabel('Expression', parent=self) self.expression_line = lineedit.BaseLineEdit(parent=self) self.expression_btn = buttons.BaseButton(' <', parent=self) self.expression_btn.setEnabled(False) self.expression_btn.setStyleSheet( "QPushButton::menu-indicator{image:url(none.jpg);}") self.expression_menu = QMenu(self) self.expression_btn.setMenu(self.expression_menu) iterator_lbl = label.BaseLabel('Iterator', parent=self) self.iterator_cbx = combobox.BaseComboBox(parent=self) for it_format in [ '@', '@^', '#', '##', '###', '####', '#####', 'None' ]: self.iterator_cbx.addItem(it_format) description_rule_lbl = label.BaseLabel('Description', parent=self) self.description_rule_text = QTextEdit(parent=self) self.group_layout.addWidget(self.rules_widget) rules_layout.addWidget(expression_lbl, 0, 0, Qt.AlignRight) rules_layout.addWidget(self.expression_line, 0, 1) rules_layout.addWidget(self.expression_btn, 0, 2) rules_layout.addWidget(iterator_lbl, 1, 0, Qt.AlignRight | Qt.AlignTop) rules_layout.addWidget(self.iterator_cbx, 1, 1, 1, 2) rules_layout.addWidget(description_rule_lbl, 2, 0, Qt.AlignRight | Qt.AlignTop) rules_layout.addWidget(self.description_rule_text, 2, 1, 1, 2) # Tokens Panel self.tokens_widget = QWidget(parent=self) tokens_layout = layouts.GridLayout() self.tokens_widget.setLayout(tokens_layout) values_lbl = label.BaseLabel('Values') data = {'key': list(), 'value': list()} self.values_table = TokensTable(data, 0, 2) self.values_table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) values_buttons_layout = layouts.HorizontalLayout(margins=(5, 5, 5, 0)) self.add_key_value_btn = buttons.BaseButton(parent=self) self.remove_key_value_btn = buttons.BaseButton(parent=self) self.add_key_value_btn.setIcon(resources.icon('plus')) self.remove_key_value_btn.setIcon(resources.icon('minus')) values_buttons_layout.addWidget(self.add_key_value_btn) values_buttons_layout.addWidget(self.remove_key_value_btn) values_buttons_layout.addStretch() default_lbl = label.BaseLabel('Default', parent=self) self.default_cbx = combobox.BaseComboBox(parent=self) description_tokens_lbl = label.BaseLabel('Description', parent=self) self.description_token_text = QTextEdit(parent=self) self.group_layout.addWidget(self.tokens_widget) self.tokens_widget.hide() tokens_layout.addWidget(values_lbl, 0, 0, Qt.AlignRight | Qt.AlignTop) tokens_layout.addWidget(self.values_table, 0, 1, 2, 1) tokens_layout.addLayout(values_buttons_layout, 0, 2) tokens_layout.addWidget(default_lbl, 4, 0, Qt.AlignRight) tokens_layout.addWidget(self.default_cbx, 4, 1, 1, 2) tokens_layout.addWidget(description_tokens_lbl, 5, 0, Qt.AlignRight | Qt.AlignTop) tokens_layout.addWidget(self.description_token_text, 5, 1, 1, 2) # Templates Panel self.templates_widget = QWidget(parent=self) templates_layout = layouts.VerticalLayout() self.templates_widget.setLayout(templates_layout) pattern_layout = layouts.HorizontalLayout(spacing=5, margins=(5, 5, 5, 5)) pattern_lbl = label.BaseLabel('Pattern: ', parent=self) self.pattern_line = lineedit.BaseLineEdit(parent=self) pattern_layout.addWidget(pattern_lbl) pattern_layout.addWidget(self.pattern_line) templates_layout.addLayout(pattern_layout) templates_layout.addLayout(dividers.DividerLayout()) self.template_tokens_layout = layouts.GridLayout() self.template_tokens_layout.setAlignment(Qt.AlignTop) template_tokens_frame = QFrame(parent=self) template_tokens_frame.setFrameShape(QFrame.StyledPanel) template_tokens_frame.setFrameShadow(QFrame.Sunken) template_tokens_frame.setLayout(self.template_tokens_layout) templates_layout.addWidget(template_tokens_frame) self.group_layout.addWidget(self.templates_widget) self.templates_widget.hide() # Templates Tokens Panel self.templates_tokens_widget = QWidget(parent=self) templates_tokens_layout = layouts.VerticalLayout() self.templates_tokens_widget.setLayout(templates_tokens_layout) description_templates_token_layout = layouts.HorizontalLayout( spacing=5, margins=(5, 5, 5, 5)) description_tokens_layout = layouts.VerticalLayout() description_templates_token_lbl = label.BaseLabel('Description: ', parent=self) description_tokens_layout.addWidget(description_templates_token_lbl) description_tokens_layout.addStretch() self.description_templates_token_text = QTextEdit(parent=self) description_templates_token_layout.addLayout(description_tokens_layout) description_templates_token_layout.addWidget( self.description_templates_token_text) templates_tokens_layout.addLayout(description_templates_token_layout) self.group_layout.addWidget(self.templates_tokens_widget) self.templates_tokens_widget.hide() # Initialize database self._init_db() # First update of the UI self.update_expression_menu() self.update_tokens_properties_state() self.update_rules_properties_state() self.update_templates_properties_state() def setup_signals(self): super(NameIt, self).setup_signals() self.tabs.currentChanged.connect(self._on_change_tab) self._name_file_line.directoryChanged.connect( self._on_naming_file_changed) self.add_rule_btn.clicked.connect(self._on_add_rule) self.remove_rule_btn.clicked.connect(self._on_remove_rule) self.rules_list.currentItemChanged.connect(self._on_change_rule) self.rules_list.itemChanged.connect(self._on_edit_rule_name) self.expression_line.textChanged.connect(self._on_edit_rule_expression) self.description_rule_text.textChanged.connect( self._on_edit_rule_description) self.iterator_cbx.currentIndexChanged.connect( self._on_edit_rule_iterator) self.add_token_btn.clicked.connect(self._on_add_token) self.remove_token_btn.clicked.connect(self._on_remove_token) self.tokens_list.currentItemChanged.connect(self._on_change_token) self.tokens_list.itemChanged.connect(self._on_edit_token_name) self.values_table.itemChanged.connect(self._on_change_token_value) self.add_key_value_btn.clicked.connect(self._on_add_token_value) self.remove_key_value_btn.clicked.connect(self._on_remove_token_value) self.description_token_text.textChanged.connect( self._on_edit_token_description) self.default_cbx.currentIndexChanged.connect( self._on_edit_token_default) self.add_template_btn.clicked.connect(self._on_add_template) self.remove_template_btn.clicked.connect(self._on_remove_template) self.templates_list.currentItemChanged.connect( self._on_change_template) self.templates_list.itemChanged.connect(self._on_edit_template_name) self.pattern_line.textChanged.connect(self._on_edit_template_pattern) self.add_template_token_btn.clicked.connect( self._on_add_template_token) self.remove_template_token_btn.clicked.connect( self._on_remove_template_token) self.template_tokens_list.currentItemChanged.connect( self._on_change_template_token) self.template_tokens_list.itemChanged.connect( self._on_edit_template_token_name) self.description_templates_token_text.textChanged.connect( self._on_edit_template_token_description) def set_data_file(self, data_file): """ Sets the data file used by the naming library :param data_file: str """ if data_file and os.path.isfile(data_file): self._naming_lib.naming_file = data_file self._data_file = data_file else: naming_file = self._naming_lib.naming_file if naming_file and os.path.isfile(naming_file): self._data_file = self._naming_lib.naming_file else: self._data_file = self._get_default_data_file() self._naming_lib.naming_file = self._data_file self._init_db() def add_expression(self, name): """ Add an expression to the list of expressions :param str name: Expression name :return: None """ if self.expression_line.text() == '': self.expression_line.setText('{' + name + '}') else: self.expression_line.setText(self.expression_line.text() + '_{' + name + '}') def update_expression_menu(self): """ Updates the expression menu :return: """ # First, we clear the expression menu self.expression_menu.clear() tokens = self._naming_lib.tokens if tokens and len(tokens) > 0: self.expression_btn.setEnabled(True) for token in tokens: self.expression_menu.addAction( token.name, partial(self.add_expression, token.name)) else: self.expression_btn.setEnabled(False) def update_expression_state(self): # TODO: This method is used to check the name of the expression (its different parts) and # TODO: check if some token of the expression does not exist and in that case, update the expression # TODO: so it becomes a valid expression pass def update_rules_properties_state(self): if self.rules_list.count() <= 0 or self.rules_list.currentItem( ) is None: self.expression_line.setText('') self.description_rule_text.setText('') self.iterator_cbx.setCurrentIndex(0) self.rules_widget.setEnabled(False) else: rule = self._naming_lib.get_rule( self.rules_list.currentItem().text()) if rule is not None: self.expression_line.setText(rule.expression) self.description_rule_text.setText(rule.description) self.iterator_cbx.setCurrentText(rule.iterator_format) self.rules_widget.setEnabled(True) def update_tokens_properties_state(self): if self.tokens_list.currentItem() is None: self.tokens_widget.setEnabled(False) else: self.tokens_widget.setEnabled(True) def update_default_token_list(self): self.default_cbx.blockSignals(True) for i in range(self.default_cbx.count()): self.default_cbx.removeItem(self.default_cbx.count() - 1) item_text = self.tokens_list.currentItem().text() self.default_cbx.addItem('') tokens = self._naming_lib.tokens for token in tokens: if token.name != item_text: continue for value in token.values['key']: self.default_cbx.addItem(value) token = self._naming_lib.get_token(item_text) if token: self.default_cbx.setCurrentIndex(token.default) self.default_cbx.blockSignals(False) def update_tokens_key_table(self): item_text = self.tokens_list.currentItem().text() self.clean_tokens_key_table() if self.tokens_list.count() > 0: keys = [] values = [] tokens = self._naming_lib.tokens for token in tokens: if token.name != item_text: continue for i in range(len(token.values['key'])): self.values_table.insertRow(self.values_table.rowCount()) keys.append(QTableWidgetItem()) values.append(QTableWidgetItem()) for index, value in enumerate(token.values['key']): keys[index].setText(value) self.values_table.setItem(index, 0, keys[index]) for index, value in enumerate(token.values['value']): values[index].setText(value) self.values_table.setItem(index, 1, values[index]) def clean_tokens_key_table(self): for i in range(self.values_table.rowCount()): self.values_table.removeRow(self.values_table.rowCount() - 1) def update_templates_properties_state(self): if self.templates_list.count() <= 0 or self.templates_list.currentItem( ) is None: self.pattern_line.setText('') self.templates_widget.setEnabled(False) else: template = self._naming_lib.get_template( self.templates_list.currentItem().text()) if template is not None: self.pattern_line.setText(template.pattern) self.templates_widget.setEnabled(True) self._update_template_tokens(template) def _init_db(self): """ Initializes the naming data base """ self._naming_lib.init_naming_data() self._init_data() self._name_file_line.set_directory(str(self._naming_lib.naming_file)) def _init_data(self): if self._load_rules(): self._load_tokens() self._load_templates() self._load_template_tokens() def _get_default_data_file(self): """ Internal function that returns default path to nameing data file :return: str """ return os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'data', 'naming_data.json') def _load_rules(self): """ Internal function that load rules from data file """ self.rules_list.clear() rules = self._naming_lib.rules if not rules: return False for rule in rules: if rule == '_active': continue self._on_add_rule(rule) return True def _load_tokens(self): """ Load tokens from data file """ self.tokens_list.clear() tokens = self._naming_lib.tokens if not tokens: return False for token in tokens: self._on_add_token(token) return True def _load_templates(self): """ Internal function that loads templates from DB """ try: templates = self._naming_lib.templates self.templates_list.clear() if templates is not None: for template in templates: self._on_add_template(template) return True except Exception as e: logger.error( 'Error while loading templates from: {} | {} | {}'.format( self.DATA_FILE, e, traceback.format_exc())) return False def _load_template_tokens(self): """ Internal function that loads template tokens from DB """ try: template_tokens = self._naming_lib.template_tokens self.template_tokens_list.clear() if template_tokens is not None: for template_token in template_tokens: self._on_add_template_token(template_token) return True except Exception as e: logger.error( 'Error while loading template tokens from: {} | {} | {}'. format(self.DATA_FILE, e, traceback.format_exc())) return False def _clear_template_tokens(self): """ Intenral function that clears all template tokens from layout """ for i in range(self.template_tokens_layout.count(), -1, -1): item = self.template_tokens_layout.itemAt(i) if item is None: continue item.widget().setParent(None) self.template_tokens_layout.removeItem(item) def _add_template_token(self, template_token_name, template_token_description=None): """ Adds template token to layout :param template_token_name: str :param template_token_data: dict """ row = 0 while self.template_tokens_layout.itemAtPosition(row, 0) is not None: row += 1 self.template_tokens_layout.addWidget( label.BaseLabel(template_token_name), row, 0) self.template_tokens_layout.addWidget( label.BaseLabel(template_token_description if template_token_description else '< NOT FOUND >', parent=self), row, 1) def _update_template_tokens(self, template): """ Internal function that updates template tokens currently loaded :param template: Template """ if not template: return temp_tokens = list() try: temp = self._naming_lib.get_template(template.name) # temp = lucidity.Template(template.name, template.pattern) temp_template = temp.template temp_template.duplicate_placeholder_mode = lucidity.Template.STRICT temp_tokens = temp_template.keys() except (ValueError, lucidity.error.ResolveError) as exc: self._clear_template_tokens() return template_tokens = self._naming_lib.template_tokens self._clear_template_tokens() for token in temp_tokens: token_found = False for template_token in template_tokens: if token == template_token.name: self._add_template_token(token, template_token.description) token_found = True if not token_found: self._add_template_token(token) def _on_change_tab(self, tab_index): """ This methods changes the properties tab widgets :param tab_index: Index of the current tab (0:rules tab, 1:tokens tab) :return: None """ if tab_index == 0: self.rules_widget.show() self.tokens_widget.hide() self.templates_widget.hide() self.templates_tokens_widget.hide() self.update_expression_menu() self.update_expression_state() elif tab_index == 1: self.rules_widget.hide() self.tokens_widget.show() self.templates_widget.hide() self.templates_tokens_widget.hide() self.update_tokens_properties_state() elif tab_index == 2: self.rules_widget.hide() self.tokens_widget.hide() self.templates_widget.show() self.templates_tokens_widget.hide() self.update_templates_properties_state() else: self.rules_widget.hide() self.tokens_widget.hide() self.templates_widget.hide() self.templates_tokens_widget.show() def _on_naming_file_changed(self, file_path): """ Internal callback function that is called when a new file path is set :param file_path: str """ self._naming_lib.naming_file = file_path if not file_path or not os.path.isfile(file_path): self._refresh_btn.setEnabled(False) self._save_btn.setEnabled(False) else: self._refresh_btn.setEnabled(True) self._save_btn.setEnabled(True) self._on_refresh() def _on_add_rule(self, *args): """ Creates a new standard rule and add it to the Naming Manager :return: """ load_rule = True if len(args) == 0: load_rule = False self.description_rule_text.blockSignals(True) rule = None if not load_rule: rule = self._naming_lib.get_rule_unique_name(name='New_Rule') elif load_rule and len(args) == 1: rule = args[0].name if rule is not None: # Create a new item based on the rule name and add it item = QListWidgetItem(rule) item.setFlags(item.flags() | Qt.ItemIsEditable) self.rules_list.addItem(item) # Add the data of the rule to our JSON data file if len(args) == 0: self._naming_lib.add_rule(rule) # Update necessary UI Widgets if not load_rule: self.rules_list.setCurrentItem(item) self.update_expression_menu() self.update_rules_properties_state() self.description_rule_text.blockSignals(False) def _on_remove_rule(self): """ Remove the selected rule from the list of rules :return: bool, True if the element deletion is successful or False otherwise """ self.description_rule_text.blockSignals(True) curr_rule = self.rules_list.currentItem() if curr_rule is not None: rule_name = self.rules_list.currentItem().text() rule = self._naming_lib.get_rule(rule_name) if rule is not None: self._naming_lib.remove_rule(rule_name) self.rules_list.takeItem( self.rules_list.row(self.rules_list.currentItem())) self.update_rules_properties_state() self.description_rule_text.blockSignals(False) def _on_change_rule(self, rule_item): """ Change the selected rule :param rule_item: new QListWidgetItem selected :return: None """ if rule_item is not None: if rule_item.listWidget().count() > 0: rule = self._naming_lib.get_rule(rule_item.text()) if rule is not None: self.description_rule_text.setText(rule.description) self.expression_line.setText(rule.expression) self.iterator_cbx.setCurrentText(rule.iterator_format) self.update_expression_menu() self.update_rules_properties_state() def _on_edit_rule_name(self, rule_item): """ Changes name of the rule :param rule_item: Renamed QListWidgetItem :return: None """ rule_index = rule_item.listWidget().currentRow() rule = self._naming_lib.get_rule_by_index(rule_index) if rule: rule.name = rule_item.text() def _on_edit_rule_expression(self): """ Changes expression of the selected rule :return: None """ selected_rule = self.rules_list.currentItem() if not selected_rule: return rule_name = selected_rule.text() rule = self._naming_lib.get_rule(rule_name) if rule: rule.expression = self.expression_line.text() def _on_edit_rule_description(self): """ Changes description of the selected rule :return: None """ rule_name = self.rules_list.currentItem().text() rule = self._naming_lib.get_rule(rule_name) if rule: rule.description = self.description_rule_text.toPlainText() def _on_edit_rule_iterator(self, iterator_index): """ Changes iterator of the selected rule :param iterator_index: int :return: None """ rule_item = self.rules_list.currentItem() if not rule_item: return rule_name = rule_item.text() rule = self._naming_lib.get_rule(rule_name) if rule: rule.iterator_format = self.iterator_cbx.itemText(iterator_index) def _on_add_token(self, *args): """ Creates a new token and add it to the Naming Manager :return: None """ load_token = True if len(args) == 0: load_token = False token = None if not load_token: token = self._naming_lib.get_token_unique_name(name='New_Token') elif load_token and len(args) == 1: token = args[0].name if token: # Create a new item based on the token name and add it item = QListWidgetItem(token) item.setFlags(item.flags() | Qt.ItemIsEditable) self.tokens_list.addItem(item) # Add the data of the token to our JSON data file if len(args) == 0: self._naming_lib.add_token(token) # Update necessary UI wigdets if not load_token: self.tokens_list.setCurrentItem(item) def _on_remove_token(self): """ Remove the selected token from the list of tokens :return: True if the element deletion is successfull or False otherwise """ curr_token = self.tokens_list.currentItem() if curr_token is not None: token_index = self.tokens_list.currentRow() name = self.tokens_list.currentItem().text() if token_index > -1 and name is not None: token = self._naming_lib.get_token(name) if token is not None: if token.name == name: self._naming_lib.remove_token(name) self.tokens_list.takeItem( self.tokens_list.row( self.tokens_list.currentItem())) def _on_change_token(self, token_item): """ Change the selected token :param token_item: new QListWidgetItem selected :return: None """ if token_item is not None: if token_item.listWidget().count() > 0: token = self._naming_lib.get_token(token_item.text()) if token: try: self.description_rule_text.blockSignals(True) self.default_cbx.blockSignals(True) self.description_token_text.setText(token.description) self.default_cbx.setCurrentIndex(int(token.default)) finally: self.description_rule_text.blockSignals(False) self.default_cbx.blockSignals(False) self.update_tokens_properties_state() self.update_tokens_key_table() self.update_default_token_list() def _on_edit_token_name(self, token_item): """ Changes name of the token :param token_item: Renamed QListWidgetItem :return: """ token_index = self.tokens_list.currentRow() token = self._naming_lib.get_token_by_index(token_index) if token: token.name = token_item.text() def _on_change_token_value(self, item): """ Called when we change a token value name :return: None """ token_text = self.tokens_list.currentItem().text() token = self._naming_lib.get_token(token_text) if not token: return if self.tokens_list.currentRow() > -1 and item.row() > -1: if item.column() == 0: token.set_token_key(item.row(), item.text()) else: token.set_token_value(item.row(), item.text()) self.update_default_token_list() def _on_add_token_value(self, *args): self.description_rule_text.blockSignals(True) token_text = self.tokens_list.currentItem().text() token = self._naming_lib.get_token(token_text) if token: key_data = token.add_token_value() if key_data: self.clean_tokens_key_table() keys = list() values = list() for i in range(len(key_data['key'])): self.values_table.insertRow(self.values_table.rowCount()) keys.append(QTableWidgetItem()) values.append(QTableWidgetItem()) for index, value in enumerate(key_data['key']): keys[index].setText(value) self.values_table.setItem(index, 0, keys[index]) for index, value in enumerate(key_data['value']): values[index].setText(value) self.values_table.setItem(index, 1, values[index]) self.update_default_token_list() # new_index = self.default_cbx.currentIndex() + 2 # self.default_cbx.setCurrentIndex(new_index) self.description_rule_text.blockSignals(False) def _on_remove_token_value(self): """ Removes a token value from the list of tokens values """ self.description_rule_text.blockSignals(True) token_text = self.tokens_list.currentItem().text() token = self._naming_lib.get_token(token_text) if token: key_data = token.remove_token_value(self.values_table.currentRow()) if key_data: self.clean_tokens_key_table() keys = list() values = list() for i in range(len(key_data['key'])): self.values_table.insertRow(self.values_table.rowCount()) keys.append(QTableWidgetItem()) values.append(QTableWidgetItem()) for index, value in enumerate(key_data['key']): keys[index].setText(value) self.values_table.setItem(index, 0, keys[index]) for index, value in enumerate(key_data['value']): values[index].setText(value) self.values_table.setItem(index, 1, values[index]) self.update_default_token_list() new_index = self.default_cbx.currentIndex() token.default = new_index self.default_cbx.setCurrentIndex(new_index) self.description_rule_text.blockSignals(False) def _on_edit_token_default(self, index): """ Edits the default token :param index: int, index of the token to edit """ token_text = self.tokens_list.currentItem().text() token = self._naming_lib.get_token(token_text) if token: token.default = index def _on_edit_token_description(self): """ Edits the token description """ token_text = self.tokens_list.currentItem().text() token = self._naming_lib.get_token(token_text) if token: token.description = self.description_token_text.toPlainText( ).strip() def _on_add_template(self, *args): """ Creates a new template and add it to the Naming Manager :return: """ load_template = True if len(args) == 0: load_template = False template = None if not load_template: template = self._naming_lib.get_template_unique_name( 'New_Template') elif load_template and len(args) == 1: template = args[0].name if template is not None: item = QListWidgetItem(template) item.setFlags(item.flags() | Qt.ItemIsEditable) self.templates_list.addItem(item) if len(args) == 0: self._naming_lib.add_template(template) if not load_template: self.templates_list.setCurrentItem(item) def _on_remove_template(self): """ Removes the selected template from the list of templates :return: bool, True if the element deletion is successful or False otherwise """ current_template = self.templates_list.currentItem() if current_template is not None: template_index = self.templates_list.currentRow() name = self.templates_list.currentItem().text() if template_index > -1 and name is not None: template = self._naming_lib.get_template(name) if template is not None: if template.name == name: valid_remove = self._naming_lib.remove_template(name) if valid_remove: self.templates_list.takeItem( self.templates_list.row( self.templates_list.currentItem())) def _on_change_template(self, template_item): """ Changes the selected template :param template_item: new QlistWidgetItem selected """ if not template_item or not template_item.listWidget().count() > 0: return template_name = template_item.text() template = self._naming_lib.get_template(template_name) if not template: return self.pattern_line.setText(template.pattern) self.update_templates_properties_state() def _on_edit_template_name(self, template_item): """ Changes name of the template :param template_item: Renamed QListWidgetItem """ template_index = self.templates_list.currentRow() template = self._naming_lib.get_template_by_index(template_index) if template: template.name = template_item.text() def _on_edit_template_pattern(self, pattern_item): """ Changes template pattern :param pattern_item: """ template_index = self.templates_list.currentRow() template = self._naming_lib.get_template_by_index(template_index) if template: template.pattern = self.pattern_line.text() self._update_template_tokens(template) def _on_add_template_token(self, *args): """ Creates a new template token :param args: :return: """ load_template_token = True if len(args) == 0: load_template_token = False template_token = None if not load_template_token: template_token = self._naming_lib.get_template_token_unique_name( 'New_Template_Token') elif load_template_token and len(args) == 1: template_token = args[0].name if template_token is not None: item = QListWidgetItem(template_token) item.setFlags(item.flags() | Qt.ItemIsEditable) self.template_tokens_list.addItem(item) if len(args) == 0: self._naming_lib.add_template_token(template_token) if not load_template_token: self.template_tokens_list.setCurrentItem(item) def _on_remove_template_token(self): """ Remove the selected template token from the list of template tokens :return: """ curr_template_token = self.template_tokens_list.currentItem() if curr_template_token is not None: template_token_index = self.template_tokens_list.currentRow() name = self.template_tokens_list.currentItem().text() if template_token_index > -1 and name is not None: template = self._naming_lib.get_template_token(name) if template.name == name: valid_remove = self._naming_lib.remove_template_token(name) if valid_remove: self.template_tokens_list.takeItem( self.template_tokens_list.row( self.template_tokens_list.currentItem())) def _on_change_template_token(self, template_token_item): """ Changes the selected template token :param template_item: new QlistWidgetItem selected """ if not template_token_item or not template_token_item.listWidget( ).count() > 0: return template_tokien_name = template_token_item.text() template_token = self._naming_lib.get_template_token( template_tokien_name) if not template_token: return self.description_templates_token_text.setText( template_token.description) def _on_edit_template_token_name(self, token_template_item): """ Changes name of the token :param token_template_item: Renamed QListWidgetItem :return: """ token_index = self.template_tokens_list.currentRow() template_token = self._naming_lib.get_template_token_by_index( token_index) if template_token: template_token.name = token_template_item.text() def _on_edit_template_token_description(self): """ Edits the template token description """ template_token_text = self.template_tokens_list.currentItem().text() template_token = self._naming_lib.get_template_token( template_token_text) if template_token: template_token.description = self.description_templates_token_text.toPlainText( ).rstrip() def _on_open_renamer_tool(self): """ Internal function that is used by toolbar to open Renamer Tool """ try: tools.ToolsManager().launch_tool_by_id( 'tpDcc-tools-renamer', naming_file=self._naming_lib.naming_file) except Exception: logger.warning('tpDcc-tools-renamer is not available!') return None def _is_renamer_tool_available(self): """ Returns whether or not tpRenamer tool is available or not :return: bool """ try: import tpDcc.tools.renamer except Exception: return False return True def _on_refresh(self): """ Internal function that is called when save button is pressed """ self._naming_lib.load_session() self._init_data() def _on_save(self): """ Internal function that is called when save button is pressed """ self._naming_lib.save_session()
def ui(self): super(NameIt, self).ui() tools_toolbar = toolbar.ToolBar('Main ToolBar', parent=self) tools_toolbar.setMovable(False) # tools_toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) self.main_layout.addWidget(tools_toolbar) refresh_btn = buttons.BaseToolButton(parent=tools_toolbar) refresh_btn.setIcon(resources.icon('refresh')) refresh_btn.clicked.connect(self._on_refresh) self._refresh_btn = tools_toolbar.addWidget(refresh_btn) self._refresh_btn.setEnabled(False) save_btn = buttons.BaseToolButton(parent=tools_toolbar) save_btn.setIcon(resources.icon('save')) save_btn.clicked.connect(self._on_save) self._save_btn = tools_toolbar.addWidget(save_btn) self._save_btn.setEnabled(False) if self._is_renamer_tool_available(): renamer_btn = buttons.BaseToolButton(parent=tools_toolbar) renamer_btn.setIcon(resources.icon('rename')) renamer_btn.clicked.connect(self._on_open_renamer_tool) tools_toolbar.addWidget(renamer_btn) self._name_file_line = directory.SelectFile(label_text='Naming File', parent=tools_toolbar) tools_toolbar.addWidget(self._name_file_line) base_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self.main_layout.addLayout(base_layout) panels_splitter = QSplitter(parent=self) panels_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) base_layout.addWidget(panels_splitter) left_panel_widget = QWidget() left_panel_layout = layouts.VerticalLayout(margins=(5, 0, 5, 0)) left_panel_widget.setLayout(left_panel_layout) panels_splitter.addWidget(left_panel_widget) # Tab Widget rules_tab = QWidget() tokens_tab = QWidget() templates_tab = QWidget() templates_tokens_tab = QWidget() self.tabs = tabs.BaseTabWidget() self.tabs.addTab(rules_tab, 'Rules') self.tabs.addTab(tokens_tab, 'Tokens') self.tabs.addTab(templates_tab, 'Templates') self.tabs.addTab(templates_tokens_tab, 'Templates Tokens') left_panel_layout.addWidget(self.tabs) # Rules Tab rules_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.rules_list = QListWidget() rules_main_layout.addWidget(self.rules_list) left_panel_buttons_layout_rules = layouts.HorizontalLayout(margins=(5, 5, 5, 0)) rules_main_layout.addLayout(left_panel_buttons_layout_rules) self.add_rule_btn = buttons.BaseButton(parent=self) self.remove_rule_btn = buttons.BaseButton(parent=self) self.add_rule_btn.setIcon(resources.icon('plus')) self.remove_rule_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_rules.addStretch() left_panel_buttons_layout_rules.addWidget(self.add_rule_btn) left_panel_buttons_layout_rules.addWidget(self.remove_rule_btn) rules_tab.setLayout(rules_main_layout) # Tokens Tab tokens_main_layout = layouts.VerticalLayout(margins=(5, 5, 5, 5)) tokens_main_layout.setSpacing(0) self.tokens_list = QListWidget() tokens_main_layout.addWidget(self.tokens_list) left_panel_buttons_layout_tokens = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) tokens_main_layout.addLayout(left_panel_buttons_layout_tokens) self.add_token_btn = buttons.BaseButton(parent=self) self.remove_token_btn = buttons.BaseButton(parent=self) self.add_token_btn.setIcon(resources.icon('plus')) self.remove_token_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_tokens.addStretch() left_panel_buttons_layout_tokens.addWidget(self.add_token_btn) left_panel_buttons_layout_tokens.addWidget(self.remove_token_btn) tokens_tab.setLayout(tokens_main_layout) # Templates Tab templates_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.templates_list = QListWidget() templates_main_layout.addWidget(self.templates_list) left_panel_buttons_layout_templates = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) templates_main_layout.addLayout(left_panel_buttons_layout_templates) self.add_template_btn = buttons.BaseButton(parent=self) self.remove_template_btn = buttons.BaseButton(parent=self) self.add_template_btn.setIcon(resources.icon('plus')) self.remove_template_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_templates.addStretch() left_panel_buttons_layout_templates.addWidget(self.add_template_btn) left_panel_buttons_layout_templates.addWidget(self.remove_template_btn) templates_tab.setLayout(templates_main_layout) # Template Tokens Tab templates_tokens_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.template_tokens_list = QListWidget() templates_tokens_main_layout.addWidget(self.template_tokens_list) left_panel_buttons_layout_templates_tokens = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) left_panel_buttons_layout_templates_tokens.setContentsMargins( 5, 5, 5, 0) templates_tokens_main_layout.addLayout( left_panel_buttons_layout_templates_tokens) self.add_template_token_btn = buttons.BaseButton(parent=self) self.remove_template_token_btn = buttons.BaseButton(parent=self) self.add_template_token_btn.setIcon(resources.icon('plus')) self.remove_template_token_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_templates_tokens.addStretch() left_panel_buttons_layout_templates_tokens.addWidget( self.add_template_token_btn) left_panel_buttons_layout_templates_tokens.addWidget( self.remove_template_token_btn) templates_tokens_tab.setLayout(templates_tokens_main_layout) # === PROPERTIES === # main_group = QGroupBox('Properties') panels_splitter.addWidget(main_group) self.group_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) main_group.setLayout(self.group_layout) # Rules Panel self.rules_widget = QWidget(self) rules_layout = layouts.GridLayout() self.rules_widget.setLayout(rules_layout) expression_lbl = label.BaseLabel('Expression', parent=self) self.expression_line = lineedit.BaseLineEdit(parent=self) self.expression_btn = buttons.BaseButton(' <', parent=self) self.expression_btn.setEnabled(False) self.expression_btn.setStyleSheet( "QPushButton::menu-indicator{image:url(none.jpg);}") self.expression_menu = QMenu(self) self.expression_btn.setMenu(self.expression_menu) iterator_lbl = label.BaseLabel('Iterator', parent=self) self.iterator_cbx = combobox.BaseComboBox(parent=self) for it_format in [ '@', '@^', '#', '##', '###', '####', '#####', 'None' ]: self.iterator_cbx.addItem(it_format) description_rule_lbl = label.BaseLabel('Description', parent=self) self.description_rule_text = QTextEdit(parent=self) self.group_layout.addWidget(self.rules_widget) rules_layout.addWidget(expression_lbl, 0, 0, Qt.AlignRight) rules_layout.addWidget(self.expression_line, 0, 1) rules_layout.addWidget(self.expression_btn, 0, 2) rules_layout.addWidget(iterator_lbl, 1, 0, Qt.AlignRight | Qt.AlignTop) rules_layout.addWidget(self.iterator_cbx, 1, 1, 1, 2) rules_layout.addWidget(description_rule_lbl, 2, 0, Qt.AlignRight | Qt.AlignTop) rules_layout.addWidget(self.description_rule_text, 2, 1, 1, 2) # Tokens Panel self.tokens_widget = QWidget(parent=self) tokens_layout = layouts.GridLayout() self.tokens_widget.setLayout(tokens_layout) values_lbl = label.BaseLabel('Values') data = {'key': list(), 'value': list()} self.values_table = TokensTable(data, 0, 2) self.values_table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) values_buttons_layout = layouts.HorizontalLayout(margins=(5, 5, 5, 0)) self.add_key_value_btn = buttons.BaseButton(parent=self) self.remove_key_value_btn = buttons.BaseButton(parent=self) self.add_key_value_btn.setIcon(resources.icon('plus')) self.remove_key_value_btn.setIcon(resources.icon('minus')) values_buttons_layout.addWidget(self.add_key_value_btn) values_buttons_layout.addWidget(self.remove_key_value_btn) values_buttons_layout.addStretch() default_lbl = label.BaseLabel('Default', parent=self) self.default_cbx = combobox.BaseComboBox(parent=self) description_tokens_lbl = label.BaseLabel('Description', parent=self) self.description_token_text = QTextEdit(parent=self) self.group_layout.addWidget(self.tokens_widget) self.tokens_widget.hide() tokens_layout.addWidget(values_lbl, 0, 0, Qt.AlignRight | Qt.AlignTop) tokens_layout.addWidget(self.values_table, 0, 1, 2, 1) tokens_layout.addLayout(values_buttons_layout, 0, 2) tokens_layout.addWidget(default_lbl, 4, 0, Qt.AlignRight) tokens_layout.addWidget(self.default_cbx, 4, 1, 1, 2) tokens_layout.addWidget(description_tokens_lbl, 5, 0, Qt.AlignRight | Qt.AlignTop) tokens_layout.addWidget(self.description_token_text, 5, 1, 1, 2) # Templates Panel self.templates_widget = QWidget(parent=self) templates_layout = layouts.VerticalLayout() self.templates_widget.setLayout(templates_layout) pattern_layout = layouts.HorizontalLayout(spacing=5, margins=(5, 5, 5, 5)) pattern_lbl = label.BaseLabel('Pattern: ', parent=self) self.pattern_line = lineedit.BaseLineEdit(parent=self) pattern_layout.addWidget(pattern_lbl) pattern_layout.addWidget(self.pattern_line) templates_layout.addLayout(pattern_layout) templates_layout.addLayout(dividers.DividerLayout()) self.template_tokens_layout = layouts.GridLayout() self.template_tokens_layout.setAlignment(Qt.AlignTop) template_tokens_frame = QFrame(parent=self) template_tokens_frame.setFrameShape(QFrame.StyledPanel) template_tokens_frame.setFrameShadow(QFrame.Sunken) template_tokens_frame.setLayout(self.template_tokens_layout) templates_layout.addWidget(template_tokens_frame) self.group_layout.addWidget(self.templates_widget) self.templates_widget.hide() # Templates Tokens Panel self.templates_tokens_widget = QWidget(parent=self) templates_tokens_layout = layouts.VerticalLayout() self.templates_tokens_widget.setLayout(templates_tokens_layout) description_templates_token_layout = layouts.HorizontalLayout( spacing=5, margins=(5, 5, 5, 5)) description_tokens_layout = layouts.VerticalLayout() description_templates_token_lbl = label.BaseLabel('Description: ', parent=self) description_tokens_layout.addWidget(description_templates_token_lbl) description_tokens_layout.addStretch() self.description_templates_token_text = QTextEdit(parent=self) description_templates_token_layout.addLayout(description_tokens_layout) description_templates_token_layout.addWidget( self.description_templates_token_text) templates_tokens_layout.addLayout(description_templates_token_layout) self.group_layout.addWidget(self.templates_tokens_widget) self.templates_tokens_widget.hide() # Initialize database self._init_db() # First update of the UI self.update_expression_menu() self.update_tokens_properties_state() self.update_rules_properties_state() self.update_templates_properties_state()
class InfoMessage(base.BaseWidget, object): def __init__(self, name='', description='', instructions='', parent=None): self._name = '' self._description = '' self._instructions = instructions super(InfoMessage, self).__init__(parent) self.setAttribute(Qt.WA_StyledBackground) self.theme_type = message.MessageTypes.INFO self.style().polish(self) self.name = name self.description = description self.instructions = instructions # ================================================================================================================= # PROPERTIES # ================================================================================================================= def _get_name(self): return self._name def _set_name(self, name): self._name = str(name) self._expandable_frame.set_title(self._name) def _get_description(self): return self._description def _set_description(self, text): self._description = str(text) self._description_text.setPlainText(self._description) def _get_instructions(self): return self._instructions def _set_instructions(self, instructions): self._instructions = str(instructions) self._instructions_text.clear() self._instructions_text.insertHtml(instructions) self._instructions_widget.setVisible(bool(instructions)) name = Property(str, _get_name, _set_name) description = Property(str, _get_description, _set_description) instructions = Property(str, _get_instructions, _set_instructions) # ================================================================================================================= # OVERRIDES # ================================================================================================================= def ui(self): super(InfoMessage, self).ui() self.setMaximumHeight(150) info_icon = resources.icon('info') self._expandable_frame = expandables.ExpandableFrame(icon=info_icon, parent=self) self._expandable_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) expandable_layout = layouts.HorizontalLayout(margins=(2, 2, 2, 2)) texts_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._description_text = QPlainTextEdit(parent=self) self._description_text.setReadOnly(True) self._description_text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self._description_text.setFocusPolicy(Qt.NoFocus) self._description_text.setFrameShape(QFrame.NoFrame) self._instructions_widget = QWidget() instructions_layout = layouts.VerticalLayout(spacing=2, margins=(0, 0, 0, 0)) self._instructions_widget.setLayout(instructions_layout) self._instructions_text = QTextEdit(parent=self) self._instructions_text.setReadOnly(True) self._instructions_text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self._instructions_text.setFocusPolicy(Qt.NoFocus) self._instructions_text.setFrameShape(QFrame.NoFrame) self._instructions_widget.setVisible(False) # self._instructions_text.insertHtml("<ul><li>text 1</li><li>text 2</li><li>text 3</li></ul> <br />") instructions_layout.addWidget(dividers.Divider('Instructions')) instructions_layout.addWidget(self._instructions_text) texts_layout.addWidget(self._description_text) texts_layout.addWidget(self._instructions_widget) content_layout = layouts.VerticalLayout() content_layout.addLayout(texts_layout) expandable_layout.addLayout(content_layout) self._expandable_frame.addLayout(expandable_layout) self.main_layout.addWidget(self._expandable_frame)
def _on_show_detail(self): dlg = QTextEdit(self) dlg.setReadOnly(True) geo = QApplication.desktop().screenGeometry() dlg.setGeometry(geo.width() / 2, geo.height() / 2, geo.width() / 4, geo.height() / 4) dlg.setWindowTitle('Error Detail Information') dlg.setText(self.property('history')) dlg.setWindowFlags(Qt.Dialog) dlg.show()
class TextEditDialog(QDialog): def __init__(self, font, textColor, parent=None): super(TextEditDialog, self).__init__(parent) self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint) self.resize(QtCore.QSize(400, 300)) self.layout = QVBoxLayout(self) self.layout.setContentsMargins(2, 2, 2, 2) self.te = QTextEdit() self._font = font self._font = QtGui.QFont(font) self.te.setFont(self._font) self.te.setTextColor(textColor) self.layout.addWidget(self.te) self.buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self) self.buttons.accepted.connect(self.onAccept) self.buttons.rejected.connect(self.onReject) self.layout.addWidget(self.buttons) self._result = None self.te.textChanged.connect( lambda color=textColor: self.te.setTextColor(color)) def zoomIn(self, factor): self.te.zoomIn(factor) def setHtml(self, html): self.te.setHtml(html) def onReject(self): self._result = "", False self.reject() def getResult(self): return self._result def onAccept(self): self.te.selectAll() # self._font.setPointSize(self.initialPointSize) self.te.setFont(self._font) self._result = self.te.toHtml(), True self.accept()
class MainWindow(QMainWindow): def __init__(self, url): super().__init__() self.setAttribute(Qt.WA_DeleteOnClose, True) self.progress = 0 f = QFile() f.setFileName(":/jquery.min.js") f.open(QIODevice.ReadOnly) self.jQuery = f.readAll().data().decode() self.jQuery += "\nvar qt = { 'jQuery': jQuery.noConflict(true) };" f.close() self.view = QWebEngineView(self) self.view.load(url) self.view.loadFinished.connect(self.adjustLocation) self.view.titleChanged.connect(self.adjustTitle) self.view.loadProgress.connect(self.setProgress) self.view.loadFinished.connect(self.finishLoading) self.locationEdit = QLineEdit(self) self.locationEdit.setSizePolicy( QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy()) self.locationEdit.returnPressed.connect(self.changeLocation) toolBar = self.addToolBar(self.tr("Navigation")) toolBar.addAction(self.view.pageAction(QWebEnginePage.Back)) toolBar.addAction(self.view.pageAction(QWebEnginePage.Forward)) toolBar.addAction(self.view.pageAction(QWebEnginePage.Reload)) toolBar.addAction(self.view.pageAction(QWebEnginePage.Stop)) toolBar.addWidget(self.locationEdit) viewMenu = self.menuBar().addMenu(self.tr("&View")) viewSourceAction = QAction(self.tr("Page Source"), self) viewSourceAction.triggered.connect(self.viewSource) viewMenu.addAction(viewSourceAction) effectMenu = self.menuBar().addMenu(self.tr("&Effect")) effectMenu.addAction(self.tr("Highlight all links"), self.highlightAllLinks) self.rotateAction = QAction(self) self.rotateAction.setIcon(self.style().standardIcon( QStyle.SP_FileDialogDetailedView)) self.rotateAction.setCheckable(True) self.rotateAction.setText(self.tr("Turn images upside down")) self.rotateAction.toggled.connect(self.rotateImages) effectMenu.addAction(self.rotateAction) toolsMenu = self.menuBar().addMenu(self.tr("&Tools")) toolsMenu.addAction(self.tr("Remove GIF images"), self.removeGifImages) toolsMenu.addAction(self.tr("Remove all inline frames"), self.removeInlineFrames) toolsMenu.addAction(self.tr("Remove all object elements"), self.removeObjectElements) toolsMenu.addAction(self.tr("Remove all embedded elements"), self.removeEmbeddedElements) self.setCentralWidget(self.view) @Slot() def adjustLocation(self): self.locationEdit.setText(self.view.url().toString()) @Slot() def changeLocation(self): url = QUrl.fromUserInput(self.locationEdit.text()) self.view.load(url) self.view.setFocus() @Slot() def adjustTitle(self): if self.progress <= 0 or self.progress >= 100: self.setWindowTitle(self.view.title()) else: self.setWindowTitle("%s (%2d)" % (self.view.title(), self.progress)) @Slot(int) def setProgress(self, p): self.progress = p self.adjustTitle() @Slot() def finishLoading(self): self.progress = 100 self.adjustTitle() self.view.page().runJavaScript(self.jQuery) self.rotateImages(self.rotateAction.isChecked()) @Slot() def viewSource(self): self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.adjustSize() self.textEdit.move(self.geometry().center() - self.textEdit.rect().center()) self.textEdit.show() self.view.page().toHtml(self.textEdit.setPlainText) @Slot() def highlightAllLinks(self): code = "qt.jQuery('a').each( function () { qt.jQuery(this).css('background-color', 'yellow') } )" self.view.page().runJavaScript(code) @Slot(bool) def rotateImages(self, invert): code = "" if invert: code = "qt.jQuery('img').each( function () { qt.jQuery(this).css('transition', 'transform 2s'); qt.jQuery(this).css('transform', 'rotate(180deg)') } )" # noqa: E501 else: code = "qt.jQuery('img').each( function () { qt.jQuery(this).css('transition', 'transform 2s'); qt.jQuery(this).css('transform', 'rotate(0deg)') } )" # noqa: E501 self.view.page().runJavaScript(code) @Slot() def removeGifImages(self): code = "qt.jQuery('[src*=gif]').remove()" self.view.page().runJavaScript(code) @Slot() def removeInlineFrames(self): code = "qt.jQuery('iframe').remove()" self.view.page().runJavaScript(code) @Slot() def removeObjectElements(self): code = "qt.jQuery('object').remove()" self.view.page().runJavaScript(code) @Slot() def removeEmbeddedElements(self): code = "qt.jQuery('embed').remove()" self.view.page().runJavaScript(code)
def embed_readme_images(readme_path): """ QTextEdit used to auto detect raw / html / markdown, and do the conversion to html. """ readme_dir = os.path.dirname(readme_path) dada = None with open(readme_path) as f: dada = f.read() # strip old embeded dada dada = re.sub(r'source=\"data:image.*\"', "", dada) editor = QTextEdit() editor.setText(dada) editor.document().setMetaInformation(QTextDocument.DocumentUrl, readme_dir) html = editor.toHtml() image_elements = list() root = ElementTree.fromstring(html) for element in root.iter(): for sub in element[:]: if sub.tag == "head": element.remove(sub) element.attrib.pop("style", None) if element.tag == "img": image_elements.append(element) width_scalar = calculate_width_scalar(readme_dir, image_elements, 577.0) for img in image_elements: rel_src_path = img.attrib.get("src", "") if rel_src_path.startswith("data:image"): print( "Embeded src. skipping! (readme_path={readme_path!r})".format( **locals())) return if rel_src_path: src_path = os.path.abspath(os.path.join(readme_dir, rel_src_path)) if not os.path.isfile(src_path): print( "image file missing! (readme_dir={readme_dir!r}, src_path={src_path!r})" .format(**locals())) else: rel_src_path = os.path.relpath(src_path, readme_dir) rel_src_path = "./" + rel_src_path.replace("\\", "/") # attribute order must be correct !!! # ElementTree in python<3.8 uses sort(dict.items()) # sooo... lets make sure that there is alphaphetical dummy prefix... new_attributes = [ ("001_DEL_XML_SUCKS_src", rel_src_path), ("002_DEL_XML_SUCKS_source", embeded_image_scale_to_width(src_path, width_scalar=width_scalar)) ] new_attributes.extend((k, v) for k, v in sorted(img.attrib.items()) if k not in {"src", "source"}) img.attrib.clear() img.attrib.update(new_attributes) html = ElementTree.tostring(root).decode() html = re.sub(r"\d{3}_DEL_XML_SUCKS_", "", html) with open(readme_path, "wb") as f: f.write(html.encode("utf-8"))