def __init__(self, title, comm, topics, userWidget): super().__init__(title) self.setObjectName(title) self.comm = comm self.topics = topics splitter = QSplitter() self.fieldDataIndex = None plotLayout = QVBoxLayout() selectionLayout = QVBoxLayout() detailsLayout = QFormLayout() filterLayout = QHBoxLayout() w_left = QWidget() w_left.setLayout(plotLayout) splitter.addWidget(w_left) w_right = QWidget() w_right.setLayout(selectionLayout) splitter.addWidget(w_right) splitter.setCollapsible(0, False) splitter.setStretchFactor(0, 10) splitter.setStretchFactor(1, 0) selectionLayout.addLayout(detailsLayout) selectionLayout.addWidget(QLabel("Filter Data")) selectionLayout.addLayout(filterLayout) self.selectedActuatorIdLabel = QLabel("") self.selectedActuatorValueLabel = QLabel("") self.selectedActuatorWarningLabel = QLabel("") self.lastUpdatedLabel = TimeDeltaLabel() self.topicList = QListWidget() self.topicList.currentRowChanged.connect(self.currentTopicChanged) for topic in self.topics.topics: self.topicList.addItem(topic.name) self.fieldList = QListWidget() self.fieldList.currentRowChanged.connect(self.currentFieldChanged) plotLayout.addWidget(userWidget) detailsLayout.addRow(QLabel("Selected Actuator Details"), QLabel("")) detailsLayout.addRow(QLabel("Actuator Id"), self.selectedActuatorIdLabel) detailsLayout.addRow(QLabel("Actuator Value"), self.selectedActuatorValueLabel) detailsLayout.addRow(QLabel("Actuator Warning"), self.selectedActuatorWarningLabel) detailsLayout.addRow(QLabel("Last Updated"), self.lastUpdatedLabel) filterLayout.addWidget(self.topicList) filterLayout.addWidget(self.fieldList) self.topicList.setCurrentRow(0) self.setWidget(splitter)
def _SetupUI(self): main_layout = QVBoxLayout() search_filter_widget = QWidget() search_filter_layout = QHBoxLayout(search_filter_widget) central_widget = QWidget() central_layout = QVBoxLayout(central_widget) panel_splitter = QSplitter(Qt.Horizontal) panel_splitter.setHandleWidth(4) panel_splitter.addWidget(self._source_panel) panel_splitter.addWidget(central_widget) panel_splitter.setStretchFactor(0, 0) panel_splitter.setStretchFactor(1, 1) panel_splitter.moveSplitter(LEFT_PANEL_WIDTH, 1) panel_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) search_filter_layout.addWidget(self._search_bar) search_filter_layout.addWidget(self._filter_bar) search_filter_layout.setContentsMargins(0, 0, 0, 0) central_layout.addWidget(search_filter_widget) central_layout.addWidget(self._splitter) central_layout.addWidget(self._status_bar) central_layout.setContentsMargins(0, 0, 0, 0) main_layout.addWidget(panel_splitter) self.setLayout(main_layout)
def __init__(self, totaltime=0): super().__init__() # 在主窗口左侧添加题干和选项 lsplitter = QSplitter(Qt.Vertical) self.question_panel = Question_panel() lsplitter.addWidget(self.question_panel) # 在主窗口右侧添加绘图和文本编辑,并把比例设置为3比1 rsplitter = QSplitter(Qt.Vertical) self.painter = QGraphicsView(rsplitter) self.note = QTextEdit(rsplitter) rsplitter.setStretchFactor(0, 3) rsplitter.setStretchFactor(1, 1) # 添加插件的顺序会导致左右不同 mainSplitter = QSplitter(Qt.Horizontal) mainSplitter.addWidget(lsplitter) mainSplitter.addWidget(rsplitter) self.setCentralWidget(mainSplitter) # 点击暂停按钮切换图标和停继时间 self.question_panel.ui.pushButtonPause.clicked.connect( self.toggle_play_and_pause) self.totaltime = totaltime # 当前试卷答题总时间 self.elapsed_time = QElapsedTimer() # 答题总时间的计时器 self.paused = False # 默认刚打开时,还未暂停时间 self.setTime() # 更新时间显示 self.timer = QTimer() self.timer.timeout.connect(self.setTime) # 每秒更新时间显示的定时器
class ProcessMonitorUI: def __init__(self, window: ProcessMonitorWindow): self.window = window self.main_widget = QWidget(window) self.main_layout = QVBoxLayout() self.layout_connection = QHBoxLayout() self.txt_conenction = QLineEdit() self.btn_connect = QPushButton() self.layout_connection.addWidget(self.txt_conenction) self.layout_connection.addWidget(self.btn_connect) self.process_list = ProcessListWidget() self.tabs_output = ProcessOutputTabsWidget() self.splitter = QSplitter(Qt.Horizontal) self.splitter.setMinimumSize(680, 540) policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) policy.setHorizontalStretch(0) policy.setVerticalStretch(0) policy.setHeightForWidth(self.splitter.sizePolicy().hasHeightForWidth()) self.splitter.setSizePolicy(policy) self.splitter.setStretchFactor(0, 1) self.splitter.setStretchFactor(1, 0) self.main_layout.addLayout(self.layout_connection) self.splitter.addWidget(self.process_list) self.splitter.addWidget(self.tabs_output) self.main_layout.addWidget(self.splitter) self.main_widget.setLayout(self.main_layout) self.statusbar = QStatusBar(window) self.txt_conenction.setPlaceholderText("ws://127.0.0.1:8766") self.txt_conenction.setText("ws://127.0.0.1:8766") self.btn_connect.setText("Connect") window.setCentralWidget(self.main_widget) window.setStatusBar(self.statusbar) window.setWindowTitle("Process Monitor") window.setWindowIcon(window.style().standardIcon(QStyle.SP_BrowserReload)) self.set_disconnected_ui("Click on Connect to establish a connection") def set_disconnected_ui(self, msg: str): self.process_list.setDisabled(True) self.btn_connect.setDisabled(False) self.statusbar.showMessage(f"Disconnected. {msg}") def set_connected_ui(self): self.process_list.setDisabled(False) self.btn_connect.setDisabled(True) self.statusbar.showMessage("Connection established.") def handle_output(self, output: OutputEvent): self.tabs_output.append_output(output.uid, output.output)
def _create_splitter(self, ) -> QSplitter: splitter = QSplitter( Qt.Vertical, self, ) splitter.addWidget(self._tab_widget) splitter.addWidget(self._logs_widget) splitter.addWidget(self._progress_bar) splitter.setCollapsible(0, False) # noqa: WPS425 splitter.setCollapsible(1, False) # noqa: WPS425 splitter.setCollapsible(2, False) # noqa: WPS425 splitter.setStretchFactor(0, 98) splitter.setStretchFactor(1, 1) splitter.setStretchFactor(2, 1) return splitter
class MainWindow(QMainWindow): def __init__(self, *, setup_input_panel=None, plot_panel=None): super().__init__() self.setup_input_panel = setup_input_panel self.plot_panel = plot_panel self.main_widget = None self.menu_bar = None self.new_menu_action = None self.setup_ui() self.setWindowTitle(TITLE) def setup_ui(self): self.main_widget = QSplitter(Qt.Horizontal) self.setCentralWidget(self.main_widget) self.main_widget.addWidget(self.setup_input_panel) self.main_widget.addWidget(self.plot_panel) self.main_widget.setStretchFactor(0, 2) self.main_widget.setStretchFactor(1, 1) self.create_menu_bar() def create_menu_bar(self): self.menu_bar = QMenuBar(self) self.setMenuBar(self.menu_bar) self.create_file_menu() def create_file_menu(self): file_menu = self.menu_bar.addMenu('&File') self.new_menu_action = QAction('New', self) self.new_menu_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) file_menu.addAction(self.new_menu_action)
def __init__(self): super().__init__() self.setLayout(QVBoxLayout()) vsplit = QSplitter(QtCore.Qt.Vertical) hsplit = QSplitter(QtCore.Qt.Horizontal) self.layout().addWidget(vsplit) self.layout().setMargin(0) self.layout().setSpacing(0) self.unity_area = UnityWidget() self.prop_area = Properties() self.timeline = Timeline() hsplit.addWidget(self.unity_area) hsplit.setCollapsible(0, False) hsplit.addWidget(self.prop_area) hsplit.setStretchFactor(0, 1) hsplit.setSizes([hsplit.width() - 400, 400]) vsplit.addWidget(hsplit) vsplit.setCollapsible(0, False) vsplit.addWidget(self.timeline) vsplit.setStretchFactor(0, 1) vsplit.setSizes([vsplit.height() - 150, 150])
def _gui_setup(self): layout = QHBoxLayout() splitter = QSplitter(Qt.Horizontal) panel_right = QWidget(self) layout.addWidget(splitter) annotation = Annotation() self.annotation = annotation transcripts = Transcripts(annotation, self) transcripts.setDisabled(True) splitter.addWidget(transcripts) self.transcripts = transcripts splitter.addWidget(panel_right) l = QVBoxLayout() l.setContentsMargins(0, 0, 0, 0) panel_right.setLayout(l) panel_right = l minutes = Minutes(annotation, self) minutes.setDisabled(True) self.minutes = minutes problems = Problems(self) problems.setEnabled(False) problems.problem_selected.connect(self.annotation.set_problem) problems.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.problems = problems splitter_right = QSplitter(Qt.Vertical) splitter_right.addWidget(minutes) splitter_right.addWidget(problems) panel_right.addWidget(splitter_right) player = Player(self) player.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.player = player panel_right.addWidget(player) evaluation = Evaluation(self) evaluation.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.evaluation = evaluation panel_right.addWidget(evaluation) widget = QWidget() widget.setLayout(layout) self.setCentralWidget(widget) menu = self.menuBar() file_menu = menu.addMenu('&File') a = file_menu.addAction('&New') a.setShortcut('Ctrl+n') a.triggered.connect(self.new) a = file_menu.addAction('&Open existing') a.setShortcut('Ctrl+o') a.triggered.connect(self.open_existing) a = file_menu.addAction('&Open repository') a.setShortcut('Ctrl+g') a.triggered.connect(self.open_repository) file_menu.addSeparator() a = file_menu.addAction('Evaluation mode') a.setCheckable(True) a.setShortcut('Ctrl+e') a.triggered.connect(self.set_evaluation_mode) file_menu.addSeparator() a = file_menu.addAction('&Save') a.setShortcut('Ctrl+s') a.triggered.connect(self.save) file_menu.addSeparator() a = file_menu.addAction('S&ettings') a.setShortcut('Ctrl++') a.triggered.connect(lambda: Settings(self).exec_()) file_menu.addSeparator() a = file_menu.addAction('&Close') a.setShortcut('Alt+c') a.triggered.connect(self.close) playback_menu = menu.addMenu('&Playback') a = playback_menu.addAction('&Open audio') a.triggered.connect(self._open_audio) playback_menu.addSeparator() playback_menu.addActions(player.playback_actions) s = QSettings(self) self.splitter = splitter self.splitter_righ = splitter_right splitter.restoreState(s.value('splitter')) splitter_right.restoreState(s.value('splitter_righ')) self.restoreGeometry(s.value('window_geometry')) self.restoreState(s.value('window_state')) splitter_right.setStretchFactor(0, 10) splitter_right.setStretchFactor(1, 1)
class WTreeEdit(QWidget): """TreeEdit widget is to show and edit all of the pyleecan objects data.""" # Signals dataChanged = Signal() def __init__(self, obj, *args, **kwargs): QWidget.__init__(self, *args, **kwargs) self.class_dict = ClassInfo().get_dict() self.treeDict = None # helper to track changes self.obj = obj # the object self.is_save_needed = False self.model = TreeEditModel(obj) self.setupUi() # === Signals === self.selectionModel.selectionChanged.connect(self.onSelectionChanged) self.treeView.collapsed.connect(self.onItemCollapse) self.treeView.expanded.connect(self.onItemExpand) self.treeView.customContextMenuRequested.connect(self.openContextMenu) self.model.dataChanged.connect(self.onDataChanged) self.dataChanged.connect(self.setSaveNeeded) # === Finalize === # set 'root' the selected item and resize columns self.treeView.setCurrentIndex(self.treeView.model().index(0, 0)) self.treeView.resizeColumnToContents(0) def setupUi(self): """Setup the UI""" # === Widgets === # TreeView self.treeView = QTreeView() # self.treeView.rootNode = model.invisibleRootItem() self.treeView.setModel(self.model) self.treeView.setAlternatingRowColors(False) # self.treeView.setColumnWidth(0, 150) self.treeView.setMinimumWidth(100) self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.selectionModel = self.treeView.selectionModel() self.statusBar = QStatusBar() self.statusBar.setSizeGripEnabled(False) self.statusBar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) self.statusBar.setStyleSheet( "QStatusBar {border: 1px solid rgb(200, 200, 200)}") self.saveLabel = QLabel("unsaved") self.saveLabel.setVisible(False) self.statusBar.addPermanentWidget(self.saveLabel) # Splitters self.leftSplitter = QSplitter() self.leftSplitter.setStretchFactor(0, 0) self.leftSplitter.setStretchFactor(1, 1) # === Layout === # Horizontal Div. self.hLayout = QVBoxLayout() self.hLayout.setContentsMargins(0, 0, 0, 0) self.hLayout.setSpacing(0) # add widgets to layout self.hLayout.addWidget(self.leftSplitter) self.hLayout.addWidget(self.statusBar) # add widgets self.leftSplitter.addWidget(self.treeView) self.setLayout(self.hLayout) def update(self, obj): """Check if object has changed and update tree in case.""" if not obj is self.obj: self.obj = obj self.model = TreeEditModel(obj) self.treeView.setModel(self.model) self.model.dataChanged.connect(self.onDataChanged) self.selectionModel = self.treeView.selectionModel() self.selectionModel.selectionChanged.connect( self.onSelectionChanged) self.treeView.setCurrentIndex(self.treeView.model().index(0, 0)) self.setSaveNeeded(True) def setSaveNeeded(self, state=True): self.is_save_needed = state self.saveLabel.setVisible(state) def openContextMenu(self, point): """Generate and open context the menu at the given point position.""" index = self.treeView.indexAt(point) pos = QtGui.QCursor.pos() if not index.isValid(): return # get the data item = self.model.item(index) obj_info = self.model.get_obj_info(item) # init the menu menu = TreeEditContextMenu(obj_dict=obj_info, parent=self) menu.exec_(pos) self.onSelectionChanged(self.selectionModel.selection()) def onItemCollapse(self, index): """Slot for item collapsed""" # dynamic resize for ii in range(3): self.treeView.resizeColumnToContents(ii) def onItemExpand(self, index): """Slot for item expand""" # dynamic resize for ii in range(3): self.treeView.resizeColumnToContents(ii) def onDataChanged(self, first=None, last=None): """Slot for changed data""" self.dataChanged.emit() self.onSelectionChanged(self.selectionModel.selection()) def onSelectionChanged(self, itemSelection): """Slot for changed item selection""" # get the index if itemSelection.indexes(): index = itemSelection.indexes()[0] else: index = self.treeView.model().index(0, 0) self.treeView.setCurrentIndex(index) return # get the data item = self.model.item(index) obj = item.object() typ = type(obj).__name__ obj_info = self.model.get_obj_info(item) ref_typ = obj_info["ref_typ"] if obj_info else None # set statusbar information on class typ msg = f"{typ} (Ref: {ref_typ})" if ref_typ else f"{typ}" self.statusBar.showMessage(msg) # --- choose the respective widget by class type --- # numpy array -> table editor if typ == "ndarray": widget = WTableData(obj, editable=True) widget.dataChanged.connect(self.dataChanged.emit) elif typ == "MeshSolution": widget = WMeshSolution(obj) # only a view (not editable) # list (no pyleecan type, non empty) -> table editor # TODO add another widget for lists of non 'primitive' types (e.g. DataND) elif isinstance(obj, list) and not self.isListType(ref_typ) and obj: widget = WTableData(obj, editable=True) widget.dataChanged.connect(self.dataChanged.emit) # generic editor else: # widget = SimpleInputWidget().generate(obj) widget = WTableParameterEdit(obj) widget.dataChanged.connect(self.dataChanged.emit) # show the widget if self.leftSplitter.widget(1) is None: self.leftSplitter.addWidget(widget) else: self.leftSplitter.replaceWidget(1, widget) widget.setParent( self.leftSplitter) # workaround for PySide2 replace bug widget.show() pass def isListType(self, typ): if not typ: return False return typ[0] == "[" and typ[-1] == "]" and typ[1:-1] in self.class_dict def isDictType(self, typ): if not typ: return False return typ[0] == "{" and typ[-1] == "}" and typ[1:-1] in self.class_dict
def __init__(self, path_to_rom=""): super(MainWindow, self).__init__() self.setWindowIcon(icon("foundry.ico")) file_menu = QMenu("File") open_rom_action = file_menu.addAction("&Open ROM") open_rom_action.triggered.connect(self.on_open_rom) self.open_m3l_action = file_menu.addAction("&Open M3L") self.open_m3l_action.triggered.connect(self.on_open_m3l) file_menu.addSeparator() self.save_rom_action = file_menu.addAction("&Save ROM") self.save_rom_action.triggered.connect(self.on_save_rom) self.save_rom_as_action = file_menu.addAction("&Save ROM as ...") self.save_rom_as_action.triggered.connect(self.on_save_rom_as) """ file_menu.AppendSeparator() """ self.save_m3l_action = file_menu.addAction("&Save M3L") self.save_m3l_action.triggered.connect(self.on_save_m3l) """ file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "") file_menu.AppendSeparator() file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "") file_menu.AppendSeparator() file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "") """ file_menu.addSeparator() settings_action = file_menu.addAction("&Settings") settings_action.triggered.connect(show_settings) file_menu.addSeparator() exit_action = file_menu.addAction("&Exit") exit_action.triggered.connect(lambda _: self.close()) self.menuBar().addMenu(file_menu) """ edit_menu = wx.Menu() edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "") edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "") edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "") edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "") edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "") edit_menu.AppendSeparator() edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "") edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "") """ self.level_menu = QMenu("Level") self.select_level_action = self.level_menu.addAction("&Select Level") self.select_level_action.triggered.connect(self.open_level_selector) self.reload_action = self.level_menu.addAction("&Reload Level") self.reload_action.triggered.connect(self.reload_level) self.level_menu.addSeparator() self.edit_header_action = self.level_menu.addAction("&Edit Header") self.edit_header_action.triggered.connect(self.on_header_editor) self.edit_autoscroll = self.level_menu.addAction("Edit Autoscrolling") self.edit_autoscroll.triggered.connect(self.on_edit_autoscroll) self.menuBar().addMenu(self.level_menu) self.object_menu = QMenu("Objects") view_blocks_action = self.object_menu.addAction("&View Blocks") view_blocks_action.triggered.connect(self.on_block_viewer) view_objects_action = self.object_menu.addAction("&View Objects") view_objects_action.triggered.connect(self.on_object_viewer) self.object_menu.addSeparator() view_palettes_action = self.object_menu.addAction( "View Object Palettes") view_palettes_action.triggered.connect(self.on_palette_viewer) self.menuBar().addMenu(self.object_menu) self.view_menu = QMenu("View") self.view_menu.triggered.connect(self.on_menu) action = self.view_menu.addAction("Mario") action.setProperty(ID_PROP, ID_MARIO) action.setCheckable(True) action.setChecked(SETTINGS["draw_mario"]) action = self.view_menu.addAction("&Jumps on objects") action.setProperty(ID_PROP, ID_JUMP_OBJECTS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jump_on_objects"]) action = self.view_menu.addAction("Items in blocks") action.setProperty(ID_PROP, ID_ITEM_BLOCKS) action.setCheckable(True) action.setChecked(SETTINGS["draw_items_in_blocks"]) action = self.view_menu.addAction("Invisible items") action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS) action.setCheckable(True) action.setChecked(SETTINGS["draw_invisible_items"]) action = self.view_menu.addAction("Autoscroll Path") action.setProperty(ID_PROP, ID_AUTOSCROLL) action.setCheckable(True) action.setChecked(SETTINGS["draw_autoscroll"]) self.view_menu.addSeparator() action = self.view_menu.addAction("Jump Zones") action.setProperty(ID_PROP, ID_JUMPS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jumps"]) action = self.view_menu.addAction("&Grid lines") action.setProperty(ID_PROP, ID_GRID_LINES) action.setCheckable(True) action.setChecked(SETTINGS["draw_grid"]) action = self.view_menu.addAction("Resize Type") action.setProperty(ID_PROP, ID_RESIZE_TYPE) action.setCheckable(True) action.setChecked(SETTINGS["draw_expansion"]) self.view_menu.addSeparator() action = self.view_menu.addAction("&Block Transparency") action.setProperty(ID_PROP, ID_TRANSPARENCY) action.setCheckable(True) action.setChecked(SETTINGS["block_transparency"]) self.view_menu.addSeparator() self.view_menu.addAction( "&Save Screenshot of Level").triggered.connect(self.on_screenshot) """ self.view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "") self.view_menu.Append(ID_TOOLBAR, "&Toolbar", "") self.view_menu.AppendSeparator() self.view_menu.Append(ID_ZOOM, "&Zoom", "") self.view_menu.AppendSeparator() self.view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "") self.view_menu.Append(ID_PALETTE, "&Palette", "") self.view_menu.AppendSeparator() self.view_menu.Append(ID_MORE, "&More", "") """ self.menuBar().addMenu(self.view_menu) help_menu = QMenu("Help") """ help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "") help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "") help_menu.AppendSeparator() help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "") help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "") help_menu.AppendSeparator() """ update_action = help_menu.addAction("Check for updates") update_action.triggered.connect(self.on_check_for_update) help_menu.addSeparator() video_action = help_menu.addAction("Feature Video on YouTube") video_action.triggered.connect(lambda: open_url(feature_video_link)) github_action = help_menu.addAction("Github Repository") github_action.triggered.connect(lambda: open_url(github_link)) discord_action = help_menu.addAction("SMB3 Rom Hacking Discord") discord_action.triggered.connect(lambda: open_url(discord_link)) help_menu.addSeparator() about_action = help_menu.addAction("&About") about_action.triggered.connect(self.on_about) self.menuBar().addMenu(help_menu) self.block_viewer = None self.object_viewer = None self.level_ref = LevelRef() self.level_ref.data_changed.connect(self._on_level_data_changed) self.context_menu = ContextMenu(self.level_ref) self.context_menu.triggered.connect(self.on_menu) self.level_view = LevelView(self, self.level_ref, self.context_menu) self.scroll_panel = QScrollArea() self.scroll_panel.setWidgetResizable(True) self.scroll_panel.setWidget(self.level_view) self.setCentralWidget(self.scroll_panel) self.spinner_panel = SpinnerPanel(self, self.level_ref) self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in) self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out) self.spinner_panel.object_change.connect(self.on_spin) self.object_list = ObjectList(self, self.level_ref, self.context_menu) self.object_dropdown = ObjectDropdown(self) self.object_dropdown.object_selected.connect( self._on_placeable_object_selected) self.level_size_bar = LevelSizeBar(self, self.level_ref) self.enemy_size_bar = EnemySizeBar(self, self.level_ref) self.jump_list = JumpList(self, self.level_ref) self.jump_list.add_jump.connect(self.on_jump_added) self.jump_list.edit_jump.connect(self.on_jump_edit) self.jump_list.remove_jump.connect(self.on_jump_removed) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.addWidget(self.object_list) splitter.setStretchFactor(0, 1) splitter.addWidget(self.jump_list) splitter.setChildrenCollapsible(False) level_toolbar = QToolBar("Level Info Toolbar", self) level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) level_toolbar.setOrientation(Qt.Horizontal) level_toolbar.setFloatable(False) level_toolbar.addWidget(self.spinner_panel) level_toolbar.addWidget(self.object_dropdown) level_toolbar.addWidget(self.level_size_bar) level_toolbar.addWidget(self.enemy_size_bar) level_toolbar.addWidget(splitter) level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.RightToolBarArea, level_toolbar) self.object_toolbar = ObjectToolBar(self) self.object_toolbar.object_selected.connect( self._on_placeable_object_selected) object_toolbar = QToolBar("Object Toolbar", self) object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) object_toolbar.setFloatable(False) object_toolbar.addWidget(self.object_toolbar) object_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.LeftToolBarArea, object_toolbar) self.menu_toolbar = QToolBar("Menu Toolbar", self) self.menu_toolbar.setOrientation(Qt.Horizontal) self.menu_toolbar.setIconSize(QSize(20, 20)) self.menu_toolbar.addAction( icon("settings.svg"), "Editor Settings").triggered.connect(show_settings) self.menu_toolbar.addSeparator() self.menu_toolbar.addAction( icon("folder.svg"), "Open ROM").triggered.connect(self.on_open_rom) self.menu_toolbar.addAction( icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom) self.menu_toolbar.addSeparator() self.undo_action = self.menu_toolbar.addAction(icon("rotate-ccw.svg"), "Undo Action") self.undo_action.triggered.connect(self.level_ref.undo) self.undo_action.setEnabled(False) self.redo_action = self.menu_toolbar.addAction(icon("rotate-cw.svg"), "Redo Action") self.redo_action.triggered.connect(self.level_ref.redo) self.redo_action.setEnabled(False) self.menu_toolbar.addSeparator() play_action = self.menu_toolbar.addAction(icon("play-circle.svg"), "Play Level") play_action.triggered.connect(self.on_play) play_action.setWhatsThis( "Opens an emulator with the current Level set to 1-1.\nSee Settings." ) self.menu_toolbar.addSeparator() self.menu_toolbar.addAction(icon("zoom-out.svg"), "Zoom Out").triggered.connect( self.level_view.zoom_out) self.menu_toolbar.addAction(icon("zoom-in.svg"), "Zoom In").triggered.connect( self.level_view.zoom_in) self.menu_toolbar.addSeparator() header_action = self.menu_toolbar.addAction(icon("tool.svg"), "Edit Level Header") header_action.triggered.connect(self.on_header_editor) header_action.setWhatsThis( "<b>Header Editor</b><br/>" "Many configurations regarding the level are done in its header, like the length of " "the timer, or where and how Mario enters the level.<br/>") self.jump_destination_action = self.menu_toolbar.addAction( icon("arrow-right-circle.svg"), "Go to Jump Destination") self.jump_destination_action.triggered.connect( self._go_to_jump_destination) self.jump_destination_action.setWhatsThis( "Opens the level, that can be reached from this one, e.g. by entering a pipe." ) self.menu_toolbar.addSeparator() whats_this_action = QWhatsThis.createAction() whats_this_action.setWhatsThis( "Click on parts of the editor, to receive help information.") whats_this_action.setIcon(icon("help-circle.svg")) whats_this_action.setText("Starts 'What's this?' mode") self.menu_toolbar.addAction(whats_this_action) self.menu_toolbar.addSeparator() self.warning_list = WarningList(self, self.level_ref) warning_action = self.menu_toolbar.addAction( icon("alert-triangle.svg"), "Warning Panel") warning_action.setWhatsThis("Shows a list of warnings.") warning_action.triggered.connect(self.warning_list.show) warning_action.setDisabled(True) self.warning_list.warnings_updated.connect(warning_action.setEnabled) self.addToolBar(Qt.TopToolBarArea, self.menu_toolbar) self.status_bar = ObjectStatusBar(self, self.level_ref) self.setStatusBar(self.status_bar) self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self, self.remove_selected_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self, self.level_view.zoom_in) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self, self.level_view.zoom_out) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self, self.level_view.select_all) self.on_open_rom(path_to_rom) self.showMaximized()
class ClassTreeWidget(QDialog): """ TreeView widget to show pyleecan classes structured by their inheritance together with the selected class description. """ def __init__(self, keys=None, expand=True): super(ClassTreeWidget, self).__init__() self.setupUi() self.expandAll = expand self.setMinimumHeight(600) # === default variables === self.classDict = ClassInfo().get_dict() self.keys = keys or ClassInfo().get_base_classes() # TODO all classes self.selectionModel = self.treeView.selectionModel() # === Signals === self.selectionModel.selectionChanged.connect(self.onSelectionChanged) self.treeView.doubleClicked.connect(self.onClassSelected) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) # === Generate content === self.generate() def onClassSelected(self, index): """Method to accept the selection if a class was double clicked.""" if index.isValid(): self.accept() def onSelectionChanged(self, itSelection): """ """ index = itSelection.indexes()[0] desc = index.model().itemFromIndex(index).data() self.text.setText(desc) def getSelectedClass(self): """Get the currently selected class by its name.""" index = self.selectionModel.selectedIndexes()[0] return index.model().itemFromIndex(index).text() def setupUi(self): """Init. the UI.""" self.setWindowIcon(QIcon(ICON)) # === Widgets === # TreeView model = QtGui.QStandardItemModel(0, 1) model.setHorizontalHeaderLabels(["Class"]) self.treeView = QTreeView() self.treeView.rootNode = model.invisibleRootItem() self.treeView.setModel(model) self.treeView.setAlternatingRowColors(False) # size options # setting min. width in self.generate to fit content self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) # Hide Debug Columns # self.treeView.hideColumn(1) # text output self.text = QLabel() self.text.setAlignment(Qt.AlignTop) self.text.setWordWrap(True) self.text.setMinimumWidth(300) # Splitters self.splitter = QSplitter(self) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.splitter.addWidget(self.treeView) self.splitter.addWidget(self.text) # dialog buttons self.buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel ) # window to create confirmation and cancellation buttons # === Layout === # Horizontal Div. layout = QVBoxLayout() # layout.setContentsMargins(0, 0, 0, 0) # layout.setSpacing(0) layout.addWidget(self.splitter) layout.addWidget(self.buttons) self.setLayout(layout) def generate(self): """Method to (recursively) build the tree (view) of the data object.""" self.treeDict = dict() for key in self.keys: self.genTreeDict(key, self.treeDict) self.genTreeView(self.treeDict) # set first row active & expand all branches index = self.treeView.model().index(0, 0) self.treeView.setCurrentIndex(index) self.treeView.expandAll() wHint = self.treeView.sizeHintForColumn(0) self.treeView.setMinimumWidth(wHint) self.treeView.setColumnWidth(0, wHint) if not self.expandAll: self.treeView.collapseAll() def genTreeDict(self, key, parent): """Generate a dict structure of the classes recursively on the parent dict.""" parent[key] = dict() for typ in self.classDict[key]["daughters"]: if key == self.classDict[typ]["mother"]: self.genTreeDict(typ, parent[key]) def genTreeView(self, tree, parent=None): """Generate the view item structure on the parent item.""" # init if root if parent is None: parent = self.treeView.rootNode self.treeView.rootNode.removeRows( 0, self.treeView.rootNode.rowCount()) for key, item in tree.items(): desc = ( f"Class: {key} \nPackage: {self.classDict[key]['package']}" + f"\nDescription: {self.classDict[key]['desc']}") row = self.addRow(parent, key, desc) if item: self.genTreeView(item, parent=row) def addRow(self, parent, name="", desc=""): """Add a new row to the parent item.""" item = QtGui.QStandardItem(name) item.setEditable(False) item.setData(desc) parent.appendRow([item]) return item
class MainWindow(QObject): """ 控制系统的主窗口 """ def __init__(self): """ 初始化界面 """ # # 成员变量 # super().__init__() self.isPolling = False # 是否在轮询中 self.helper = Helper() # 控制器 self.polling_timer = None # 轮询定时器,设成成员变量是为了能够取消定时器 self.play_sound = True # 默认允许播放蜂鸣声 # # 初始化界面 # self.ui = QUiLoader().load('./QtDesigner/main_window.ui' ) # 加载 UI 文件,self.ui 就是应用中 MainWindow 这个对象 self.init_window() # 初始化主窗口 self.init_interval_combobox() # 初始化轮询时间下拉框 self.init_splitter() # 初始化分离器 self.init_table() # 初始化表格 self.init_output_edit() # 初始化输出信息的窗口 self.init_menu_bar() # 初始化菜单 self.ui.btn_submit.clicked.connect(self.start_polling) # 绑定点击事件 def init_window(self): """ 初始化主窗口 :return: 无 """ self.ui.setWindowTitle("光伏集群现场监控") # 窗口标题 # # 左上角图标,任务栏图标 # app_icon = QIcon("./other/logo.ico") self.ui.setWindowIcon(app_icon) # # 读取配置文件 # baudrate, address = self.helper.read_config() if baudrate != "" and address != "": self.ui.edit_baudrate.setText(baudrate) self.ui.edit_addr.setText(address) def init_menu_bar(self): """ 初始化菜单栏 :return: 无 """ self.ui.restart.triggered.connect( partial(self.one_key, self.ui.restart.text())) self.ui.lock.triggered.connect( partial(self.one_key, self.ui.lock.text())) self.ui.unlock1.triggered.connect( partial(self.one_key, self.ui.unlock1.text())) self.ui.wind_bread.triggered.connect( partial(self.one_key, self.ui.wind_bread.text())) self.ui.planish.triggered.connect( partial(self.one_key, self.ui.planish.text())) self.ui.snow_removal.triggered.connect( partial(self.one_key, self.ui.snow_removal.text())) self.ui.clean_board.triggered.connect( partial(self.one_key, self.ui.clean_board.text())) self.ui.sub_angle.triggered.connect( partial(self.one_key, self.ui.sub_angle.text())) self.ui.add_angle.triggered.connect( partial(self.one_key, self.ui.add_angle.text())) self.ui.advance.triggered.connect(self.show_advance_dialog) # # 日志 # self.ui.wind_log.triggered.connect(partial(self.show_window, "wind")) self.ui.error_log.triggered.connect(partial(self.show_window, "error")) # # 帮助 # self.ui.about.triggered.connect(self.show_about) self.ui.open_book.triggered.connect(partial(self.show_window, "book")) # # 蜂鸣报警声 # self.ui.close_beep.triggered.connect(self.set_play_sound) def init_interval_combobox(self): """ 初始化轮询时间下拉列表框 :return: 无 """ interval_list = ['5 s', '10 s', '15 s', '30 s', '60 s'] self.ui.combobox_interval.addItems(interval_list) def init_splitter(self): """ 初始化分离器控件 :return: 无 """ self.main_splitter = QSplitter(Qt.Vertical) # 新建一个分离器,垂直分离 # # 分离器添加控件 # self.main_splitter.addWidget(self.ui.tableView) self.main_splitter.addWidget(self.ui.output_edit) # # 设置窗口比例 # self.main_splitter.setStretchFactor(0, 8) self.main_splitter.setStretchFactor(1, 2) self.ui.data_layout.addWidget( self.main_splitter) # 把这个 splitter 放在一个布局里才能显示出来 def init_output_edit(self): """ 初始化输出框 :return: 无 """ self.ui.output_edit.setReadOnly(True) # 禁止编辑 # # 添加右键菜单 # self.ui.output_edit.setContextMenuPolicy( Qt.ActionsContextMenu) # 允许右键菜单 send_option = QAction(self.ui.output_edit) # 具体菜单项 send_option.setText("清除内容") send_option.triggered.connect( self.ui.output_edit.clear) # 点击菜单中的具体选项执行的函数 self.ui.output_edit.addAction(send_option) def init_table(self): """ 初始化表格样式 :return:无 """ self.ui.tableView.verticalHeader().setVisible(False) # 隐藏垂直表头 self.ui.tableView.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) # 设置每列宽度:根据表头调整表格宽度 self.ui.tableView.resizeColumnsToContents() # 根据内容调整列宽 self.ui.tableView.clicked.connect(self.handle_table_click) # 鼠标左键点击事件 def show_window(self, name: str): """ 显示 Windows 资源管理器 :param url: 路径 :return: 无 """ path = None if name == "wind": path = os.getcwd() + r"\log\wind_speed" elif name == "error": path = os.getcwd() + r"\log\error_log" elif name == "book": path = os.getcwd() + r"\other\光伏集群现场监控使用说明书.pdf" if os.path.exists(path): os.system("explorer.exe %s" % path) else: dail = InfoDialog("提示", "目前还没有日志!") dail.exec_() def show_about(self): """ 显示关于信息 :return: 无 """ dial = InfoDialog("关于", "四川近日点新能源科技有限公司\n联系电话:15108303256") dial.exec_() # 进入事件循环,不关闭不会退出循环 def set_play_sound(self): """ 设置蜂鸣声 :return: """ if self.play_sound is False: self.play_sound = True self.append_info("已打开蜂鸣报警声") self.ui.close_beep.setText("关闭蜂鸣报警声") else: self.play_sound = False self.append_info("已关闭蜂鸣报警声") self.ui.close_beep.setText("打开蜂鸣报警声") def get_serial_info(self): com_port = self.ui.box_com_port.currentText() # 端口号 collector_addr = self.ui.edit_addr.text() # 数采地址 baud_rate = self.ui.edit_baudrate.text() # 波特率 return com_port, collector_addr, baud_rate def one_key(self, option: str): """ 一键命令 :param option:命令 :return:无 """ # self.update_serial() if self.isPolling is False: self.append_info("请先开始轮询,然后再开始一键操作!", 'red') else: message.append({'cmd': 'one_key', 'option': "%s" % option}) @Slot(int) def update_member(self, member: int): """ 更新成员数 :param member:成员数 :return:无 """ self.ui.label_member.setText(str(member)) @Slot(str) def update_port(self, port_name_list: str): """ 初始化下拉列表框 :return:无 """ port_name_list = eval(port_name_list) self.ui.box_com_port.clear() self.ui.box_com_port.addItems(port_name_list) if len(port_name_list) > 0: self.append_info("共扫描到 %d 个串口设备:%s" % (len(port_name_list), port_name_list)) self.ui.btn_submit.setEnabled(True) # 可以轮询 else: self.append_info('未检测到任何串口设备,请检查连接是否正常', 'red') # # 加个 if 是为了判断是不是正在轮询中, # 有可能是正在轮询,突然没有一个串口设备了 # self.ui.btn_submit.setEnabled(False) # 禁止轮询 @Slot(str, str) def append_info(self, text: str, color: str = 'black'): """ 追加信息到输出狂 :param info:信息,字典字符串 :return: """ black = QColor(0, 0, 0) if color == 'red': c = QColor(255, 0, 0) elif color == 'green': c = QColor(0, 128, 0) else: c = black # # 使用指定颜色追加文本,然后恢复为黑色 # self.ui.output_edit.setTextColor(c) self.ui.output_edit.append(util.get_time() + " " + text) # 显示文本 self.ui.output_edit.setTextColor(black) self.ui.output_edit.moveCursor(QTextCursor.End) # 输出信息后滚动到最后 def handle_table_click(self, item: QModelIndex): """ 处理表格左键点击事件 :param item:点击的单元格 :return:无 """ # # 首先获取点击的行、列号 # 判断点击的单元格是否可点击,如果可以, # 就弹出编辑对话框 # i = item.row() j = item.column() if self.is_editable(i, j): # # 如果在轮询的话,立即停止,并标记需要恢复 # # recover = False # # if self.isPolling: # self.stop_polling() # recover = True # # 根据行号、列号计算出机器编号,然后显示修改窗口 # machine_number = self.helper.get_machine_number(i, j) if self.isPolling is False: self.append_info("请先开始轮询,再发送控制代码!", 'red') else: self.show_modify_dialog(machine_number) # if recover is True: # self.start_polling() def is_editable(self, i, j) -> bool: """ 判断该单元格是否可编辑 :param i:行,从 0 开始 :param j:列,从 0 开始 :return:是否可编辑 """ # # i % 4 == 1 表示 i 在控制代码行 # j % 2 == 1 表示 j 在控制代码列 # if i % 4 == 1 and j % 2 == 1: return True return False def set_enable(self, enable: bool): """ 是否允许编辑串口参数相关 :param enable: 是否允许 :return: 无 """ self.ui.box_com_port.setEnabled(enable) # 端口下拉框 self.ui.edit_baudrate.setEnabled(enable) # 波特率 self.ui.edit_addr.setEnabled(enable) # 地址 self.ui.combobox_interval.setEnabled(enable) # 轮询时间 def start_polling(self): """ 开始轮询按钮响应函数 提交轮询 or 停止轮询 :return:无 """ if not self.isPolling: self.isPolling = True self.ui.btn_submit.setText("停止轮询") self.set_enable(False) # 禁止编辑 self.update_serial() self.append_info("开始轮询") self.polling() else: self.stop_polling() def update_serial(self): com_port, collector_addr, baud_rate = self.get_serial_info() message.append({ 'cmd': 'update_serial', 'com_port': com_port, 'collector_addr': collector_addr, 'baud_rate': baud_rate }) def polling(self): """ 轮询查询 :return:无 """ # # 更新界面 # message.append({'cmd': 'member'}) message.append({'cmd': 'table'}) message.append({'cmd': 'wind_speed'}) self.next_polling() # 准备下一次轮询 @Slot() def stop_polling(self): """ 立即停止轮询 :return:无 """ # # 如果正在轮询,就停止轮询 # 点击“停止轮询”,则按钮立即显示“开始轮询” # if self.isPolling: self.ui.btn_submit.setText("开始轮询") self.set_enable(True) # 恢复允许编辑 self.isPolling = False self.append_info("结束轮询") # 信息台输出结束信息 self.close_serial() # # 强制停止子线程 # if self.polling_timer is not None and self.polling_timer.is_alive( ): util.stop_thread(self.polling_timer) def close_serial(self): """ 发命令关闭串口 :return: """ msg = {"cmd": 'close_serial'} message.append(msg) def next_polling(self): """ 设置一个定时器,到了一定时间后就启动轮询 :return:无 """ interval = int( self.ui.combobox_interval.currentText()[:-2]) # 获取间隔时间下拉框的值 self.polling_timer = Timer(interval, self.polling) self.polling_timer.setName("polling_timer") self.polling_timer.setDaemon(True) self.polling_timer.start() @Slot(str) def update_wind_speed(self, wind_speed: str): """ 更新风速 :param wind_speed:风速值 :return: 无 """ self.ui.label_wind_speed.setText(wind_speed + " m/s") collector_addr = self.ui.edit_addr.text() # 数采地址 self.helper.write_wind_speed(float(wind_speed), collector_addr) @Slot(str) def update_table(self, table_data: str): """ 更新表格数据 :param table_data:表格数据 :return:无 """ table_model, error_number = self.helper.get_table_model(table_data) if error_number != "": self.append_info("有异常机器编号:" + error_number) self.play_error_sound() collector_addr = self.ui.edit_addr.text() # 数采地址 self.helper.write_error(error_number, collector_addr) self.ui.tableView.setModel(table_model) def play_error_sound(self): """ 播放警报 :return: """ if self.play_sound: playsound.playsound("./other/bee.mp3") def show_modify_dialog(self, machine_number: int): """ 显示发送控制代码的对话框 :return: 无 """ input_dial = ModifyDialog("发送控制代码", "%d 号控制器:" % machine_number) if input_dial.exec_(): try: value = input_dial.textValue() # # 检查输入 # obj = re.match(r"^[0-9]{1,2}$", "12") if obj is None: raise ValueError else: code = int(value) if 1 <= code <= 99: code = int(input_dial.textValue()) self.send_control_code(machine_number, code) else: raise ValueError except ValueError: # # 提示输入有误 # info = "输入的控制代码有误!控制代码只能是:1~99" dial = InfoDialog("提示", info) dial.exec_() # 进入事件循环 def show_advance_dialog(self): """ 显示高级对话框,留着预防以后加什么新命令,可以通过这个对话框进行发送 :return: """ input_dial = ModifyDialog("发送控制代码", "对所有控制器发送:") if input_dial.exec_(): try: value = input_dial.textValue() # # 判断输入字符的长度,大于 2 就不是 1<= x <= 99 # obj = re.match(r"^[0-9]{1,2}$", "12") if obj is None: raise ValueError else: code = int(value) if 1 <= code <= 99: code = int(input_dial.textValue()) self.one_key(code) else: raise ValueError except ValueError: # # 提示输入有误 # info = "输入的控制代码有误!控制代码只能是 1~99!" dial = InfoDialog("提示", info) dial.exec_() # 进入事件循环 def send_control_code(self, machine_number: int, code: int) -> None: """ 发送控制代码 :param machine_number:机器编号 :param code:控制代码 :return:无 """ msg = {} msg["cmd"] = 'send_control_code' code = '{:04X}'.format(code) # 十进制控制代码转十六进制 msg['machine_number'] = "%d" % machine_number msg['code'] = "%s" % code message.append(msg)
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(1280, 720) self.central_widget = QWidget(MainWindow) self.splitter = QSplitter(self.central_widget) self.splitter.setChildrenCollapsible(False) self.splitter.setOpaqueResize(True) self.main_grid_layout = QGridLayout(self.central_widget) self.main_grid_layout.addWidget(self.splitter) self.main_grid_layout.setSizeConstraint(QLayout.SetDefaultConstraint) self.tab_widget = QTabWidget(self.central_widget) self.tab_widget.setMinimumSize(QSize(500, 0)) self._set_up_component_tree_view() self.splitter.addWidget(self.tab_widget) self._set_up_3d_view() MainWindow.setCentralWidget(self.central_widget) self._set_up_menus(MainWindow) self.tab_widget.setCurrentIndex(0) QMetaObject.connectSlotsByName(MainWindow) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) def _set_up_3d_view(self): self.sceneWidget.setMinimumSize(QSize(600, 0)) self.splitter.addWidget(self.sceneWidget) def _set_up_component_tree_view(self): self.sceneWidget = InstrumentView(self.splitter) self.component_tree_view_tab = ComponentTreeViewTab( scene_widget=self.sceneWidget, parent=self ) self.tab_widget.addTab(self.component_tree_view_tab, "") def _set_up_menus(self, MainWindow: QObject): self.menu_bar = QMenuBar() self.menu_bar.setGeometry(QRect(0, 0, 1280, 720)) self.file_menu = QMenu(self.menu_bar) MainWindow.setMenuBar(self.menu_bar) self.status_bar = QStatusBar(MainWindow) MainWindow.setStatusBar(self.status_bar) self.open_json_file_action = QAction(MainWindow) self.open_json_file_action.setShortcut(QKeySequence("Ctrl+O")) self.export_to_filewriter_JSON_action = QAction(MainWindow) self.export_to_filewriter_JSON_action.setShortcut(QKeySequence("Ctrl+S")) self.file_menu.addAction(self.open_json_file_action) self.file_menu.addAction(self.export_to_filewriter_JSON_action) self.view_menu = QMenu(self.menu_bar) self.show_action_labels = QAction(MainWindow) self.show_action_labels.setCheckable(True) self.show_action_labels.setChecked(True) self.simple_tree_view = QAction(MainWindow) self.simple_tree_view.setCheckable(True) self.about_window = QAction(MainWindow) self.view_menu.addAction(self.about_window) self.view_menu.addAction(self.show_action_labels) self.view_menu.addAction(self.simple_tree_view) self.menu_bar.addAction(self.file_menu.menuAction()) self.menu_bar.addAction(self.view_menu.menuAction()) self._set_up_titles(MainWindow) def _set_up_titles(self, MainWindow): MainWindow.setWindowTitle("NeXus Constructor") self.tab_widget.setTabText( self.tab_widget.indexOf(self.component_tree_view_tab), "Nexus Structure" ) self.file_menu.setTitle("File") self.open_json_file_action.setText("Open File writer JSON file") self.export_to_filewriter_JSON_action.setText("Export to File writer JSON") self.view_menu.setTitle("View") self.show_action_labels.setText("Show Button Labels") self.simple_tree_view.setText("Use Simple Tree Model View") self.about_window.setText("About") self.menu_bar.setNativeMenuBar(False)
def __init__(self, path_to_rom=""): super(MainWindow, self).__init__() self.setWindowIcon(icon("foundry.ico")) file_menu = QMenu("File") open_rom_action = file_menu.addAction("&Open ROM") open_rom_action.triggered.connect(self.on_open_rom) self.open_m3l_action = file_menu.addAction("&Open M3L") self.open_m3l_action.triggered.connect(self.on_open_m3l) file_menu.addSeparator() save_rom_action = file_menu.addAction("&Save ROM") save_rom_action.triggered.connect(self.on_save_rom) save_rom_as_action = file_menu.addAction("&Save ROM as ...") save_rom_as_action.triggered.connect(self.on_save_rom_as) """ file_menu.AppendSeparator() """ self.save_m3l_action = file_menu.addAction("&Save M3L") self.save_m3l_action.triggered.connect(self.on_save_m3l) """ file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "") file_menu.AppendSeparator() file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "") file_menu.AppendSeparator() file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "") """ file_menu.addSeparator() settings_action = file_menu.addAction("&Settings") settings_action.triggered.connect(show_settings) file_menu.addSeparator() exit_action = file_menu.addAction("&Exit") exit_action.triggered.connect(lambda _: self.close()) self.menuBar().addMenu(file_menu) """ edit_menu = wx.Menu() edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "") edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "") edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "") edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "") edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "") edit_menu.AppendSeparator() edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "") edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "") """ level_menu = QMenu("Level") select_level_action = level_menu.addAction("&Select Level") select_level_action.triggered.connect(self.open_level_selector) """ level_menu.Append(ID_GOTO_NEXT_AREA, "&Go to next Area", "") level_menu.AppendSeparator() """ self.reload_action = level_menu.addAction("&Reload Level") self.reload_action.triggered.connect(self.reload_level) level_menu.addSeparator() self.edit_header_action = level_menu.addAction("&Edit Header") self.edit_header_action.triggered.connect(self.on_header_editor) """ level_menu.Append(ID_EDIT_POINTERS, "&Edit Pointers", "") """ self.menuBar().addMenu(level_menu) object_menu = QMenu("Objects") view_blocks_action = object_menu.addAction("&View Blocks") view_blocks_action.triggered.connect(self.on_block_viewer) view_objects_action = object_menu.addAction("&View Objects") view_objects_action.triggered.connect(self.on_object_viewer) """ object_menu.AppendSeparator() object_menu.Append(ID_CLONE_OBJECT_ENEMY, "&Clone Object/Enemy", "") object_menu.AppendSeparator() object_menu.Append(ID_ADD_3_BYTE_OBJECT, "&Add 3 Byte Object", "") object_menu.Append(ID_ADD_4_BYTE_OBJECT, "&Add 4 Byte Object", "") object_menu.Append(ID_ADD_ENEMY, "&Add Enemy", "") object_menu.AppendSeparator() object_menu.Append(ID_DELETE_OBJECT_ENEMY, "&Delete Object/Enemy", "") object_menu.Append(ID_DELETE_ALL, "&Delete All", "") """ self.menuBar().addMenu(object_menu) view_menu = QMenu("View") view_menu.triggered.connect(self.on_menu) action = view_menu.addAction("Mario") action.setProperty(ID_PROP, ID_MARIO) action.setCheckable(True) action.setChecked(SETTINGS["draw_mario"]) action = view_menu.addAction("&Jumps on objects") action.setProperty(ID_PROP, ID_JUMP_OBJECTS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jump_on_objects"]) action = view_menu.addAction("Items in blocks") action.setProperty(ID_PROP, ID_ITEM_BLOCKS) action.setCheckable(True) action.setChecked(SETTINGS["draw_items_in_blocks"]) action = view_menu.addAction("Invisible items") action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS) action.setCheckable(True) action.setChecked(SETTINGS["draw_invisible_items"]) view_menu.addSeparator() action = view_menu.addAction("Jump Zones") action.setProperty(ID_PROP, ID_JUMPS) action.setCheckable(True) action.setChecked(SETTINGS["draw_jumps"]) action = view_menu.addAction("&Grid lines") action.setProperty(ID_PROP, ID_GRID_LINES) action.setCheckable(True) action.setChecked(SETTINGS["draw_grid"]) action = view_menu.addAction("Resize Type") action.setProperty(ID_PROP, ID_RESIZE_TYPE) action.setCheckable(True) action.setChecked(SETTINGS["draw_expansion"]) view_menu.addSeparator() action = view_menu.addAction("&Block Transparency") action.setProperty(ID_PROP, ID_TRANSPARENCY) action.setCheckable(True) action.setChecked(SETTINGS["block_transparency"]) view_menu.addSeparator() view_menu.addAction("&Save Screenshot of Level").triggered.connect( self.on_screenshot) """ view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "") view_menu.Append(ID_TOOLBAR, "&Toolbar", "") view_menu.AppendSeparator() view_menu.Append(ID_ZOOM, "&Zoom", "") view_menu.AppendSeparator() view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "") view_menu.Append(ID_PALETTE, "&Palette", "") view_menu.AppendSeparator() view_menu.Append(ID_MORE, "&More", "") """ self.menuBar().addMenu(view_menu) help_menu = QMenu("Help") """ help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "") help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "") help_menu.AppendSeparator() help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "") help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "") help_menu.AppendSeparator() """ update_action = help_menu.addAction("Check for updates") update_action.triggered.connect(self.on_check_for_update) help_menu.addSeparator() video_action = help_menu.addAction("Feature Video on YouTube") video_action.triggered.connect(lambda: open_url(feature_video_link)) discord_action = help_menu.addAction("SMB3 Rom Hacking Discord") discord_action.triggered.connect(lambda: open_url(discord_link)) help_menu.addSeparator() about_action = help_menu.addAction("&About") about_action.triggered.connect(self.on_about) self.menuBar().addMenu(help_menu) self.level_selector = LevelSelector(parent=self) self.block_viewer = None self.object_viewer = None self.level_ref = LevelRef() self.level_ref.data_changed.connect(self._on_level_data_changed) self.context_menu = ContextMenu(self.level_ref) self.context_menu.triggered.connect(self.on_menu) self.level_view = LevelView(self, self.level_ref, self.context_menu) self.scroll_panel = QScrollArea() self.scroll_panel.setWidgetResizable(True) self.scroll_panel.setWidget(self.level_view) self.setCentralWidget(self.scroll_panel) self.spinner_panel = SpinnerPanel(self, self.level_ref) self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in) self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out) self.spinner_panel.object_change.connect(self.on_spin) self.object_list = ObjectList(self, self.level_ref, self.context_menu) self.object_dropdown = ObjectDropdown(self) self.object_dropdown.object_selected.connect( self._on_placeable_object_selected) self.level_size_bar = LevelSizeBar(self, self.level_ref) self.enemy_size_bar = EnemySizeBar(self, self.level_ref) self.jump_list = JumpList(self, self.level_ref) self.jump_list.add_jump.connect(self.on_jump_added) self.jump_list.edit_jump.connect(self.on_jump_edit) self.jump_list.remove_jump.connect(self.on_jump_removed) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) splitter.addWidget(self.object_list) splitter.setStretchFactor(0, 1) splitter.addWidget(self.jump_list) splitter.setChildrenCollapsible(False) level_toolbar = QToolBar("Level Info Toolbar", self) level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) level_toolbar.setOrientation(Qt.Horizontal) level_toolbar.setFloatable(False) level_toolbar.addWidget(self.spinner_panel) level_toolbar.addWidget(self.object_dropdown) level_toolbar.addWidget(self.level_size_bar) level_toolbar.addWidget(self.enemy_size_bar) level_toolbar.addWidget(splitter) level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.RightToolBarArea, level_toolbar) self.object_toolbar = ObjectToolBar(self) self.object_toolbar.object_selected.connect( self._on_placeable_object_selected) object_toolbar = QToolBar("Object Toolbar", self) object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu) object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) object_toolbar.setFloatable(False) object_toolbar.addWidget(self.object_toolbar) object_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea) self.addToolBar(Qt.LeftToolBarArea, object_toolbar) menu_toolbar = QToolBar("Menu Toolbar", self) menu_toolbar.setOrientation(Qt.Horizontal) menu_toolbar.setIconSize(QSize(20, 20)) menu_toolbar.addAction( icon("settings.svg"), "Editor Settings").triggered.connect(show_settings) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("folder.svg"), "Open ROM").triggered.connect(self.on_open_rom) menu_toolbar.addAction( icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom) menu_toolbar.addSeparator() self.undo_action = menu_toolbar.addAction(icon("rotate-ccw.svg"), "Undo Action") self.undo_action.triggered.connect(self.level_ref.undo) self.undo_action.setEnabled(False) self.redo_action = menu_toolbar.addAction(icon("rotate-cw.svg"), "Redo Action") self.redo_action.triggered.connect(self.level_ref.redo) self.redo_action.setEnabled(False) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("play-circle.svg"), "Play Level").triggered.connect(self.on_play) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("zoom-out.svg"), "Zoom Out").triggered.connect( self.level_view.zoom_out) menu_toolbar.addAction(icon("zoom-in.svg"), "Zoom In").triggered.connect( self.level_view.zoom_in) menu_toolbar.addSeparator() menu_toolbar.addAction(icon("tool.svg"), "Edit Level Header").triggered.connect( self.on_header_editor) self.jump_destination_action = menu_toolbar.addAction( icon("arrow-right-circle.svg"), "Go to Jump Destination") self.jump_destination_action.triggered.connect( self._go_to_jump_destination) menu_toolbar.addSeparator() # menu_toolbar.addAction(icon("help-circle.svg"), "What's this?") self.addToolBar(Qt.TopToolBarArea, menu_toolbar) self.status_bar = ObjectStatusBar(self, self.level_ref) self.setStatusBar(self.status_bar) self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self, self.remove_selected_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self, self.level_ref.redo) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self, self.level_view.zoom_in) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self, self.level_view.zoom_out) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self, self.level_view.select_all) if not self.on_open_rom(path_to_rom): self.deleteLater() self.showMaximized()
class BasisUI(QSplitter): def __init__(self, *args, **kwargs): super(BasisUI, self).__init__(*args, **kwargs) main_layout = QHBoxLayout() self.variety_tree = VarietyTree(self) main_layout.addWidget(self.variety_tree) self.right_widget = QWidget(self) right_layout = QVBoxLayout() right_layout.setContentsMargins(QMargins(1, 1, 1, 1)) opts_layout = QHBoxLayout() opts_layout.addWidget(QLabel("合约:", self)) self.contract_combobox = QComboBox(self) self.contract_combobox.setMinimumWidth(80) opts_layout.addWidget(self.contract_combobox) self.query_button = QPushButton("查询", self) opts_layout.addWidget(self.query_button) # 日期间隔选项 self.query_month_combobox = QComboBox(self) self.query_month_combobox.addItem("近三月", 3) self.query_month_combobox.addItem("近六月", 6) self.query_month_combobox.addItem("近一年", 12) opts_layout.addWidget(self.query_month_combobox) self.tip_label = QLabel('选择对应品种和合约后查询数据. ', self) opts_layout.addWidget(self.tip_label) opts_layout.addStretch() right_layout.addLayout(opts_layout) self.show_splitter = QSplitter(orientation=Qt.Vertical) # 图形容器 self.chart_view = QWebEngineView(self) self.chart_view.setMinimumHeight(int(self.height() * 0.518)) self.show_splitter.addWidget(self.chart_view) # 数据展示 self.chart_data_table = QTableWidget(self) self.chart_data_table.setEditTriggers( QAbstractItemView.NoEditTriggers) # 不可编辑 self.chart_data_table.setFocusPolicy(Qt.NoFocus) # 去选中时的虚线框 self.chart_data_table.setAlternatingRowColors(True) # 交替行颜色 self.chart_data_table.setFrameShape(QFrame.NoFrame) self.chart_data_table.setColumnCount(6) self.chart_data_table.setHorizontalHeaderLabels( ["品种", "合约", "日期", "现货价", "收盘价", "基差"]) self.show_splitter.addWidget(self.chart_data_table) self.show_splitter.setStretchFactor(0, 6) self.show_splitter.setStretchFactor(1, 4) self.show_splitter.setHandleWidth(2) right_layout.addWidget(self.show_splitter) self.right_widget.setLayout(right_layout) main_layout.addWidget(self.right_widget) self.setLayout(main_layout) self.setStretchFactor(0, 3) self.setStretchFactor(1, 7) self.setHandleWidth(1) self.tip_label.setObjectName("tipLabel") self.chart_data_table.setObjectName("dataTable") self.setStyleSheet( "#tipLabel{border:none;color:rgb(230,50,50);font-weight:bold}" "#dataTable{selection-color:rgb(255,255,255);selection-background-color:rgb(51,143,255);alternate-background-color:rgb(245,250,248)}" )
def __init__(self, *args, **kwargs): super(ExchangeSpiderUI, self).__init__(*args, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(QMargins(2, 0, 2, 1)) main_splitter = QSplitter(self) main_splitter.setHandleWidth(1) self.tree_widget = ExchangeLibTree(self) main_splitter.addWidget(self.tree_widget) action_splitter = QSplitter(Qt.Vertical, self) action_splitter.setHandleWidth(1) spider_widget = QWidget(self) spider_widget.setAutoFillBackground(True) palette = QPalette() pix = QPixmap("images/spider_bg.png") pix = pix.scaled(QSize(700, 700), Qt.KeepAspectRatio) palette.setBrush(QPalette.Background, QBrush(pix)) spider_widget.setPalette(palette) spider_layout = QVBoxLayout() tips_layout = QHBoxLayout() tips_layout.setSpacing(1) tips_layout.addWidget(QLabel("当前交易所:", self)) self.spider_exchange_button = QPushButton("未选择", self) tips_layout.addWidget(self.spider_exchange_button) tips_layout.addWidget(QLabel(self)) tips_layout.addWidget(QLabel("当前操作:", self)) self.spider_action_button = QPushButton("未选择", self) tips_layout.addWidget(self.spider_action_button) tips_layout.addWidget(QLabel(self)) tips_layout.addWidget(QLabel("选择日期:", self)) self.spider_date_edit = QDateEdit(QDate.currentDate(), self) self.spider_date_edit.setCalendarPopup(True) self.spider_date_edit.setDisplayFormat("yyyy-MM-dd") tips_layout.addWidget(self.spider_date_edit) tips_layout.addWidget(QLabel(self)) self.spider_start_button = QPushButton("开始", self) tips_layout.addWidget(self.spider_start_button) tips_layout.addStretch() spider_layout.addLayout(tips_layout) self.spider_status = QLabel("等待开始抓取", self) self.spider_status.setWordWrap(True) self.spider_status.setAlignment(Qt.AlignCenter) spider_layout.addWidget(self.spider_status) spider_widget.setLayout(spider_layout) action_splitter.addWidget(spider_widget) # 解析部分 parser_widget = QWidget(self) parser_widget.setAutoFillBackground(True) palette = QPalette() pix = QPixmap("images/parser_bg.png") pix = pix.scaled(QSize(700, 700), Qt.KeepAspectRatio) palette.setBrush(QPalette.Background, QBrush(pix)) parser_widget.setPalette(palette) parser_layout = QVBoxLayout() tips_layout = QHBoxLayout() tips_layout.setSpacing(1) tips_layout.addWidget(QLabel("当前交易所:", self)) self.parser_exchange_button = QPushButton("未选择", self) tips_layout.addWidget(self.parser_exchange_button) tips_layout.addWidget(QLabel(self)) tips_layout.addWidget(QLabel("当前操作:", self)) self.parser_action_button = QPushButton("未选择", self) tips_layout.addWidget(self.parser_action_button) tips_layout.addWidget(QLabel(self)) tips_layout.addWidget(QLabel("选择日期:", self)) self.parser_date_edit = QDateEdit(QDate.currentDate(), self) self.parser_date_edit.setCalendarPopup(True) self.parser_date_edit.setDisplayFormat("yyyy-MM-dd") tips_layout.addWidget(self.parser_date_edit) tips_layout.addWidget(QLabel(self)) self.parser_start_button = QPushButton("开始", self) tips_layout.addWidget(self.parser_start_button) tips_layout.addStretch() parser_layout.addLayout(tips_layout) self.parser_status = QLabel("等待开始解析", self) self.parser_status.setAlignment(Qt.AlignCenter) parser_layout.addWidget(self.parser_status) parser_widget.setLayout(parser_layout) action_splitter.addWidget(parser_widget) main_splitter.addWidget(action_splitter) main_splitter.setStretchFactor(0, 4) main_splitter.setStretchFactor(1, 6) layout.addWidget(main_splitter) self.setLayout(layout) main_splitter.setObjectName("mainSplitter") action_splitter.setObjectName("actionSplitter") self.spider_exchange_button.setObjectName("tipButton") self.spider_action_button.setObjectName("tipButton") self.spider_status.setObjectName("spiderStatus") self.parser_exchange_button.setObjectName("tipButton") self.parser_action_button.setObjectName("tipButton") self.parser_status.setObjectName("parserStatus") self.setStyleSheet( "#mainSplitter::handle{background-color:rgba(50,50,50,100)}" "#actionSplitter::handle{background-color:rgba(50,50,50,100)}" "#tipButton{border:none;color:rgb(220,100,100)}" "#spiderStatus,#parserStatus{font-size:16px;font-weight:bold;color:rgb(230,50,50)}" )
def init_ui(self): self.setWindowTitle('Trace Event Window') self.setGeometry(100, 100, 800, 600) bar = self.menuBar() file_ = bar.addMenu('File') export_log = QAction('Save to File', self, triggered=lambda: self.save_log()) options = bar.addMenu('Options') auto_refresh = QAction( 'Auto Refresh', self, checkable=True, triggered=lambda: self.timer.start(100) if auto_refresh.isChecked() else self.timer.stop()) auto_refresh.setChecked(True) options.addAction(auto_refresh) file_.addAction(export_log) vgrid = QVBoxLayout() grid = QHBoxLayout() self.tree = QTreeWidget() self.tree.setHeaderLabels(['Name']) self.top = [] self.lst = [] for n, event in enumerate(self.trace_events): word = event.split('_')[0] if word not in self.top: self.top.append(word) item = QTreeWidgetItem(self.tree) self.lst.append(item) item.setText(0, word) subitem = QTreeWidgetItem(item) subitem.setText(0, ' ' + event.split(' : ')[0]) # subitem.setCheckState(0, Qt.Unchecked) cbox = QCheckBox() cbox.stateChanged.connect(lambda state, text=subitem.text(0): self. handle_checked(state, text)) self.tree.setItemWidget(subitem, 0, cbox) # self.tree.setColumnWidth(0, 25) self.tracelist = QLabel() self.disp_output() self.traceview = QScrollArea() self.traceview.setWidget(self.tracelist) self.traceview.setWidgetResizable(True) search = QHBoxLayout() self.search_bar = QLineEdit(self) self.completer = QCompleter(self.top, self) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.search_bar.setCompleter(self.completer) search_button = QPushButton('Search') search_button.clicked.connect(lambda: self.tree.setCurrentItem( self.lst[self.top.index(self.search_bar.text())])) expand = QPushButton('▼') expand.setFixedSize(QSize(25, 25)) expand.clicked.connect(lambda: self.tree.expandAll()) collapse = QPushButton('▲') collapse.setFixedSize(QSize(25, 25)) collapse.clicked.connect(lambda: self.tree.collapseAll()) self.search_bar.returnPressed.connect(lambda: search_button.click()) search.addWidget(self.search_bar) search.addWidget(search_button) search.addWidget(expand) search.addWidget(collapse) self.digest = QLabel() vgrid.addLayout(search) vgrid.addWidget(self.tree) vgridwid = QWidget() vgridwid.setLayout(vgrid) split = QSplitter(Qt.Horizontal) split.addWidget(vgridwid) split.addWidget(self.traceview) split.setStretchFactor(1, 1) # grid.addLayout(vgrid) grid.addWidget(split) # grid.addWidget(self.tracelist) self.disp_output() center = QWidget() center.setLayout(grid) self.setCentralWidget(center) self.show()
class TableWidget(QWidget): def __init__(self, db, name, select, update_ui): super().__init__() self.db = db self.setWindowTitle(name) self.dirty = False self.make_widgets(select) self.make_layout() self.make_connections(update_ui) def make_widgets(self, select): self.sqlEdit = SQLEdit.SQLEdit(select) self.sqlEdit.setTabChangesFocus(True) self.tableModel = TableModel.TableModel(self.db, Sql.uncommented(select)) self.tableView = QTableView() self.tableView.setModel(self.tableModel) self.statusLabel = QLabel() self.statusLabel.setTextFormat(Qt.RichText) self.update_status(select) def make_layout(self): self.splitter = QSplitter(Qt.Vertical) self.splitter.addWidget(self.sqlEdit) self.splitter.addWidget(self.tableView) self.splitter.setStretchFactor(1, 11) vbox = QVBoxLayout() vbox.addWidget(self.splitter) vbox.addWidget(self.statusLabel) self.setLayout(vbox) def make_connections(self, update_ui): self.sqlEdit.textChanged.connect(update_ui) self.sqlEdit.copyAvailable.connect(update_ui) self.tableModel.sql_error.connect(self.on_sql_error) @property def sizes(self): return self.splitter.sizes() @property def is_select(self): return Sql.is_select(self.sql) @property def sql(self): return self.sqlEdit.toPlainText() def refresh(self): if not self.is_select: self.statusLabel.setText('<font color=red>Only SELECT ' 'statements are supported here</font>') else: select = Sql.uncommented(self.sqlEdit.toPlainText()) if re.match(r'\s*SELECT(:?\s+(:?ALL|DISTINCT))?\s+\*', select, re.IGNORECASE | re.DOTALL): try: names = ', '.join([ Sql.quoted(name) for name in self.db.field_names_for_select(select) ]) select = select.replace('*', names, 1) except apsw.SQLError as err: self.on_sql_error(str(err)) return self.tableModel.refresh(select) self.update_status(select) def on_sql_error(self, err): self.statusLabel.setText(f'<font color=red>{err}</font>') def update_status(self, select): try: count = self.db.select_row_count(select) s = 's' if count != 1 else '' self.statusLabel.setText(f'{count:,} row{s}') except (apsw.SQLError, Sql.Error) as err: self.on_sql_error(str(err)) def closeEvent(self, event): self.save(closing=True) event.accept() def save(self, *, closing=False): print(f'TableWidget.save dirty={self.dirty} closing={closing}') saved = False errors = False if self.dirty and bool(self.db): # TODO save change to list view or form view errors = [] # self.db.save_... if errors: if not closing: error = '\n'.join(errors) QMessageBox.warning(self, f'Save error — {APPNAME}', f'Failed to save:\n{error}') else: saved = True self.dirty = False return saved
class MainWidget(QWidget): def __init__(self, parent: QWidget, model: Model) -> None: super().__init__(parent) logger.add(self.log) self.mainlayout = QVBoxLayout() self.setLayout(self.mainlayout) self.splitter = QSplitter(Qt.Vertical) self.stack = QStackedWidget() self.splitter.addWidget(self.stack) # mod list widget self.modlistwidget = QWidget() self.modlistlayout = QVBoxLayout() self.modlistlayout.setContentsMargins(0, 0, 0, 0) self.modlistwidget.setLayout(self.modlistlayout) self.stack.addWidget(self.modlistwidget) # search bar self.searchbar = QLineEdit() self.searchbar.setPlaceholderText('Search...') self.modlistlayout.addWidget(self.searchbar) # mod list self.modlist = ModList(self, model) self.modlistlayout.addWidget(self.modlist) self.searchbar.textChanged.connect(lambda e: self.modlist.setFilter(e)) # welcome message welcomelayout = QVBoxLayout() welcomelayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) welcomewidget = QWidget() welcomewidget.setLayout(welcomelayout) welcomewidget.dragEnterEvent = self.modlist.dragEnterEvent # type: ignore welcomewidget.dragMoveEvent = self.modlist.dragMoveEvent # type: ignore welcomewidget.dragLeaveEvent = self.modlist.dragLeaveEvent # type: ignore welcomewidget.dropEvent = self.modlist.dropEvent # type: ignore welcomewidget.setAcceptDrops(True) icon = QIcon(str(getRuntimePath('resources/icons/open-folder.ico'))) iconpixmap = icon.pixmap(32, 32) icon = QLabel() icon.setPixmap(iconpixmap) icon.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) icon.setContentsMargins(4, 4, 4, 4) welcomelayout.addWidget(icon) welcome = QLabel('''<p><font> No mod installed yet. Drag a mod into this area to get started! </font></p>''') welcome.setAttribute(Qt.WA_TransparentForMouseEvents) welcome.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) welcomelayout.addWidget(welcome) self.stack.addWidget(welcomewidget) # output log self.output = QTextEdit(self) self.output.setTextInteractionFlags(Qt.NoTextInteraction) self.output.setReadOnly(True) self.output.setContextMenuPolicy(Qt.NoContextMenu) self.output.setPlaceholderText('Program output...') self.splitter.addWidget(self.output) # TODO: enhancement: add a launch game icon # TODO: enhancement: show indicator if scripts have to be merged self.splitter.setStretchFactor(0, 1) self.splitter.setStretchFactor(1, 0) self.mainlayout.addWidget(self.splitter) if len(model): self.stack.setCurrentIndex(0) self.splitter.setSizes([self.splitter.size().height(), 50]) else: self.stack.setCurrentIndex(1) self.splitter.setSizes([self.splitter.size().height(), 0]) model.updateCallbacks.append(self.modelUpdateEvent) def keyPressEvent(self, event: QKeyEvent) -> None: if event.key() == Qt.Key_Escape: self.modlist.setFocus() self.searchbar.setText('') elif event.matches(QKeySequence.Find): self.searchbar.setFocus() elif event.matches(QKeySequence.Paste): self.pasteEvent() super().keyPressEvent(event) def pasteEvent(self) -> None: clipboard = QApplication.clipboard().text().splitlines() if len(clipboard) == 1 and isValidNexusModsUrl(clipboard[0]): self.parentWidget().showDownloadModDialog() else: urls = [ url for url in QApplication.clipboard().text().splitlines() if len(str(url.strip())) ] if all( isValidModDownloadUrl(url) or isValidFileUrl(url) for url in urls): asyncio.create_task(self.modlist.checkInstallFromURLs(urls)) def modelUpdateEvent(self, model: Model) -> None: if len(model) > 0: if self.stack.currentIndex() != 0: self.stack.setCurrentIndex(0) self.repaint() else: if self.stack.currentIndex() != 1: self.stack.setCurrentIndex(1) self.repaint() def unhideOutput(self) -> None: if self.splitter.sizes()[1] < 10: self.splitter.setSizes([self.splitter.size().height(), 50]) def unhideModList(self) -> None: if self.splitter.sizes()[0] < 10: self.splitter.setSizes([50, self.splitter.size().height()]) def log(self, message: Any) -> None: # format log messages to user readable output settings = QSettings() record = message.record message = record['message'] extra = record['extra'] level = record['level'].name.lower() name = str(extra['name'] ) if 'name' in extra and extra['name'] is not None else '' path = str(extra['path'] ) if 'path' in extra and extra['path'] is not None else '' dots = bool( extra['dots'] ) if 'dots' in extra and extra['dots'] is not None else False newline = bool( extra['newline'] ) if 'newline' in extra and extra['newline'] is not None else False output = bool( extra['output'] ) if 'output' in extra and extra['output'] is not None else bool( message) modlist = bool( extra['modlist'] ) if 'modlist' in extra and extra['modlist'] is not None else False if level in ['debug' ] and settings.value('debugOutput', 'False') != 'True': if newline: self.output.append(f'') return n = '<br>' if newline else '' d = '...' if dots else '' if len(name) and len(path): path = f' ({path})' if output: message = html.escape(message, quote=True) if level in ['success', 'error', 'warning']: message = f'<strong>{message}</strong>' if level in ['success']: message = f'<font color="#04c45e">{message}</font>' if level in ['error', 'critical']: message = f'<font color="#ee3b3b">{message}</font>' if level in ['warning']: message = f'<font color="#ff6500">{message}</font>' if level in ['debug', 'trace']: message = f'<font color="#aaa">{message}</font>' path = f'<font color="#aaa">{path}</font>' if path else '' d = f'<font color="#aaa">{d}</font>' if d else '' time = record['time'].astimezone( tz=None).strftime('%Y-%m-%d %H:%M:%S') message = f'<font color="#aaa">{time}</font> {message}' self.output.append( f'{n}{message.strip()}{" " if name or path else ""}{name}{path}{d}' ) else: self.output.append(f'') self.output.verticalScrollBar().setValue( self.output.verticalScrollBar().maximum()) self.output.repaint() if modlist: self.unhideModList() if settings.value('unhideOutput', 'True') == 'True' and output: self.unhideOutput()
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(1280, 720) self.central_widget = QWidget(MainWindow) self.splitter = QSplitter(self.central_widget) self.splitter.setChildrenCollapsible(False) self.splitter.setOpaqueResize(True) self.main_grid_layout = QGridLayout(self.central_widget) self.main_grid_layout.addWidget(self.splitter) self.main_grid_layout.setSizeConstraint(QLayout.SetDefaultConstraint) self.tab_widget = QTabWidget(self.central_widget) self.tab_widget.setMinimumSize(QSize(500, 0)) self._set_up_component_tree_view() self._set_up_silx_view() self.splitter.addWidget(self.tab_widget) self._set_up_3d_view() MainWindow.setCentralWidget(self.central_widget) self._set_up_menus(MainWindow) self.tab_widget.setCurrentIndex(0) QMetaObject.connectSlotsByName(MainWindow) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) def _set_up_3d_view(self): self.sceneWidget = InstrumentView(self.splitter) self.sceneWidget.setMinimumSize(QSize(600, 0)) self.splitter.addWidget(self.sceneWidget) def _set_up_silx_view(self): self.silx_tab = QWidget() self.silx_tab_layout = QGridLayout(self.silx_tab) self.tab_widget.addTab(self.silx_tab, "") def _set_up_component_tree_view(self): self.component_tree_view_tab = ComponentTreeViewTab(parent=self) self.tab_widget.addTab(self.component_tree_view_tab, "") def _set_up_menus(self, MainWindow): self.menu_bar = QMenuBar() self.menu_bar.setGeometry(QRect(0, 0, 1280, 720)) self.file_menu = QMenu(self.menu_bar) MainWindow.setMenuBar(self.menu_bar) self.status_bar = QStatusBar(MainWindow) MainWindow.setStatusBar(self.status_bar) self.open_nexus_file_action = QAction(MainWindow) self.open_json_file_action = QAction(MainWindow) self.open_idf_file_action = QAction(MainWindow) self.export_to_nexus_file_action = QAction(MainWindow) self.export_to_filewriter_JSON_action = QAction(MainWindow) self.export_to_forwarder_JSON_action = QAction(MainWindow) self.file_menu.addAction(self.open_nexus_file_action) self.file_menu.addAction(self.open_json_file_action) self.file_menu.addAction(self.open_idf_file_action) self.file_menu.addAction(self.export_to_nexus_file_action) self.file_menu.addAction(self.export_to_filewriter_JSON_action) self.file_menu.addAction(self.export_to_forwarder_JSON_action) self.menu_bar.addAction(self.file_menu.menuAction()) self._set_up_titles(MainWindow) def _set_up_titles(self, MainWindow): MainWindow.setWindowTitle("NeXus Constructor") self.tab_widget.setTabText( self.tab_widget.indexOf(self.component_tree_view_tab), "Components" ) self.tab_widget.setTabText( self.tab_widget.indexOf(self.silx_tab), "NeXus File Layout" ) self.file_menu.setTitle("File") self.open_nexus_file_action.setText("Open NeXus file") self.open_json_file_action.setText("Open Filewriter JSON file") self.open_idf_file_action.setText("Open Mantid IDF file") self.export_to_nexus_file_action.setText("Export to NeXus file") self.export_to_filewriter_JSON_action.setText("Export to Filewriter JSON") self.export_to_forwarder_JSON_action.setText("Export to Forwarder JSON")