def __reload_nodes(self, node: QTreeWidgetItem, _: int): """Mark edited node as unsaved.""" name = node.text(0) code = int(node.text(2)) if name.startswith('@'): self.__add_macro(name[1:], code) self.__root_unsaved()
def new_pointer(node: QTreeWidgetItem): """Give a new pointer code for node.""" code = self.data.new_num() self.data[code] = self.data[int(node.text(2))] node.setText(2, str(code)) for i in range(node.childCount()): new_pointer(node.child(i))
def getpath(node: QTreeWidgetItem) -> str: """Get the path of current node.""" parent = node.parent() file_name = node.text(1) if parent: return QFileInfo(QDir( node_getpath(parent)).filePath(file_name)).absoluteFilePath() return file_name
def node_getpath(node: QTreeWidgetItem) -> str: """Recursive return the path of the node.""" path = node.text(1) parent = node.parent() if parent is None: if file_suffix(path) == 'kmol': return QFileInfo(path).absolutePath() else: return path return QDir(node_getpath(parent)).filePath(path)
def __delete_node_data(self, node: QTreeWidgetItem): """Delete data from data structure.""" name = node.text(0) if name.startswith('@'): for action in self.macros_toolbar.actions(): if action.text() == name[1:]: self.macros_toolbar.removeAction(action) del self.data[int(node.text(2))] for i in range(node.childCount()): self.__delete_node_data(node.child(i))
def find_in_nodes(node: QTreeWidgetItem, last_name: str = ''): """Find the word in all nodes.""" last_name += node.text(0) if node.childCount(): last_name += '->' code = int(node.text(2)) doc = self.data[code] pattern = re.compile(text, flags) for m in pattern.finditer(doc): add_find_result(code, last_name, *m.span()) for i in range(node.childCount()): find_in_nodes(node.child(i), last_name)
def _get_root(node: QTreeWidgetItem) -> QTreeWidgetItem: """Return the top-level parent if exist.""" parent = node.parent() if parent is not None: return _get_root(parent) else: return node
def find_in_nodes(node: QTreeWidgetItem, last_name: str = ''): """Find the word in all nodes.""" last_name += node.text(0) if node.childCount(): last_name += '->' code = int(node.text(2)) doc = self.data[code] pattern = re.compile(text.encode('utf-8'), flags) for m in pattern.finditer(doc.encode('utf-8')): start, end = m.span() item = QListWidgetItem(last_name) item.setToolTip(f"{code}:{start}:{end}") self.find_list_node[code] = node self.find_list.addItem(item) for i in range(node.childCount()): find_in_nodes(node.child(i), last_name)
def _write_tree(proj_name: str, root_node: QTreeWidgetItem, data: DataDict): """Write to YAML file.""" yml_data: YMLData = {} my_codes: List[int] = [] def add_node(node: QTreeWidgetItem) -> NodeDict: code_int = int(node.text(2)) node_dict: NodeDict = { 'code': code_int, 'name': node.text(0), 'path': node.text(1), 'sub': [], } if file_suffix(node.text(1)) not in _SUPPORTED_FILE_SUFFIX: my_codes.append(code_int) if QFileInfo(QDir(node_getpath(node.parent())).filePath(node.text(1))).isFile(): # Files do not need to make a copy. return node_dict for j in range(node.childCount()): node_dict['sub'].append(add_node(node.child(j))) return node_dict root_code = int(root_node.text(2)) yml_data['description'] = root_code yml_data['node'] = [] for i in range(root_node.childCount()): yml_data['node'].append(add_node(root_node.child(i))) yml_data['data'] = {root_code: _LiteralDoc(data[root_code]) or ''} for code in my_codes: yml_data['data'][code] = _LiteralDoc(data[code]) or '' data.save_all() yml_str = ( f"# Generated by Kmol editor {__version__}\n\n" + yaml.dump(yml_data, default_flow_style=False) ) with open(proj_name, 'w', encoding='utf-8') as f: f.write(yml_str) print("Saved: {}".format(proj_name))
def _parse_tree(root_node: QTreeWidgetItem, data: DataDict): """Parse in to tree widget.""" try: with open(root_node.text(1), encoding='utf-8') as f: yaml_script = f.read() except FileNotFoundError: return yml_data: YMLData = yaml.load(yaml_script, Loader=yaml.FullLoader) parse_list: List[QTreeWidgetItem] = [] root_node.setText(2, str(yml_data['description'])) data.update(yml_data['data']) def add_node(node_dict: NodeDict) -> QTreeWidgetItem: """Add node in to tree widget.""" name: str = node_dict['name'] code_int: int = node_dict['code'] path: str = node_dict['path'] node = QTreeItem(name, path, str(code_int)) if name.startswith('@'): node.setIcon(0, file_icon("python")) data.add_macro(name[1:], code_int) suffix_text = file_suffix(path) if suffix_text: parse_list.append(node) elif path: node.setIcon(0, file_icon("directory")) subs: List[NodeDict] = node_dict['sub'] for sub in subs: node.addChild(add_node(sub)) return node child_node_dicts: List[NodeDict] = yml_data['node'] for child_node_dict in child_node_dicts: root_node.addChild(add_node(child_node_dict)) for node_item in parse_list: parse(node_item, data) data.save_all()
def __switch_data(self, current: QTreeWidgetItem, previous: QTreeWidgetItem): """Switch node function. + Auto collapse and expand function. + Important: Store the string data. """ if self.auto_expand_option.isChecked(): self.tree_main.expandItem(current) self.tree_main.scrollToItem(current) bar: QScrollBar = self.text_editor.verticalScrollBar() if previous: key = int(previous.text(2)) self.data[key] = self.text_editor.text() self.data.set_pos(key, bar.value()) if current: # Auto highlight. path = current.text(1) file_name = QFileInfo(path).fileName() suffix = QFileInfo(file_name).suffix() if current.text(0).startswith('@'): self.highlighter_option.setCurrentText("Python") else: self.highlighter_option.setCurrentText("Markdown") if path: for name_m, suffix_m in HIGHLIGHTER_SUFFIX.items(): if suffix in suffix_m: self.highlighter_option.setCurrentText(name_m) break else: for name_m, filename_m in HIGHLIGHTER_FILENAME.items(): if file_name in filename_m: self.highlighter_option.setCurrentText(name_m) break key = int(current.text(2)) self.text_editor.setText(self.data[key]) bar.setValue(self.data.pos(key)) self.reload_html_viewer() self.__action_changed()
def add_node(node_dict: NodeDict) -> QTreeWidgetItem: """Add node in to tree widget.""" name: str = node_dict['name'] code_int: int = node_dict['code'] path: str = node_dict['path'] node = QTreeWidgetItem([name, path, str(code_int)]) if name.startswith('@'): node.setIcon(0, file_icon("python")) data.add_macro(name[1:], code_int) suffix_text = file_suffix(path) if suffix_text: parse_list.append(node) elif path: node.setIcon(0, file_icon("directory")) subs: List[NodeDict] = node_dict['sub'] for sub in subs: node.addChild(add_node(sub)) return node
def add_node(node: QTreeWidgetItem) -> NodeDict: code_int = int(node.text(2)) node_dict: NodeDict = { 'code': code_int, 'name': node.text(0), 'path': node.text(1), 'sub': [], } if file_suffix(node.text(1)) not in _SUPPORTED_FILE_SUFFIX: my_codes.append(code_int) if QFileInfo(QDir(node_getpath(node.parent())).filePath(node.text(1))).isFile(): # Files do not need to make a copy. return node_dict for j in range(node.childCount()): node_dict['sub'].append(add_node(node.child(j))) return node_dict
def save_file(node: QTreeWidgetItem, data: DataDict) -> Tuple[str, bool]: """Recursive to all the contents of nodes.""" text_data = [] all_saved = data.is_saved(int(node.text(2))) for i in range(node.childCount()): doc, saved = save_file(node.child(i), data) text_data.append(doc) all_saved &= saved my_content = data[int(node.text(2))].splitlines() for i in range(len(my_content)): content_text = my_content[i] if content_text.endswith("@others"): preffix = content_text[:-len("@others")] my_content[i] = '\n\n'.join(preffix + t for t in text_data) my_content = '\n'.join(my_content) path_text = QFileInfo(node.text(1)).fileName() if path_text and not all_saved: suffix_text = QFileInfo(path_text).suffix() if suffix_text == 'kmol': # Save project. _write_tree(node.text(1), node, data) else: # File path. file_path = QDir(QFileInfo(node_getpath(node)).absolutePath()) if not file_path.exists(): file_path.mkpath('.') print("Create Folder: {}".format(file_path.absolutePath())) file_name = file_path.filePath(path_text) if suffix_text in _SUPPORTED_FILE_SUFFIX: # Add end new line. if my_content and (my_content[-1] != '\n'): my_content += '\n' try: with open(file_name, 'w', encoding='utf-8') as f: f.write(my_content) except UnicodeError: print(f"Unicode Error in: {file_name}") else: print(f"Saved: {file_name}") elif suffix_text: print(f"Ignore file: {file_name}") return my_content, all_saved
def _expand_recursive(node: QTreeWidgetItem): """Expand node and its children.""" node.setExpanded(True) for i in range(node.childCount()): _expand_recursive(node.child(i))
def _grand_parent(node: QTreeWidgetItem) -> QTreeWidgetItem: """Return the grand parent if exist.""" parent = node.parent() return (parent.parent() if parent else node.treeWidget()) or node.treeWidget()
def _get_root(node: QTreeWidgetItem) -> QTreeWidgetItem: """Return the top-level parent if exist.""" parent = node.parent() return _get_root(parent) if parent else node
def expand(node: QTreeWidgetItem, level: int): not_target = level != target_level node.setExpanded(not_target) if not_target: for i in range(node.childCount()): expand(node.child(i), level + 1)
def parse(node: QTreeWidgetItem, data: DataDict): """Parse file to tree format.""" node.takeChildren() file_name = getpath(node) suffix_text = file_suffix(file_name) if node.text(2): code = int(node.text(2)) else: code = data.new_num() node.setText(2, str(code)) if node not in LOADED_FILES and suffix_text in _SUPPORTED_FILE_SUFFIX: LOADED_FILES.append(node) if suffix_text == 'md': # Markdown node.setIcon(0, file_icon("markdown")) parse_markdown(file_name, node, code, data) elif suffix_text == 'py': # Python script node.setIcon(0, file_icon("python")) parse_text(file_name, code, data) elif suffix_text == 'html': # TODO: Need to parse HTML (reveal.js index.html) node.setIcon(0, file_icon("html")) parse_text(file_name, code, data) elif suffix_text == 'kmol': # Kmol project node.setIcon(0, file_icon("kmol")) _parse_tree(node, data) else: # Text files and Python scripts. node.setIcon(0, file_icon("txt")) parse_text(file_name, code, data) print("Loaded: {}".format(node.text(1)))