import os import queue import re import sys import threading from PySide2.QtWidgets import QApplication, QMainWindow from PySide2.QtUiTools import QUiLoader from PySide2.QtWidgets import * from PySide2.QtCore import * from PySide2 import QtCore from PySide2.QtGui import * from ui.ui_MainWindows import Ui_MainWindow from ui.Analyze import AnalyzeWindow from sub_windows import SubWindows,LineEditWin,FormWindow,get_sub_win_list,get_sub_win from ui.About import AboutWindow from ui.Elaborate import Elaborate from ui.Read_Timing_Constraint import ReadTimeWindow from ui.ReportException import ReportExceptionWindow from ui.ReportClocks import ReportClocks from ui.ReportVirtualTiming import ReportVirtualTiming from ui.Preferences import PreferencesWindows from ui.myui import set_font_size, change_font_size, get_font_size import GuiType class Stream(QObject): """Redirects console output to text widget.""" newText = Signal(str) def __init__(self): super().__init__() self.stdout_bak = sys.stdout self.buffer = self.stdout_bak.buffer self.close = self.stdout_bak.close self.closed = self.stdout_bak.closed self.detach = self.stdout_bak.detach self.encoding = self.stdout_bak.encoding self.errors = self.stdout_bak.errors self.fileno = self.stdout_bak.fileno self.flush = self.stdout_bak.flush self.isatty = self.stdout_bak.isatty self.line_buffering = self.stdout_bak.line_buffering self.mode = self.stdout_bak.mode self.name = self.stdout_bak.name self.newlines = self.stdout_bak.newlines self.read = self.stdout_bak.read self.readable = self.stdout_bak.readable self.readline = self.stdout_bak.readline self.readlines = self.stdout_bak.readlines self.reconfigure = self.stdout_bak.reconfigure self.seek = self.stdout_bak.seek self.seekable = self.stdout_bak.seekable self.tell = self.stdout_bak.tell self.truncate = self.stdout_bak.truncate self.writable = self.stdout_bak.writable self.write_through = self.stdout_bak.write_through self.writelines = self.stdout_bak.writelines def write(self, text:str): self.stdout_bak.write(str(text)) self.stdout_bak.flush() if len(text) == 0: return if text.isspace(): return self.newText.emit(str(text+'\n')) class MessageItem(QTreeWidgetItem): def __init__(self, parent, msg:GuiType._GuiMessage): super().__init__(parent) self.setText(0, msg.Text) self.msg = msg class MessagesWidgetItem(QTreeWidgetItem): def __init__(self, parent, messages:GuiType._GuiMessages, level): super().__init__(parent) summary = messages.GetSummary() self.setText(0, level + f"({messages.Size})") for id_, size, text in summary: sumary_item = QTreeWidgetItem(self) sumary_item.setText(0, f"{id_}({size}):{text}") for msg in messages.GetMsgByID(id_): msg:GuiType._GuiMessage MessageItem(sumary_item, msg) class MainWindow(Ui_MainWindow, QMainWindow): textEdit_sig = Signal(str) sig = Signal(object) def __init__(self, GUI) -> None: super().__init__() self.setupUi(self) # 这里显示一下是为了能获取到 mdiArea 的尺寸,给 sub_windows 使用 self.GUI = GUI self.setWindowTitle(GUI.Title) self.show() sys.stdout = Stream() sys.stdout.newText.connect(self.outputWritten) sys.stderr = sys.stdout self.btn_shell.setText(GUI.Prompt) self.btn_shell.clicked.connect(self.click_btn_sell) self.tabWidget.currentChanged.connect(self.table_widget_change) self.tableHistory.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.tableHistory.customContextMenuRequested.connect(self.history_right_click_menu) self.MessageTreeView.customContextMenuRequested.connect(self.message_right_click_menu) self.status_label = QLabel("Ready") self.statusbar.addWidget(self.status_label) self.stage_label = QLabel(self.GUI._stage) self.statusbar.addPermanentWidget(self.stage_label) # -- MENUE -- # file self.actionStop_GUI.triggered.connect(GUI.Stop) self.actionExit.triggered.connect(GUI.Exit) # Edit self.actionCopy.triggered.connect(self.action_copy) self.actionPast.triggered.connect(self.action_paste) self.actionSelect_all.triggered.connect(self.action_select_all) # View self.actionIncr_Font.triggered.connect(lambda: change_font_size(self, 1)) self.actionDecr_Font.triggered.connect(lambda: change_font_size(self, -1)) self.actionToolbar.triggered.connect(lambda: self.toolBar.setHidden(not self.actionToolbar.isChecked())) self.actionSidebar.triggered.connect(lambda: self.sub_win.side_bar.mdisubwin.setHidden(not self.actionSidebar.isChecked())) self.actionConsole.triggered.connect(lambda: self.tabWidget.setHidden(not self.actionConsole.isChecked())) self.actionStatusBar.triggered.connect(lambda: self.statusbar.setHidden(not self.actionStatusBar.isChecked())) self.actionSave_as_Default.triggered.connect(self.action_save_as_default) self.actionPreferences.triggered.connect(self.preferences) self.actionShow_Tab_Bar.triggered.connect(self.toggle_tab_bar) # Design self.actionAnalyze.triggered.connect(self.show_analyze_window) self.actionElaborate.triggered.connect(self.elaborate_window) self.actionRead_Timing_Constraint.triggered.connect(self.read_timing_constraint) self.actionRead_Power_Intent self.actionReload_Design.triggered.connect(self.reload_design) # Windows # 先实例一个 ActionGroup,等到创建动态窗口的时候,才会在 菜单栏-Window 中添加Action self.action_win_group = QActionGroup(self) # 获取分隔符,方便 菜单栏-Window-Action 插入到分隔符之前 self.action_win_separator = self.menuWindow.actions()[0] action_tabgroup = QActionGroup(self) self.actionLog.setActionGroup(action_tabgroup) self.actionHistory.setActionGroup(action_tabgroup) self.actionMessages.setActionGroup(action_tabgroup) self.actionLog.triggered.connect(self.action_tab_widget) self.actionHistory.triggered.connect(self.action_tab_widget) self.actionMessages.triggered.connect(self.action_tab_widget) # Report self.actionReport_Exceptions.triggered.connect(self.report_exception) self.actionReport_Clocks.triggered.connect(self.report_clocks) self.actionReport_Virtual_Timing.triggered.connect(self.report_virtual_timing) # About self.actionAbout.triggered.connect(self.about) self.textEdit.document().contentsChanged.connect(self.textEdit_change_size) self.textEdit.document().contentsChange.connect(self.textEdit_change_contents) self.sub_win = SubWindows(self) self.sub_win.sig.connect(self.sub_win.add_windows) set_font_size(self, self.GUI.TextFontSize,self.GUI.TextFont,self.GUI.SysFontSize,self.GUI.SysFont) def toggle_tab_bar(self, checked): if checked: self.mdiArea.setViewMode(QMdiArea.TabbedView) else: self.mdiArea.setViewMode(QMdiArea.SubWindowView) def action_copy(self): # 获得文本光标所在控件 widget:QTextBrowser = self.focusWidget() if type(widget) not in (QTextBrowser, QTextEdit): return cur:QTextCursor = widget.textCursor() self.copy(cur.selectedText()) def action_paste(self): clipboard = QApplication.clipboard() # 获得文本光标所在控件 widget:QTextBrowser = self.focusWidget() if type(widget) not in (QTextBrowser, QTextEdit): return if widget.isReadOnly(): return widget.textCursor().insertText(clipboard.text()) def action_select_all(self): # 获得文本光标所在控件 widget:QTextBrowser = self.focusWidget() if not isinstance(widget, (QTextBrowser, QTextEdit, QPlainTextEdit,)): return widget.selectAll() def action_save_as_default(self): text_font_size, text_font_family, sys_font_size, sys_font_family = get_font_size(self) self.GUI.ViewSetDefault(text_font_size, text_font_family,sys_font_size,sys_font_family) def action_tab_widget(self): action_sender = self.sender() action = { self.actionLog: 0, self.actionHistory: 1, self.actionMessages: 2, } index = action.get(action_sender) if index != None: self.tabWidget.setCurrentIndex(index) def read_timing_constraint(self): ReadTimeWindow(self, os.getcwd()) def table_widget_change(self, index): member = { 0: None, 1: self.show_history, 2: self.show_message } method = member.get(index) if method: method() def show_history(self): self.tableHistory.clear() blank_row = self.tableHistory.height() / self.tableHistory.verticalHeader().height() + 2 self.tableHistory.setRowCount(blank_row) for row_text in self.GUI.GetHistory(): end_row = self.tableHistory.rowCount() self.tableHistory.insertRow(end_row) self.tableHistory.setItem(end_row, 0, QTableWidgetItem(row_text)) self.tableHistory.scrollToBottom() def copy(self, text): clipboard = QApplication.clipboard() clipboard.setText(text) def history_right_click_menu(self): itemPos = self.tableHistory.mapFromGlobal(QCursor().pos()) item = self.tableHistory.itemAt(itemPos) if type(item) != QTableWidgetItem: return pop_menu = QMenu() pop_menu.addAction(QAction('Copy', pop_menu)) pop_menu.triggered.connect(lambda: self.copy(self.tableHistory.currentItem().text())) pop_menu.exec_(QCursor.pos()) def message_right_click_menu(self): itemPos = self.MessageTreeView.mapFromGlobal(QCursor().pos()) item:MessageItem = self.MessageTreeView.itemAt(itemPos) if type(item) != MessageItem: return pop_menu = QMenu() pop_menu.addAction(QAction('Crossprobe', pop_menu)) pop_menu.addAction(QAction('Properties', pop_menu)) pop_menu.triggered.connect(lambda action: self.message_popmenu_action(action, item)) pop_menu.exec_(QCursor.pos()) def message_popmenu_action(self, action, item): if action.text() == 'Crossprobe': win_line_text = item.msg.CrossProbe() win = get_sub_win(self.mdiArea, LineEditWin, win_line_text.Title) # 如果窗口已经存在,关闭窗口,以便重新更新数据 if win: win.close() LineEditWin(self, win_line_text) elif action.text() == 'Properties': win_form = item.msg.Properties() # 如果窗口已经存在,关闭窗口,以便重新更新数据 win = get_sub_win(self.mdiArea, FormWindow, win_form.Title) if win: win.close() FormWindow(self, win_form) def show_message(self): for _ in range(self.MessageTreeView.topLevelItemCount()): self.MessageTreeView.takeTopLevelItem(0) MessagesWidgetItem(self.MessageTreeView, self.GUI.GetErrors(), "Errors") MessagesWidgetItem(self.MessageTreeView, self.GUI.GetCriticalWarnings(), "CriticalWarnings") MessagesWidgetItem(self.MessageTreeView, self.GUI.GetWarnings(), "Warnings") MessagesWidgetItem(self.MessageTreeView, self.GUI.GetInfos(), "Infos") def about(self): d = AboutWindow(self, self.GUI.About) d.exec_() def show_analyze_window(self): d = AnalyzeWindow(self, self.GUI.Shell_Execute) d.exec_() def elaborate_window(self): d = Elaborate(self) d.exec_() def report_exception(self): d = ReportExceptionWindow(self) d.exec_() def report_clocks(self): d = ReportClocks(self) d.exec_() def report_virtual_timing(self): d = ReportVirtualTiming(self) d.exec_() def preferences(self): d = PreferencesWindows(self) d.exec_() def outputWritten(self, text): cursor = self.LogText.textCursor() cursor.movePosition(QTextCursor.End) cursor.insertText(text) self.LogText.setTextCursor(cursor) self.LogText.ensureCursorVisible() def textEdit_change_size(self): height = self.textEdit.document().size().height() self.textEdit.setMinimumHeight(height) def click_btn_sell(self): self.textEdit.append('\n') def textEdit_change_contents(self, from_index, charsRemoved, charsAdded): if not charsAdded: return stdin = self.textEdit.toPlainText() if not stdin: return # 捕获回车键 if stdin[from_index] != '\n': return # 回车时,判断字符串代码是否结束 if self.GUI.Shell_IsCmdFinished(stdin): # 删除回车字符 stdin = stdin[:from_index] + stdin[from_index+1:] print(stdin) thread = threading.Thread(target=self.GUI.Shell_Execute, args=(stdin,)) thread.start() self.textEdit.setText('') self.textEdit_change_size() def reload_design(self): thread = threading.Thread(target=self.GUI.ReloadDesign) thread.start() def busy(self): self.status_label.setText("Busy") self.setCursor(Qt.WaitCursor) def ready(self): self.status_label.setText("Ready") self.setCursor(Qt.ArrowCursor) def stage(self): print(self.GUI._stage) self.stage_label.setText(self.GUI._stage)