import threading from PySide2 import QtCore from PySide2.QtUiTools import QUiLoader from PySide2.QtWidgets import * from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtUiTools import QUiLoader from ui.ui_Form import Ui_Form from ui.ui_SideBar import Ui_SideBar from ui.lineNumberWidget import LineNumberWidget from ui.ui_LineEdit import Ui_LineEdit from ui.Analyze import AnalyzeWindow import GuiType as GuiType def get_sub_win(mdiArea:QMdiArea, class_type, title=''): for mid_sub_win in mdiArea.subWindowList(): sub_win = mid_sub_win.widget() if type(sub_win) == class_type: if title: if sub_win.windowTitle() == title: return mid_sub_win else: return mid_sub_win return def get_sub_win_list(mdiArea:QMdiArea, class_type): win_list = [] for mid_sub_win in mdiArea.subWindowList(): sub_win = mid_sub_win.widget() if type(sub_win) == class_type: win_list.append(mid_sub_win) return win_list def ensure_subwin_in_midarea(mdiArea:QMdiArea, win:QWidget, pos:QPoint=None): if pos ==None: pos = win.pos() if pos.x() in range(int(win.width()*0.3),mdiArea.width()-win.width()): x = pos.x()-win.width()*0.3 elif pos.x() < 0: x = 0 else: x = mdiArea.width() - win.width() if pos.y() in range(int(win.height()*0.3), mdiArea.height()-win.height()): y = pos.y()-win.height()*0.3 elif pos.y() < 0: y = 0 else: y = mdiArea.height() - win.height() return QPoint(x, y) # 动态窗口,初始化时会添加动作到 菜单栏 - window 中 class MdiActionWindow(QMainWindow): def __init__(self, mainWindow): super().__init__(mainWindow) self.mainWindow = mainWindow self.regist_action(mainWindow) # 修改窗口标题时同步修改menu-Action信息 self.windowTitleChanged.connect(lambda: self.menuWindow_action.setText(self.windowTitle())) def closeEvent(self, event: QCloseEvent): self.mainWindow.menuWindow.removeAction(self.menuWindow_action) return super().closeEvent(event) def regist_action(self, mainWindow): action = QAction(mainWindow) action.setText(self.windowTitle()) mainWindow.menuWindow.insertAction(mainWindow.action_win_separator,action) action.setActionGroup(mainWindow.action_win_group) action.setCheckable(True) action.setChecked(True) action.triggered.connect(self.active_win) self.menuWindow_action = action def active_win(self): self.hide() self.show() class FormWindow(Ui_Form, MdiActionWindow): def __init__(self, mainWindow, instan:GuiType._GuiWinForm, pos:QPoint=None) -> None: super().__init__(mainWindow) self.setupUi(self) mdiArea:QMdiArea = mainWindow.mdiArea self.instan = instan self.setWindowTitle(instan.Title) self.reportButton.clicked.connect(self.report_virtual_timing) self.reportButton.setHidden(not instan._hasReportVirtualTimingButton) if instan._hasCrossProbeMenu: self.tableWidget.customContextMenuRequested.connect(self.right_click_menu) self.tableWidget.setModel(FormWindow.get_form_model(self.instan.Form)) self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) self.mdisubwin:QMdiSubWindow = mainWindow.mdiArea.addSubWindow(self) self.mdisubwin.setAttribute(Qt.WA_DeleteOnClose) self.mdisubwin.setWindowIcon(QIcon(":/icon/resource/todo-line.png")) # mdisubwin.move(mdiArea.x()+(mdiArea.width()-self.width())/2 + self.width()*0.8,mdiArea.y()+(mdiArea.height()-self.height())/2) self.mdisubwin.show() width = FormWindow.get_table_width(self.tableWidget, self) self.mdisubwin.resize(width, self.height()) # 必须要show之后才能 ensure_subwin_in_midarea,因为没有显示时Form窗口大小是不正确的 # 移动到鼠标 pos 位置 if pos: self.mdisubwin.move(ensure_subwin_in_midarea(mdiArea, self, pos)) def get_form_model(form:GuiType._GuiForm): model = QStandardItemModel(0, len(form.FixedRow)) model.setHorizontalHeaderLabels(form.FixedRow) for row in form.Rows: row_items=[QStandardItem(text) for text in row] model.appendRow(row_items) return model def get_table_width(tableWidget:QTableWidget, win:MdiActionWindow): # 表格与窗口的边距宽度 width = 28 for i in range(tableWidget.horizontalHeader().count()): width += tableWidget.columnWidth(i) if width < win.width(): width = win.width() return width def right_click_menu(self, pos): pop_menu = QMenu() pop_menu.addAction(QAction('Crossprobe', pop_menu)) pop_menu.triggered.connect(self.crossprobe) pop_menu.exec_(QCursor.pos()) def get_row(tableWidget:QTableView): form_row = [] for col in range(tableWidget.model().columnCount()): form_row.append(tableWidget.model().index(tableWidget.currentIndex().row(), col).data()) return form_row def crossprobe(self, action): form_row = FormWindow.get_row(self.tableWidget) line_text_win = self.instan.CrossProbe(form_row) LineEditWin(self.mainWindow, line_text_win) def report_virtual_timing(self): if self.tableWidget.currentIndex().row() == -1: return form_row = FormWindow.get_row(self.tableWidget) thread = threading.Thread(target=self.instan.ReportVirtualTiming, args=(form_row, self.mainWindow.GUI,)) thread.start() class MultiFormWin(Ui_Form, MdiActionWindow): def __init__(self, mainWindow, instan:GuiType._GuiWinFormMulti) -> None: super().__init__(mainWindow) self.setupUi(self) self.init_multi_form_ui() self.setWindowTitle(instan.Title) MultiFormWin.set_form_multi(self.tableWidget, self.sheets, instan.Forms) self.instan = instan self.sheets.activated.connect(lambda index: MultiFormWin.activate_table_sheet(self.tableWidget, self.instan.Forms, index)) self.reportButton.clicked.connect(self.report_virtual_timing) mdiArea:QMdiArea = mainWindow.mdiArea mdisub = mdiArea.addSubWindow(self) width = self.tableWidget.horizontalHeader().width() * self.tableWidget.horizontalHeader().count() + 25 mdisub.setWindowIcon(QIcon(":/icon/resource/todo-line.png")) mdisub.resize(width, self.height()) mdisub.show() def set_form_multi(tableWidget:QTableView, sheets:QComboBox, multi_form:GuiType._GuiFormMulti): sheets.clear() for sheet_name in multi_form.Names: sheets.addItem(sheet_name) if sheets.count() > 0: form = multi_form.GetForm(sheets.currentIndex()) model = FormWindow.get_form_model(form) tableWidget.setModel(model) def activate_table_sheet(tableWidget, form_multi_instan, index): model = FormWindow.get_form_model(form_multi_instan.GetForm(index)) tableWidget.setModel(model) def crossprobe(self, action): form_row = FormWindow.get_row(self.tableWidget) form_name = self.sheets.currentText() line_text_win = self.instan.CrossProbe(form_name, form_row) LineEditWin(self.mainWindow, line_text_win) def report_virtual_timing(self): if self.tableWidget.currentIndex().row() == -1: return form_row = FormWindow.get_row(self.tableWidget) thread = threading.Thread(target=self.instan.ReportVirtualTiming, args=(self.sheets.currentText(), form_row, self.mainWindow.GUI,)) thread.start() def init_multi_form_ui(self): self.horizontalLayout_2 = QHBoxLayout() self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.sheets = QComboBox(self.centralwidget) self.sheets.setObjectName(u"sheets") self.horizontalLayout_2.addWidget(self.sheets) self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_2.addItem(self.horizontalSpacer_3) self.horizontalLayout_2.setStretch(0, 3) self.horizontalLayout_2.setStretch(1, 6) self.verticalLayout.insertLayout(0,self.horizontalLayout_2) self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.tableWidget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.tableWidget.customContextMenuRequested.connect(self.right_click_menu) def right_click_menu(self, pos): pop_menu = QMenu() pop_menu.addAction(QAction('Crossprobe', pop_menu)) pop_menu.triggered.connect(self.crossprobe) pop_menu.exec_(QCursor.pos()) class TreeWidgetItem(QTreeWidgetItem): def __init__(self, parent: int, tree_row:GuiType._GuiTreeRow): super().__init__(parent) self.tree_row = tree_row class TextBrowserWin(Ui_LineEdit, MdiActionWindow): def __init__(self, mainWindow, instance:GuiType._GuiWinText) -> None: super().__init__(mainWindow) self.setupUi(self) self.setWindowTitle(instance.Title) self.textBrowser.append(instance.Text) self.instance = instance self.mdisubwin:QMdiSubWindow = mainWindow.mdiArea.addSubWindow(self) self.mdisubwin.setAttribute(Qt.WA_DeleteOnClose) self.mdisubwin.setWindowIcon(QIcon(":/icon/resource/todo-line.png")) self.mdisubwin.resize(self.width(), mainWindow.mdiArea.height()) self.mdisubwin.show() self.mdisubwin.move(ensure_subwin_in_midarea(mainWindow.mdiArea, self)) class LineEditWin(Ui_LineEdit, MdiActionWindow): def __init__(self, mainWindow, instance:GuiType._GuiWinLinedText) -> None: super().__init__(mainWindow) self.setupUi(self) self.setWindowTitle(instance.Title) self.textBrowser.textChanged.connect(self.__line_widget_line_count_changed) self.lineWidget = LineNumberWidget(self.textBrowser) self.horizontalLayout.insertWidget(0,self.lineWidget) self.instance = instance self.set_text(instance) mdiArea:QMdiArea = mainWindow.mdiArea self.mdisubwin:QMdiSubWindow = mdiArea.addSubWindow(self) self.mdisubwin.setAttribute(Qt.WA_DeleteOnClose) self.mdisubwin.setWindowIcon(QIcon(":/icon/resource/todo-line.png")) self.mdisubwin.resize(self.width(), mdiArea.height()) self.mdisubwin.show() self.go_to_line(self.instance.GotoLine) def set_text(self, instance:GuiType._GuiWinLinedText): for text in instance.LinedText: self.textBrowser.append(text) def go_to_line(self, line): if line > self.lineWidget.line_count(): line = self.lineWidget.line_count() cur = self.textBrowser.textCursor() cur.movePosition(QTextCursor.Start) self.textBrowser.setTextCursor(cur) position = self.textBrowser.document().findBlockByLineNumber(line-1).position() if position < self.textBrowser.height()/2: position = 0 elif position < self.textBrowser.document().lastBlock().position() - self.textBrowser.height()/2: position += self.textBrowser.height()/2 else: position = self.textBrowser.document().lastBlock().position() cur.setPosition(position, QTextCursor.MoveAnchor) self.textBrowser.setTextCursor(cur) self.lineWidget.highlightLineNumber(line) def __line_widget_line_count_changed(self): if self.lineWidget: n = int(self.textBrowser.document().lineCount()) self.lineWidget.changeLineCount(n) class SideBarWin(Ui_SideBar, MdiActionWindow): def __init__(self, ui, instance:GuiType._GuiSideBar) -> None: super().__init__(ui) self.setupUi(self) self.ui = ui self.instance = instance self.init_win_data(instance) self.form_multi_instan = None self.treeWidget.itemExpanded.connect(self.expand_tree) self.treeWidget.itemCollapsed.connect(self.collapse_tree) self.treeWidget.currentItemChanged.connect(self.item_change) self.treeWidget.customContextMenuRequested.connect(self.right_click_menu) self.sheets.activated.connect(lambda index: MultiFormWin.activate_table_sheet(self.tableView, self.form_multi_instan, index)) self.tableView.customContextMenuRequested.connect(self.right_click_menu) self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) mdiArea:QMdiArea = ui.mdiArea self.mdiArea:QMdiArea = mdiArea self.mdisubwin:QMdiSubWindow = mdiArea.addSubWindow(self) self.mdisubwin.setAttribute(Qt.WA_DeleteOnClose) self.mdisubwin.setWindowIcon(QIcon(":/icon/resource/node-tree.png")) # self.mdisubwin 是用于管理子窗口的,在子窗口中,是由 mdiArea 来管理显示/隐藏、大小、关闭等 self.mdisubwin.resize(self.width(), mdiArea.height()) self.mdisubwin.show() def closeEvent(self, event: QCloseEvent): event.ignore() self.mdisubwin.hide() self.menuWindow_action.setChecked(False) def right_click_menu(self, pos): sender = self.sender() pop_menu = QMenu() pop_menu.addAction(QAction('Crossprobe', pop_menu)) pop_menu.addAction(QAction('Properties', pop_menu)) pop_menu.triggered.connect(lambda action: self.right_click_menu_action(action, sender)) pop_menu.exec_(QCursor.pos()) def right_click_menu_action(self, action:QAction, sender): if action.text() == 'Crossprobe': self.add_crossprobe_win(sender) elif action.text() == 'Properties': self.add_properties_win(sender) def add_crossprobe_win(self, sender): tree_names, formname, formrow = self.get_win_argument(sender) line_text = self.instance.CrossProbe(tree_names, formname, formrow) property_win:QMdiSubWindow = get_sub_win(self.mdiArea, LineEditWin, line_text.Title) # 窗口已经存在 if property_win: property_win.close() self.lineEdit_win = LineEditWin(self.ui, line_text) def add_properties_win(self, sender:QWidget): tree_names, formname, formrow = self.get_win_argument(sender) win_form_instan = self.instance.Properties(tree_names, formname, formrow) property_win:QMdiSubWindow = get_sub_win(self.mdiArea, FormWindow, win_form_instan.Title) # 窗口已经存在 if property_win: property_win.close() mouse_pos = QCursor().pos() pos = self.mdiArea.mapFromGlobal(mouse_pos) form_window = FormWindow(self.ui, win_form_instan, pos) def get_win_argument(self, sender): item = self.treeWidget.currentItem() tree_names = self.get_tree_names(item) if type(sender) is QTreeWidget: formname = None formrow = [] elif type(sender) is QTableView: formname = self.sheets.currentText() formrow = [] for column in range(self.tableView.model().columnCount()): data = self.tableView.model().index(self.tableView.currentIndex().row(), column).data() formrow.append(data) return tree_names, formname, formrow # 侧边栏只有一个,只需要 update 即可 def init_win_data(self, instance:GuiType._GuiSideBar): if self.treeWidget.topLevelItemCount(): for i in range(self.treeWidget.topLevelItemCount()): # 获取这个 item 并删除 self.treeWidget.takeTopLevelItem(i) self.ui.setWindowTitle(self.ui.GUI.Title) self.instance = instance self.setWindowTitle(self.instance.Title) self.set_tree(self.instance.Tree) # 添加一行树节点,该节点包含 n 列数据 def add_tree_row(self, parent, tree_row:GuiType._GuiTreeRow): item = TreeWidgetItem(parent, tree_row) item.setText(0,tree_row.Name) for i in range(0, len(tree_row.Row)): item.setText(i+1,tree_row.Row[i]) return item def get_tree_names(self, tree_item:TreeWidgetItem): tree_names = [tree_item.text(0)] parent = tree_item.parent() while 1: if parent: tree_names.append(parent.text(0)) parent = parent.parent() else: break return tree_names[::-1] def item_change(self, item:TreeWidgetItem, column): if not item: return self.form_multi_instan = self.instance.GetForms(self.get_tree_names(item)) MultiFormWin.set_form_multi(self.tableView, self.sheets, self.form_multi_instan) def collapse_tree(self, item:TreeWidgetItem): # 取出并删除所有子节点 childrens = item.takeChildren() if not childrens: return if item.tree_row.HasSubTreeRows: # 只添加一个子节点,让父节点变成可展开形式 sub_tree_row:GuiType._GuiTreeRow = item.tree_row.GetSubTreeRows()[0] self.add_tree_row(item, sub_tree_row) def expand_tree(self, item:TreeWidgetItem): # 取出所有 Tree row item.takeChildren() # 加载所有 Tree row if item.tree_row.HasSubTreeRows: sub_tree_rows = item.tree_row.GetSubTreeRows() for row in sub_tree_rows: self.set_tree_row(item, row) def set_tree_row(self, parent:TreeWidgetItem, tree_row:GuiType._GuiTreeRow): tree_widget_item = self.add_tree_row(parent, tree_row) if tree_row.HasSubTreeRows: # 只添加一个子节点,让父节点变成可展开形式 sub_tree_row:GuiType._GuiTreeRow = tree_row.GetSubTreeRows()[0] self.add_tree_row(tree_widget_item, sub_tree_row) return tree_widget_item # 树形控件设置数据 def set_tree(self, tree:GuiType._GuiTree): # 树 header_item = self.treeWidget.headerItem() for i in range(len(tree.FixedRow)): header_item.setText(i, tree.FixedRow[i]) for tree_row in tree.TreeRows: # 根节点 self.set_tree_row(self.treeWidget, tree_row) # 默认选择第一行 if self.treeWidget.topLevelItemCount(): self.treeWidget.setItemSelected(self.treeWidget.topLevelItem(0), True) class SubWindows(QObject): sig = Signal(object) def __init__(self, parent): super().__init__(parent) self.ui = parent self.side_bar = SideBarWin(self.ui, self.ui.GUI.SideBar) self.info = {} def add_win_form_multi(self, instance:GuiType._GuiWinFormMulti): form_multi_win = MultiFormWin(self.ui, instance) form_multi_win.setWindowTitle(instance.Title) form_multi_win.sheets.currentIndexChanged.connect(lambda:self.change_table_sheet(form_multi_win)) self.set_form_multi_table(form_multi_win,instance.Forms) self.ui.mdiArea.activeSubWindow().resize(self.ui.mdiArea.width()/2,self.ui.mdiArea.height()) self.ui.mdiArea.activeSubWindow().move(form_multi_win.width(),0) def set_form_multi_table(self, win:MultiFormWin, multi_form:GuiType._GuiFormMulti): multi_form:GuiType._GuiFormMulti for i in range(len(multi_form.Names)): sheet_name = multi_form.Names[i] form:GuiType._GuiForm = multi_form.GetForm(i) model = QStandardItemModel(0, len(form.FixedRow)) model.setHorizontalHeaderLabels(form.FixedRow) for row in form.Rows: row_items=[QStandardItem(text) for text in row] model.appendRow(row_items) win.sheets.addItem(sheet_name) win.models.append(model) if win.sheets.count() > 0: win.sheets.setCurrentIndex(0) win.tableView.setModel(win.models[0]) def change_table_sheet(self, win:MultiFormWin): if not win.models: return win.tableView.setModel(win.models[win.sheets.currentIndex()]) def add_windows(self, instance): win_info = { GuiType._GuiWinFormMulti: lambda: MultiFormWin(self.ui, instance), GuiType._GuiSideBar: lambda: self.side_bar.init_win_data(instance), GuiType._GuiWinForm: lambda: FormWindow(self.ui, instance), GuiType._GuiWinText: lambda: TextBrowserWin(self.ui, instance), } win_info.get(type(instance))() return if isinstance(instance, GuiType._GuiWinFormMulti): MultiFormWin(self.ui, instance) elif isinstance(instance, GuiType._GuiSideBar): self.side_bar.init_win_data(instance) if __name__ == '__main__': m = GuiType._GuiWinFormMulti('multi', 1)