class Weasel(QMainWindow): def __init__(self): """Creates the MDI container.""" super().__init__() self.showFullScreen() self.setWindowTitle("WEASEL") self.centralwidget = QWidget(self) self.setCentralWidget(self.centralwidget) self.centralwidget.setLayout(QVBoxLayout(self.centralwidget)) self.mdiArea = QMdiArea(self.centralwidget) self.mdiArea.tileSubWindows() self.centralwidget.layout().addWidget(self.mdiArea) self.statusBar = QStatusBar() self.centralwidget.layout().addWidget(self.statusBar) self.selectedStudy = '' self.selectedSeries = '' self.selectedImageName = '' self.currentImagePath = '' # XML reader object to process XML configuration file self.objConfigXMLReader = WeaselConfigXMLReader() menuXMLFile = self.objConfigXMLReader.getMenuFile() self.weaselDataFolder = self.objConfigXMLReader.getWeaselDataFolder() # XML reader object to process XML DICOM data file self.objXMLReader = WeaselXMLReader() menus.setupMenus(self, menuXMLFile) menus.buildContextMenu(self, menuXMLFile) #toolBar.setupToolBar(self) commented out to remove Ferret from Weasel self.setStyleSheet(styleSheet.TRISTAN_GREY) logger.info("WEASEL GUI created successfully.") def getMDIAreaDimensions(self): return self.mdiArea.height(), self.mdiArea.width()
class MDIWindow(QMainWindow): count = 0 def __init__(self): super().__init__() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() file = bar.addMenu("File") file.addAction("New") file.addAction("cascade") file.addAction("Tiled") file.triggered[QAction].connect(self.WindowTrig) self.setWindowTitle("MDI Application") def WindowTrig(self, p): if p.text() == "New": MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("Sub Window" + str(MDIWindow.count)) self.mdi.addSubWindow(sub) sub.show() if p.text() == "cascade": self.mdi.cascadeSubWindows() if p.text() == "Tiled": self.mdi.tileSubWindows()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.title = 'event annotation tool' self.left = 50 self.top = 50 self.v_width = 640-200 #1400# self.v_height = 480-200 #1000# self.set_input=0 self.setWindowTitle(self.title) self.mdi = QMdiArea() self.setCentralWidget(self.mdi) self.mdi.tileSubWindows() PATH_FILE = '/home/barbara/Desktop/demo'#'/home/barbara/Desktop/car_data/Normal/Nevsky prospect traffic surveillance video' video_feed=action_time_antation_tool.annotation_openCV(PATH_FILE) v_sub = SubWindow(video_feed) # QMdiSubWindow() v_sub.setWidget(video_feed) v_sub.resize(self.v_width+25, self.v_height+75) v_sub.setMaximumSize(self.v_width+25, self.v_height+75) v_sub.setWindowTitle("Feed " + str(len(self.label_v_list))) self.mdi.addSubWindow(v_sub) # self.mdi self.mdi.tileSubWindows() v_sub.show()
class MainWin(QMainWindow): """主窗口""" count = 0 def __init__(self, parent=None): super().__init__(parent=parent) self.setWindowTitle("QMdiArea QMdiSubWidow 使用") self.mdi_area = QMdiArea() self.setCentralWidget(self.mdi_area) menu_bar = self.menuBar() menu_file = menu_bar.addMenu("文件") action_new = menu_file.addAction("新建") action_new.setData("new") action_save = menu_file.addAction("保存") action_save.setData("save") action_cascade = menu_file.addAction("级联&Cascade") action_cascade.setData("cascade") action_tiled = menu_file.addAction("平铺&Tiled") action_tiled.setData("tiled") menu_file.triggered.connect(self.window_action) def window_action(self, action): print("点击", action.data()) if action.data() == "new": MainWin.count += 1 sub_win = QMdiSubWindow() sub_win.setWidget(QTextEdit()) sub_win.setWindowTitle("子窗口 %d" % MainWin.count) self.mdi_area.addSubWindow(sub_win) if action.data() == "csscade": self.mdi_area.cascadeSubWindows() # 级联模式排列 if action.data() == "tiled": self.mdi_area.tileSubWindows() # 平铺排列
def setupWindows(self): """ Set up QMdiArea parent and subwindows. Add available cameras on local system as items to list widget. """ # Create images directory if it does not already exist path = 'images' if not os.path.exists(path): os.makedirs(path) # Set up list widget that will display identified # cameras on your computer. picture_label = QLabel("Press 'Spacebar' to take pictures.") camera_label = QLabel("Available Cameras") self.camera_list_widget = QListWidget() self.camera_list_widget.setAlternatingRowColors(True) # Add availableCameras to a list to be displayed in # list widget. Use QCameraInfo() to list available cameras. self.cameras = list(QCameraInfo().availableCameras()) for camera in self.cameras: self.list_item = QListWidgetItem() self.list_item.setText(camera.deviceName()) self.camera_list_widget.addItem(self.list_item) # Create button that will allow user to select camera choose_cam_button = QPushButton("Select Camera") choose_cam_button.clicked.connect(self.selectCamera) # Create child widgets and layout for camera controls subwindow controls_gbox = QGroupBox() controls_gbox.setTitle("Camera Controls") v_box = QVBoxLayout() v_box.addWidget(picture_label, alignment=Qt.AlignCenter) v_box.addWidget(camera_label) v_box.addWidget(self.camera_list_widget) v_box.addWidget(choose_cam_button) controls_gbox.setLayout(v_box) controls_sub_window = QMdiSubWindow() controls_sub_window.setWidget(controls_gbox) controls_sub_window.setAttribute(Qt.WA_DeleteOnClose) # Create view finder subwindow self.view_finder_window = QMdiSubWindow() self.view_finder_window.setWindowTitle("Camera View") self.view_finder_window.setAttribute(Qt.WA_DeleteOnClose) # Create QMdiArea widget to manage subwindows mdi_area = QMdiArea() mdi_area.tileSubWindows() mdi_area.addSubWindow(self.view_finder_window) mdi_area.addSubWindow(controls_sub_window) # Set mdi_area widget as the central widget of main window self.setCentralWidget(mdi_area)
class SubWin(QMainWindow): count = 0 def __init__(self): super().__init__() self.initUI() def initUI(self): self.mdi = QMdiArea() self.setCentralWidget(self.mdi) self.toolBar = QToolBar() self.addToolBar(self.toolBar) self.toolBar.addAction("新建") self.toolBar.addAction("级联") self.toolBar.addAction("平铺") self.toolBar.addAction("关闭全部") self.toolBar.addAction("关闭活动窗口") self.toolBar.addAction("测试") self.toolBar.actionTriggered[QAction].connect(self.windowaction) bar = self.menuBar() file = bar.addMenu("File") # 添加子菜单 file.addAction("新建") file.addAction("级联") file.addAction("平铺") file.triggered[QAction].connect(self.windowaction) self.setWindowTitle("MDI Demo") #self.showFullScreen() #全屏显示 self.showMaximized() #窗口最大化 #self.showNormal() #正常显示 # self.setGeometry(QDesktopWidget().screenGeometry()) def windowaction(self, q): type = q.text() print("Triggered : %s" % type) if type == "新建": # 子窗口增加一个 self.count = self.count + 1 # 实例化多文档界面对象 sub = QMdiSubWindow() # 向sub内部添加控件 sub.setWidget(QTextEdit()) sub.setWindowTitle("subWindow %d" % self.count) self.mdi.addSubWindow(sub) sub.show() #sub.hide() elif type == "级联": self.mdi.cascadeSubWindows() elif type == "平铺": self.mdi.tileSubWindows() elif type == "关闭全部": self.mdi.closeAllSubWindows() elif type == "关闭活动窗口": self.mdi.closeActiveSubWindow() elif type == "测试": lst = self.mdi.subWindowList() print(lst) def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft())
class MyMainWindow(QMainWindow): count = 0 def __init__(self, widgets): super().__init__() self.widget_classes = widget_classes self.init_ui() def init_ui(self): self.setWindowTitle('MDI Application') self.setWindowIcon(QIcon('pyicon.png')) self.setGeometry(100, 100, 800, 600) # Create an instance of MDI. self.mdi = QMdiArea() self.setCentralWidget(self.mdi) # Menu bar. menu_bar = self.menuBar() file_menu = menu_bar.addMenu('File') file_menu.addAction('New') file_menu.addAction('Cascade') file_menu.addAction('Tiled') file_menu.triggered[QAction].connect(self.window_triggered) # Exit QAction. exit_action = QAction('Exit', self) exit_action.setShortcut(QKeySequence.Quit) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) # Status bar. status = self.statusBar() status.showMessage('Status bar') def window_triggered(self, p): if p.text() == 'New': MyMainWindow.count += 1 sub = QMdiSubWindow() widget = self.widget_classes[MyMainWindow.count % 2]() sub.setWidget(widget) sub.setWindowTitle('Sub Window {}'.format(MyMainWindow.count)) self.mdi.addSubWindow(sub) sub.show() if p.text() == 'Cascade': self.mdi.cascadeSubWindows() if p.text() == 'Tiled': self.mdi.tileSubWindows()
class MDIWindow(QMainWindow): count = 0 def __init__(self): super().__init__() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() self.current_dir = None file = bar.addMenu("File") file.addAction("New") file.addAction("cascade") file.addAction("Tiled") file.triggered[QAction].connect(self.WindowTrig) self.setWindowTitle("MDI Application") load = bar.addMenu("Load") load.addAction("2D") load.addAction("3D") load.triggered[QAction].connect(self.self.dir_open) def WindowTrig(self, p): if p.text() == "New": MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("Sub Window" + str(MDIWindow.count)) self.mdi.addSubWindow(sub) sub.show() if p.text() == "cascade": self.mdi.cascadeSubWindows() if p.text() == "Tiled": self.mdi.tileSubWindows() def dir_open(self, p): if p.text() == "1D": self.current_dir = dlg.File_dlg.openDirNameDialog(self) files_ls = glob.glob(self.current_dir + '/*.ibw') fls = [f[len(self.current_dir) + 1:] for f in files_ls] print(fls) # self.twoD_list.addItems(fls) # self.threeD_list.addItems(zip) if p.text() == "2D": self.current_dir = dlg.File_dlg.openDirNameDialog(self) zip_ls = glob.glob(self.current_dir + '/*.zip') zp = [f[len(self.current_dir) + 1:] for f in zip_ls] print(zp)
class MainWindow(QMainWindow): def __init__(self, application_name): super(MainWindow, self).__init__() self.title = application_name self.mdi = QMdiArea() self.initialize_ui() def initialize_ui(self): self.setCentralWidget(self.mdi) self.setWindowTitle(self.title) self.showMaximized() def add_tile(self, module): self.mdi.addSubWindow(module) def tile(self): self.mdi.tileSubWindows()
def tile_subwindows(mdi: QtWidgets.QMdiArea, except_type: Type = type(None)): """Tile all subwindows of an MDI area, except for objects of 'except_type'.""" # Hide windows to keep them from being tiled: windows_hidden = list() # type: List[QtWidgets.QMdiSubWindow] for window in mdi.subWindowList(): if type(window) == except_type: windows_hidden.append(window) window.hide() mdi.tileSubWindows() # Show hidden windows again: for window in windows_hidden: window.show() # Move all tiled windows above the excluded ones: for window in mdi.subWindowList(): if window not in windows_hidden: mdi.setActiveSubWindow(window)
class MdiQWebViewWindows(BaseQWebViewWindows): def __init__(self): super().__init__() self._mdi_area = None def add_webview(self, webview): if self._mdi_area is None: self._mdi_area = QMdiArea() self._mdi_area.resize(1024, 768) # self._mdi_area.setWindowTitle() self._mdi_area.show() subwindow = self._mdi_area.addSubWindow(webview) webview.titleChanged.connect(subwindow.setWindowTitle) subwindow.show() self._mdi_area.tileSubWindows() def remove_webview(self, webview): subwindow = webview.parentWidget() webview.close() self._mdi_area.removeSubWindow(subwindow) self._mdi_area.tileSubWindows()
def init_window(self): """ Set up QMdiArea parent and sub_windows. Add available cameras on local system as items to list widget. """ # Load image in sub_window_view self.load_image("images/chameleon.png") self.sub_window_view.setWidget(self.label_image) self.sub_window_models.setWidget(self.gbox_model) # Create QMdiArea widget to manage sub_windows mdi_area = QMdiArea() mdi_area.tileSubWindows() # mdi_area.addSubWindow(self.sub_window_camera) mdi_area.addSubWindow(self.sub_window_models) mdi_area.addSubWindow(self.sub_window_view) # Set up dock widget self.dock_camera.setWindowTitle("Camera Control") self.dock_camera.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.dock_camera.setWidget(self.camera_contents) self.dock_filter.setWindowTitle("Camera Filter") self.dock_filter.setWidget(self.filter_contents) self.dock_color.setWindowTitle("Camera Color") self.dock_color.setWidget(self.color_contents) self.dock_color.setVisible(False) # Set initial location of dock widget in main window self.addDockWidget(Qt.RightDockWidgetArea, self.dock_camera) self.addDockWidget(Qt.RightDockWidgetArea, self.dock_filter) self.addDockWidget(Qt.RightDockWidgetArea, self.dock_color) # Set mdi_area widget as the central widget of main window self.setCentralWidget(mdi_area)
class Ventana_Principal(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.num_ventanas = 0 self.setWindowTitle('Ejemplo de aplicacio MDI para getphones') self.setGeometry(0, 0, 800, 600) self.centraEnPantalla() self.mdi_area = QMdiArea() self.setCentralWidget(self.mdi_area) #personPhoto_FilePath is a string #personData is a tuple: #(name, surname, ubication, phone ext) #personExtension is a string def addNewFichaPersona(self, personPhoto_FilePath, personData, personExtension): fichaPersona = Ficha_Persona() fichaPersona.setPersonPhoto(personPhoto_FilePath) fichaPersona.setPersonData(personData) fichaPersona.setPersonPhonenumber(personExtension) subventana = QMdiSubWindow() subventana.setWidget(fichaPersona) subventana.setWindowTitle('Subventana nº {}'.format(self.num_ventanas)) self.mdi_area.addSubWindow(subventana) subventana.show() self.mdi_area.tileSubWindows() def centraEnPantalla(self): resolucion_pantalla = QDesktopWidget().screenGeometry() self.move( int(resolucion_pantalla.width() / 2 - self.frameSize().width() / 2), int(resolucion_pantalla.height() / 2 - self.frameSize().height() / 2))
class MDIWindow(QMainWindow): count = 0 def __init__(self): super().__init__() uic.loadUi("mainwindow.ui", self) self.mdi = QMdiArea() self.setCentralWidget(self.mdi) self.menu_File.triggered[QAction].connect(self.WindowTrig) #self.setWindowTitle("MDI Application") def WindowTrig(self, p): if p.text() == "&New": MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("Sub Window" + str(MDIWindow.count)) self.mdi.addSubWindow(sub) sub.show() if p.text() == "&Cascade": self.mdi.cascadeSubWindows() if p.text() == "&Tile": self.mdi.tileSubWindows()
class GUIWindow(QMainWindow): def __init__(self, app, pipeline=Pipeline()): super().__init__() self._app = app self._logger = logging.getLogger(self.__class__.__name__) self._is_initialized = False self.init_basic(pipeline) self.init_ui() self.init_controls() self.setWindowTitle("Cognigraph") self.setWindowIcon(QIcon(':/cognigraph_icon.png')) def init_basic(self, pipeline): self._pipeline = pipeline # type: Pipeline self._updater = AsyncUpdater(self._app, pipeline) self._pipeline._signal_sender.long_operation_started.connect( self._show_progress_dialog) self._pipeline._signal_sender.long_operation_finished.connect( self._hide_progress_dialog) self._pipeline._signal_sender.request_message.connect( self._show_message) self._pipeline._signal_sender.node_widget_added.connect( self._on_node_widget_added) self._controls = Controls(pipeline=self._pipeline) self._controls.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self._controls.tree_widget.node_removed.connect(self._on_node_removed) if hasattr(self, "central_widget"): for w in self.central_widget.subWindowList(): self.central_widget.removeSubWindow(w) def init_controls(self): self.controls_dock.setWidget(self._controls) self.run_toggle_action.triggered.disconnect() self.run_toggle_action.triggered.connect(self._updater.toggle) self._updater._sender.run_toggled.connect(self._on_run_button_toggled) self._updater._sender.errored.connect(self._show_message) self.is_initialized = False def init_ui(self): self.central_widget = QMdiArea() self.setCentralWidget(self.central_widget) # -------- controls widget -------- # self.controls_dock = QDockWidget("Processing pipeline setup", self) self.controls_dock.setObjectName("Controls") self.controls_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.controls_dock.visibilityChanged.connect( self._update_pipeline_tree_widget_action_text) self.addDockWidget(Qt.LeftDockWidgetArea, self.controls_dock) # self._controls.setMinimumWidth(800) # --------------------------------- # file_menu = self.menuBar().addMenu("&File") # file menu load_pipeline_action = self._createAction("&Load pipeline", self._load_pipeline) save_pipeline_action = self._createAction("&Save pipeline", self._save_pipeline) file_menu.addAction(load_pipeline_action) file_menu.addAction(save_pipeline_action) # -------- view menu & toolbar -------- # tile_windows_action = self._createAction( "&Tile windows", self.central_widget.tileSubWindows) view_menu = self.menuBar().addMenu("&View") view_menu.addAction(tile_windows_action) view_toolbar = self.addToolBar("View") view_toolbar.addAction(tile_windows_action) # ------------------------------------- # edit_menu = self.menuBar().addMenu("&Edit") self._toggle_pipeline_tree_widget_action = self._createAction( "&Hide pipeline settings", self._toggle_pipeline_tree_widget) edit_menu.addAction(self._toggle_pipeline_tree_widget_action) edit_toolbar = self.addToolBar("Edit") edit_toolbar.setObjectName("edit_toolbar") edit_toolbar.addAction(self._toggle_pipeline_tree_widget_action) # -------- run menu & toolbar -------- # self.run_toggle_action = self._createAction( "&Start", self._on_run_button_toggled) run_menu = self.menuBar().addMenu("&Run") self.initialize_pipeline = self._createAction("&Initialize pipeline", self.initialize) run_menu.addAction(self.run_toggle_action) run_menu.addAction(self.initialize_pipeline) run_toolbar = self.addToolBar("Run") run_toolbar.setObjectName("run_toolbar") run_toolbar.addAction(self.run_toggle_action) run_toolbar.addAction(self.initialize_pipeline) # ------------------------------------ # def _toggle_pipeline_tree_widget(self): if self.controls_dock.isHidden(): self.controls_dock.show() else: self.controls_dock.hide() def _update_pipeline_tree_widget_action_text(self, is_visible): if is_visible: self._toggle_pipeline_tree_widget_action.setText( "&Hide pipelne settings") else: self._toggle_pipeline_tree_widget_action.setText( "&Show pipelne settings") def _load_pipeline(self): file_dialog = QFileDialog(caption="Select pipeline file", directory=PIPELINES_DIR) ext_filter = "JSON file (*.json);; All files (*.*)" pipeline_path = file_dialog.getOpenFileName(filter=ext_filter)[0] if pipeline_path: self._logger.info("Loading pipeline configuration from %s" % pipeline_path) if not self._updater.is_paused: self.run_toggle_action.trigger() with open(pipeline_path, "r") as db: try: params_dict = json.load(db) except json.decoder.JSONDecodeError as e: self._show_message("Bad pipeline configuration file", detailed_text=str(e)) pipeline = self.assemble_pipeline(params_dict, "Pipeline") self.init_basic(pipeline) self.init_controls() # self.resize(self.sizeHint()) else: return def _save_pipeline(self): self._logger.info("Saving pipeline") file_dialog = QFileDialog(caption="Select pipeline file", directory=PIPELINES_DIR) ext_filter = "JSON file (*.json);; All files (*.*)" pipeline_path = file_dialog.getSaveFileName(filter=ext_filter)[0] if pipeline_path: self._logger.info("Saving pipeline configuration to %s" % pipeline_path) try: self._pipeline.save_pipeline(pipeline_path) except Exception as exc: self._show_message( "Cant`t save pipeline configuration to %s" % pipeline_path, detailed_text=str(exc), ) self._logger.exception(exc) def assemble_pipeline(self, d, class_name): node_class = getattr(nodes, class_name) node = node_class(**d["init_args"]) for child_class_name in d["children"]: child = self.assemble_pipeline(d["children"][child_class_name], child_class_name) node.add_child(child) return node def initialize(self): is_paused = self._updater.is_paused if not is_paused: self._updater.stop() self._logger.info("Initializing all nodes") async_initer = AsyncPipelineInitializer(pipeline=self._pipeline, parent=self) async_initer.no_blocking_execution() for node in self._pipeline.all_nodes: if hasattr(node, "widget"): if not node.widget.parent(): # widget not added to QMdiArea self._add_subwindow(node.widget, repr(node)) self.central_widget.tileSubWindows() self.run_toggle_action.setDisabled(False) if not is_paused: self._updater.start() def _finish_initialization(self): self.progress_dialog.hide() self.progress_dialog.deleteLater() for node in self._pipeline.all_nodes: if hasattr(node, "widget"): self._add_subwindow(node.widget, repr(node)) self.central_widget.tileSubWindows() def _add_subwindow(self, widget, title): sw = _HookedSubWindow(self.central_widget) sw.setWidget(widget) sw.setWindowTitle(title) widget.show() def _show_progress_dialog(self, text): # -------- setup progress dialog -------- # self.progress_dialog = QProgressDialog(self) self.progress_dialog.setLabelText(text) self.progress_dialog.setCancelButtonText(None) self.progress_dialog.setRange(0, 0) self.progress_dialog.show() def _hide_progress_dialog(self): self.progress_dialog.hide() self.progress_dialog.deleteLater() def _on_subwindow_close(self, close_event): pass def _on_node_widget_added(self, widget, widget_name): self._add_subwindow(widget, widget_name) self.central_widget.tileSubWindows() def _on_node_removed(self, tree_item): if hasattr(tree_item.node, "widget"): try: self.central_widget.removeSubWindow( tree_item.node.widget.parent()) except AttributeError: pass except Exception as exc: self._show_message( "Can`t remove widget for %s" % tree_item.node, detailed_text=str(exc), ) self._logger.exception(exc) def _show_message(self, text, detailed_text=None, level="error"): if level == "error": icon = QMessageBox.Critical elif level == "warning": icon = QMessageBox.Warning elif level == "info": icon = QMessageBox.Information msg = QMessageBox(self) msg.setIcon(icon) msg.setText(text) msg.setDetailedText(detailed_text) msg.show() def _createAction( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, ): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: action.triggered.connect(slot) if checkable: action.setCheckable(True) return action def moveEvent(self, event): return super(GUIWindow, self).moveEvent(event) def _on_run_button_toggled(self, is_paused=True): if is_paused: self.run_toggle_action.setText("Start") else: self.run_toggle_action.setText("Pause") @property def is_initialized(self): return self._is_initialized @is_initialized.setter def is_initialized(self, value): if value: self.run_toggle_action.setDisabled(False) else: self.run_toggle_action.setDisabled(True) self._is_initialized = value @property def _node_widgets(self) -> List[QWidget]: node_widgets = list() for node in self._pipeline.all_nodes: try: node_widgets.append(node.widget) except AttributeError: pass return node_widgets
class MainWindow(QMainWindow): count = 0 linecount = 0 maxLine = 100 def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.item0 = BytesIO() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() file = bar.addMenu("File") file.addAction("New") file.addAction("cascade") file.addAction("Tiled") file.triggered[QAction].connect(self.windowaction) self.setWindowTitle("Camphor") MainWindow.count += 1 self.output_window = OutputWindow() self.mdi.addSubWindow(self.output_window) MainWindow.count += 1 self.script_window = ScriptWindow() self.mdi.addSubWindow(self.script_window) MainWindow.count += 1 self.viewer_window = ViewerWindow() self.mdi.addSubWindow(self.viewer_window) #MainWindow.count += 1 #self.spread_sheet = SpreadSheetWidget() #self.mdi.addSubWindow(self.spread_sheet) headers = ["000", "001", "002"] tableData0 = [['abc', 100, 200], ['fff', 130, 260], ['jjj', 190, 300], ['ppp', 700, 500], ['yyy', 800, 900]] #model = MyTableModel(tableData0, headers) table_df = pd.DataFrame(tableData0, columns=headers) MainWindow.count += 1 self.dataframe_viewer = DataFrameViewer(table_df) self.mdi.addSubWindow(self.dataframe_viewer) # QProcess object for external app self.process = QProcess(self) self.process.readyReadStandardOutput.connect( lambda: self.dataReady("std")) self.process.readyReadStandardError.connect( lambda: self.dataReady("error")) self.process.finished.connect(lambda: self.update_svg()) #Connect Slots self.script_window.button_exec.clicked.connect( lambda: self.run_script()) self.script_window.button_read.clicked.connect(lambda: self.read_svg()) #self.viewer_window.button_save.clicked.connect(lambda: self.save_svg()) #Assign Shortcuts self.shortcut_update = QShortcut(QKeySequence("Ctrl+R"), self) self.shortcut_update.activated.connect(lambda: self.run_script()) self.shortcut_update = QShortcut(QKeySequence("Ctrl+O"), self) self.shortcut_update.activated.connect(lambda: self.read_svg()) self.shortcut_update = QShortcut(QKeySequence("Ctrl+S"), self) self.shortcut_update.activated.connect(lambda: self.save_svg()) def windowaction(self, q): if q.text() == "cascade": self.mdi.cascadeSubWindows() if q.text() == "Tiled": self.mdi.tileSubWindows() @pyqtSlot() def read_svg(self): print("READ") self.script_window.fname = QFileDialog.getOpenFileName( self, 'Open file', '/home') if self.script_window.fname[0]: # import script metadata svg_tree = ET.parse(self.script_window.fname[0]) root = svg_tree.getroot() for metadata in root.findall( "{http://www.w3.org/2000/svg}metadata"): for metadatum in metadata: if metadatum.tag == "{https://korintje.com}script": self.script_window.edit.setPlainText(metadatum.text) break self.run_script() # Parse original .svg print(self.script_window.fname[0]) original_svg_tree = ET.parse(self.script_window.fname[0]) original_root = original_svg_tree.getroot() for og in original_root.findall("{http://www.w3.org/2000/svg}g"): if "{http://www.inkscape.org/namespaces/inkscape}groupmode" in og.attrib and og.attrib[ "{http://www.inkscape.org/namespaces/inkscape}groupmode"] == "layer": if "{http://www.inkscape.org/namespaces/inkscape}label" in og.attrib and og.attrib[ "{http://www.inkscape.org/namespaces/inkscape}label"] == "Layer_mpl": original_root.remove(og) register_all_namespaces(self.script_window.fname[0]) original_svg_tree.write("bkg_temp.svg", encoding="UTF-8", xml_declaration=True) self.update_bkg("bkg_temp.svg") @pyqtSlot() def run_script(self): self.output_window.stdout.clear() script = self.script_window.edit.toPlainText() self.process.start('python', ['-c', script]) self.viewer_window.button_save.clicked.connect(lambda: self.save_svg()) @pyqtSlot() def update_svg(self): self.viewer_window.view.load("temp.svg") @pyqtSlot() def dataReady(self, err_or_std): cursor = self.output_window.stdout.textCursor() cursor.movePosition(cursor.End) if err_or_std == "std": message = self.process.readAllStandardOutput().data().decode( "utf8") self.output_window.stdout.setTextColor(QColor(48, 255, 48)) else: #if err_or_std == "error": message = self.process.readAllStandardError().data().decode("utf8") self.output_window.stdout.setTextColor(QColor(255, 48, 48)) self.output_window.stdout.insertPlainText(message) cursor.insertBlock() #self.output_window.setTopLevelWindow() @pyqtSlot() def update_bkg(self, filename): self.viewer_window.bkg.load(filename) @pyqtSlot() def save_svg(self): self.run_script() # Parse original .svg if self.script_window.fname[0]: print(self.script_window.fname[0]) original_svg_tree = ET.parse(self.script_window.fname[0]) original_root = original_svg_tree.getroot() for og in original_root.findall("{http://www.w3.org/2000/svg}g"): if "{http://www.inkscape.org/namespaces/inkscape}groupmode" in og.attrib and og.attrib[ "{http://www.inkscape.org/namespaces/inkscape}groupmode"] == "layer": if "{http://www.inkscape.org/namespaces/inkscape}label" in og.attrib and og.attrib[ "{http://www.inkscape.org/namespaces/inkscape}label"] == "Layer_mpl": original_root.remove(og) # Insert modified .svg into the original .svg modified_svg_tree = ET.parse("temp.svg") modified_root = modified_svg_tree.getroot() for mg in modified_root.findall("{http://www.w3.org/2000/svg}g"): if "id" in mg.attrib and mg.attrib["id"] == "figure_1": mg.set("inkscape:groupmode", "layer") mg.set("inkscape:label", "Layer_mpl") original_root.append(mg) print("done") # Update the script in the metadata for metadata in original_root.findall( "{http://www.w3.org/2000/svg}metadata"): for metadatum in metadata: print(metadatum.tag) if metadatum.tag == "{https://korintje.com}script": metadatum.text = self.script_window.edit.toPlainText() print(metadatum.text) break register_all_namespaces(self.script_window.fname[0]) original_svg_tree.write("mod_test2.svg", encoding="UTF-8", xml_declaration=True)
class GUIWindow(QMainWindow): def __init__(self, pipeline=Pipeline()): super().__init__() self._pipeline = pipeline # type: Pipeline self._controls = Controls(pipeline=self._pipeline) self._controls_widget = self._controls.widget self._controls_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) # Start button # Resize screen self.resize(QSize( QDesktopWidget().availableGeometry().width() * 0.9, QDesktopWidget().availableGeometry().height() * 0.9)) def init_ui(self): self.central_widget = QMdiArea() self.setCentralWidget(self.central_widget) # -------- controls widget -------- # self._controls.initialize() controls_dock = QDockWidget('Controls', self) controls_dock.setObjectName('Controls') controls_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) controls_dock.setWidget(self._controls_widget) self.addDockWidget(Qt.LeftDockWidgetArea, controls_dock) self._controls_widget.setMinimumWidth(800) # --------------------------------- # self.menuBar().addMenu('&File') # file menu # -------- view menu & toolbar -------- # tile_windows_action = self.createAction( '&Tile windows', self.central_widget.tileSubWindows) view_menu = self.menuBar().addMenu('&View') view_menu.addAction(tile_windows_action) view_toolbar = self.addToolBar('View') view_toolbar.addAction(tile_windows_action) # ------------------------------------- # # -------- run menu & toolbar -------- # self.run_toggle_action = self.createAction( '&Start', self._on_run_button_toggled) run_menu = self.menuBar().addMenu('&Run') run_menu.addAction(self.run_toggle_action) run_toolbar = self.addToolBar('Run') run_toolbar.setObjectName('run_toolbar') run_toolbar.addAction(self.run_toggle_action) # ------------------------------------ # def initialize(self): logger.debug('Initializing all nodes') async_initer = AsyncPipelineInitializer(pipeline=self._pipeline, parent=self) async_initer.no_blocking_execution() for node_widget in self._node_widgets: if node_widget: node_widget.setMinimumWidth(600) self.central_widget.addSubWindow(node_widget) node_widget.show() else: raise ValueError('Node widget is not defined') self.central_widget.tileSubWindows() def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: action.triggered.connect(slot) if checkable: action.setCheckable(True) return action def moveEvent(self, event): return super(GUIWindow, self).moveEvent(event) def _on_run_button_toggled(self): if self.run_toggle_action.text() == "Pause": self.run_toggle_action.setText("Start") else: self.run_toggle_action.setText("Pause") @property def _node_widgets(self) -> List[QWidget]: node_widgets = list() for node in self._pipeline.all_nodes: try: node_widgets.append(node.widget) except AttributeError: pass return node_widgets
class MainWindow(QMainWindow): count = 0 def __init__(self, parent=None, qtapp=None): super().__init__(parent) self.qtapp = qtapp self.mdi = QMdiArea() self.setCentralWidget(self.mdi) self.app_manager = AppManager(None, gui_parent=self) self.mdi.subWindowActivated.connect(self.app_manager. on_view_activated) self.is_dark = self.light_or_dark(False) self.left = 100 self.top = 50 self.width = 1800 self.height = 1200 self.setGeometry(self.left, self.top, self.width, self.height) bar = self.menuBar() file_menu = bar.addMenu("File") file_menu.addAction("New") file_menu.addAction("Open...") file_menu.addSeparator() file_menu.addAction("Save") file_menu.addAction("Save As...") file_menu.addAction("Close") file_menu.triggered[QAction].connect(self.file_action) view_menu = bar.addMenu("Data View") view_menu.addAction("Spec Sheet") view_menu.addAction("Optical Layout") view_menu.addAction("Lens Table") view_menu.addAction("Element Table") view_menu.addAction("Glass Map") # view_menu.addAction("Lens View") view_menu.triggered[QAction].connect(self.view_action) parax_menu = bar.addMenu("Paraxial Model") parax_menu.addAction("Paraxial Model") parax_menu.addAction("y-ybar View") parax_menu.addAction("nu-nubar View") parax_menu.addAction("yui Ray Table") parax_menu.addAction("3rd Order Aberrations") parax_menu.triggered[QAction].connect(self.view_action) analysis_menu = bar.addMenu("Analysis") analysis_menu.addAction("Ray Table") analysis_menu.addAction("Ray Fans") analysis_menu.addAction("OPD Fans") analysis_menu.addAction("Spot Diagram") analysis_menu.addAction("Wavefront Map") analysis_menu.addAction("Astigmatism Curves") analysis_menu.triggered[QAction].connect(self.view_action) tools_menu = bar.addMenu("Tools") tools_menu.addAction("Paraxial Vignetting") tools_menu.triggered[QAction].connect(self.view_action) wnd_menu = bar.addMenu("Window") wnd_menu.addAction("Cascade") wnd_menu.addAction("Tiled") wnd_menu.addSeparator() wnd_menu.addAction("Light UI") wnd_menu.addAction("Dark UI") wnd_menu.addSeparator() dock.create_dock_windows(self) for pi in dock.panels.values(): wnd_menu.addAction(pi.menu_action) wnd_menu.triggered[QAction].connect(self.window_action) self.setWindowTitle("Ray Optics") self.show() path = Path(rayoptics.__file__).parent self.cur_dir = path / "models" if False: # create new model self.new_model() else: # restore a default model # self.cur_dir = path / "codev/tests" # self.open_file(path / "codev/tests/asp46.seq") # self.open_file(path / "codev/tests/dar_test.seq") # self.open_file(path / "codev/tests/paraboloid.seq") # self.open_file(path / "codev/tests/paraboloid_f8.seq") # self.open_file(path / "codev/tests/schmidt.seq") # self.open_file(path / "codev/tests/questar35.seq") # self.open_file(path / "codev/tests/rc_f16.seq") # self.open_file(path / "codev/tests/ag_dblgauss.seq") # self.open_file(path / "codev/tests/threemir.seq") # self.open_file(path / "codev/tests/folded_lenses.seq") # self.open_file(path / "codev/tests/lens_reflection_test.seq") # self.open_file(path / "codev/tests/dec_tilt_test.seq") # self.open_file(path / "codev/tests/landscape_lens.seq") # self.open_file(path / "codev/tests/mangin.seq") # self.open_file(path / "codev/tests/CODV_32327.seq") # self.open_file(path / "codev/tests/dar_test.seq") # self.open_file(path / "optical/tests/cell_phone_camera.roa") # self.open_file(path / "optical/tests/singlet_f3.roa") # self.cur_dir = path / "models" # self.open_file(path / "models/Cassegrain.roa") # self.open_file(path / "models/collimator.roa") # self.open_file(path / "models/Dall-Kirkham.roa") # self.open_file(path / "models/petzval.roa") # self.open_file(path / "models/Ritchey_Chretien.roa") # self.open_file(path / "models/Sasian Triplet.roa") # self.open_file(path / "models/singlet_f5.roa") # self.open_file(path / "models/thinlens.roa") # self.open_file(path / "models/telephoto.roa") # self.open_file(path / "models/thin_triplet.roa") # self.open_file(path / "models/TwoMirror.roa") # self.open_file(path / "models/TwoSphericalMirror.roa") self.cur_dir = path / "zemax/tests" # self.open_file(path / "zemax/tests/US08427765-1.ZMX") # self.open_file(path / "zemax/tests/US00583336-2-scaled.zmx") # self.open_file(path / "zemax/tests/HoO-V2C18Ex03.zmx") # self.open_file(path / "zemax/tests/HoO-V2C18Ex27.zmx") # self.open_file(path / "zemax/tests/HoO-V2C18Ex46.zmx") # self.open_file(path / "zemax/tests/HoO-V2C18Ex66.zmx") # self.open_file(path / "zemax/tests/US05831776-1.zmx") self.open_file(path / "zemax/tests/354710-C-Zemax(ZMX).zmx") # self.cur_dir = path / "zemax/models/telescopes" # self.open_file(path / "zemax/models/telescopes/Figure4.zmx") # self.open_file(path / "zemax/models/telescopes/HoO-V2C18Ex11.zmx") # self.cur_dir = path / "zemax/models/PhotoPrime" # self.open_file(path / "zemax/models/PhotoPrime/US05321554-4.ZMX") # self.open_file(path / "zemax/models/PhotoPrime/US06476982-1.ZMX") # self.open_file(path / "zemax/models/PhotoPrime/US07190532-1.ZMX") # self.open_file(path / "zemax/models/PhotoPrime/US04331391-1.zmx") # self.open_file(path / "zemax/models/PhotoPrime/US05331467-1.zmx") def add_subwindow(self, widget, model_info): sub_wind = self.mdi.addSubWindow(widget) self.app_manager.add_view(sub_wind, widget, model_info) MainWindow.count += 1 return sub_wind def delete_subwindow(self, sub_wind): self.app_manager.delete_view(sub_wind) self.mdi.removeSubWindow(sub_wind) MainWindow.count -= 1 def add_ipython_subwindow(self): try: create_ipython_console(self, 'iPython console', 800, 600) except MultipleInstanceError: logging.debug("Unable to open iPython console. " "MultipleInstanceError") except Exception as inst: print(type(inst)) # the exception instance print(inst.args) # arguments stored in .args print(inst) # __str__ allows args to be printed directly, pass # but may be overridden in exception subclasses def initial_window_offset(self): offset_x = 50 offset_y = 25 orig_x = (MainWindow.count - 1)*offset_x orig_y = (MainWindow.count - 1)*offset_y return orig_x, orig_y def file_action(self, q): if q.text() == "New": self.new_model() if q.text() == "Open...": options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", str(self.cur_dir), "All files (*.seq *.zmx *.roa);;" "CODE V files (*.seq);;" "Ray-Optics files (*.roa);;" "Zemax files (*.zmx)", options=options) if fileName: logging.debug("open file: %s", fileName) filename = Path(fileName) self.cur_dir = filename.parent self.open_file(filename) if q.text() == "Save As...": options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", "", "Ray-Optics Files (*.roa);;All Files (*)", options=options) if fileName: logging.debug("save file: %s", fileName) self.save_file(fileName) if q.text() == "Close": self.close_model() def new_model(self): iid = cmds.create_new_ideal_imager(gui_parent=self, conjugate_type='infinite') self.add_ipython_subwindow() self.refresh_app_ui() def open_file(self, file_name, **kwargs): self.cur_filename = file_name opt_model = cmds.open_model(file_name, **kwargs) self.app_manager.set_model(opt_model) self.is_changed = True self.create_lens_table() cmds.create_live_layout_view(self.app_manager.model, gui_parent=self) self.add_ipython_subwindow() self.refresh_app_ui() def save_file(self, file_name): self.app_manager.model.save_model(file_name) self.cur_filename = file_name self.is_changed = False def close_model(self): """ NOTE: this does not check to save a modified model """ self.app_manager.close_model(self.delete_subwindow) def view_action(self, q): opt_model = self.app_manager.model if q.text() == "Spec Sheet": cmds.create_new_ideal_imager(opt_model=opt_model, gui_parent=self) if q.text() == "Optical Layout": cmds.create_live_layout_view(opt_model, gui_parent=self) if q.text() == "Lens Table": self.create_lens_table() if q.text() == "Element Table": model = cmds.create_element_table_model(opt_model) self.create_table_view(model, "Element Table") if q.text() == "Glass Map": cmds.create_glass_map_view(opt_model, gui_parent=self) if q.text() == "Ray Fans": cmds.create_ray_fan_view(opt_model, "Ray", gui_parent=self) if q.text() == "OPD Fans": cmds.create_ray_fan_view(opt_model, "OPD", gui_parent=self) if q.text() == "Spot Diagram": cmds.create_ray_grid_view(opt_model, gui_parent=self) if q.text() == "Wavefront Map": cmds.create_wavefront_view(opt_model, gui_parent=self) if q.text() == "Astigmatism Curves": cmds.create_field_curves(opt_model, gui_parent=self) if q.text() == "3rd Order Aberrations": cmds.create_3rd_order_bar_chart(opt_model, gui_parent=self) if q.text() == "y-ybar View": cmds.create_paraxial_design_view_v2(opt_model, 'ht', gui_parent=self) if q.text() == "nu-nubar View": cmds.create_paraxial_design_view_v2(opt_model, 'slp', gui_parent=self) if q.text() == "yui Ray Table": model = cmds.create_parax_table_model(opt_model) self.create_table_view(model, "Paraxial Ray Table") if q.text() == "Paraxial Model": model = cmds.create_parax_model_table(opt_model) self.create_table_view(model, "Paraxial Model") if q.text() == "Ray Table": self.create_ray_table(opt_model) if q.text() == "Paraxial Vignetting": trace.apply_paraxial_vignetting(opt_model) self.refresh_gui() def window_action(self, q): if q.text() == "Cascade": self.mdi.cascadeSubWindows() if q.text() == "Tiled": self.mdi.tileSubWindows() if q.text() == "Light UI": self.is_dark = self.light_or_dark(False) self.app_manager.sync_light_or_dark(self.is_dark) if q.text() == "Dark UI": self.is_dark = self.light_or_dark(True) self.app_manager.sync_light_or_dark(self.is_dark) def light_or_dark(self, is_dark): """ set the UI to a light or dark scheme. Qt doesn't seem to support controlling the MdiArea's background from a style sheet. Set the widget directly and save the original color to reset defaults. """ if not hasattr(self, 'mdi_background'): self.mdi_background = self.mdi.background() if is_dark: self.qtapp.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) rgb = DarkPalette.color_palette() self.mdi.setBackground(QColor(rgb['COLOR_BACKGROUND_NORMAL'])) else: self.qtapp.setStyleSheet('') self.mdi.setBackground(self.mdi_background) return is_dark def create_lens_table(self): seq_model = self.app_manager.model.seq_model def set_stop_surface(stop_surface): seq_model.stop_surface = stop_surface self.refresh_gui() def handle_context_menu(point): try: vheader = view.verticalHeader() row = vheader.logicalIndexAt(point.y()) except NameError: pass else: # show menu about the row menu = QMenu(self) if row != seq_model.stop_surface: menu.addAction('Set Stop Surface', lambda: set_stop_surface(row)) if seq_model.stop_surface is not None: menu.addAction('Float Stop Surface', lambda: set_stop_surface(None)) menu.popup(vheader.mapToGlobal(point)) model = cmds.create_lens_table_model(seq_model) view = self.create_table_view(model, "Surface Data Table") vheader = view.verticalHeader() vheader.setContextMenuPolicy(qt.CustomContextMenu) vheader.customContextMenuRequested.connect(handle_context_menu) def create_ray_table(self, opt_model): osp = opt_model.optical_spec pupil = [0., 1.] fi = 0 wl = osp.spectral_region.reference_wvl fld, wvl, foc = osp.lookup_fld_wvl_focus(fi, wl) ray, ray_op, wvl = trace.trace_base(opt_model, pupil, fld, wvl) # ray, ray_op, wvl, opd = trace.trace_with_opd(opt_model, pupil, # fld, wvl, foc) # cr = trace.RayPkg(ray, ray_op, wvl) # s, t = trace.trace_coddington_fan(opt_model, cr, foc) ray = [RaySeg(*rs) for rs in ray] model = cmds.create_ray_table_model(opt_model, ray) self.create_table_view(model, "Ray Table") def create_table_view(self, table_model, table_title, close_callback=None): # construct the top level widget widget = QWidget() # construct the top level layout layout = QVBoxLayout(widget) table_view = TableView(table_model) table_view.setAlternatingRowColors(True) # Add table to box layout layout.addWidget(table_view) # set the layout on the widget widget.setLayout(layout) sub = self.add_subwindow(widget, ModelInfo(self.app_manager.model, cmds.update_table_view, (table_view,))) sub.setWindowTitle(table_title) sub.installEventFilter(self) table_view.setMinimumWidth(table_view.horizontalHeader().length() + table_view.horizontalHeader().height()) # The following line should work but returns 0 # table_view.verticalHeader().width()) view_width = table_view.width() view_ht = table_view.height() orig_x, orig_y = self.initial_window_offset() sub.setGeometry(orig_x, orig_y, view_width, view_ht) # table data updated successfully table_model.update.connect(self.on_data_changed) sub.show() return table_view def eventFilter(self, obj, event): """Used by table_view in response to installEventFilter.""" if (event.type() == QEvent.Close): print('close event received:', obj) return False def refresh_gui(self, **kwargs): self.app_manager.refresh_gui(**kwargs) def refresh_app_ui(self): dock.update_dock_windows(self) def handle_ideal_imager_command(self, iid, command, specsheet): ''' link Ideal Imager Dialog buttons to model actions iid: ideal imager dialog command: text field with the action - same as button label specsheet: the input specsheet used to drive the actions ''' if command == 'Apply': opt_model = self.app_manager.model opt_model.set_from_specsheet(specsheet) self.refresh_gui() elif command == 'Close': for view, info in self.app_manager.view_dict.items(): if iid == info[0]: self.delete_subwindow(view) view.close() break elif command == 'Update': opt_model = self.app_manager.model specsheet = opt_model.specsheet firstorder.specsheet_from_parax_data(opt_model, specsheet) iid.specsheet_dict[specsheet.conjugate_type] = specsheet iid.update_values() elif command == 'New': opt_model = cmds.create_new_optical_model_from_specsheet(specsheet) self.app_manager.set_model(opt_model) for view, info in self.app_manager.view_dict.items(): if iid == info[0]: w = iid mi = info[1] args = (iid, opt_model) new_mi = ModelInfo(model=opt_model, fct=mi.fct, args=args, kwargs=mi.kwargs) self.app_manager.view_dict[view] = w, new_mi self.refresh_gui() self.create_lens_table() cmds.create_live_layout_view(opt_model, gui_parent=self) cmds.create_paraxial_design_view_v2(opt_model, 'ht', gui_parent=self) self.refresh_gui() @pyqtSlot(object, int) def on_data_changed(self, rootObj, index): self.refresh_gui()
class DemoMdi(QMainWindow): def __init__(self, parent=None): super(DemoMdi, self).__init__(parent) # 设置窗口标题 self.setWindowTitle('实战PyQt5: MDI多文档接口程序 演示') # 设置窗口大小 self.resize(480, 360) self.initUi() def initUi(self): self.initMenuBar() self.initToolBar() self.mdiArea = QMdiArea(self) self.setCentralWidget(self.mdiArea) self.newDocIndex = 1 def initMenuBar(self): menuBar = self.menuBar() style = QApplication.style() #==== 文件 ====# fileMenu = menuBar.addMenu('文件') #新建一个文档 aFileNew = QAction('新建文档', self) aFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon)) aFileNew.triggered.connect(self.onFileNew) fileMenu.addAction(aFileNew) #打开一个文档 aFileOpen = QAction('打开文档', self) aFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton)) aFileOpen.triggered.connect(self.onFileOpen) fileMenu.addAction(aFileOpen) #关闭一个文档 aFileCloseAll = QAction('关闭全部', self) aFileCloseAll.setIcon(style.standardIcon(QStyle.SP_DialogCloseButton)) aFileOpen.triggered.connect(self.onFileCloseAll) fileMenu.addAction(aFileCloseAll) #添加分割线 fileMenu.addSeparator() #退出 aFileExit = QAction('退出', self) aFileExit.triggered.connect(self.close) fileMenu.addAction(aFileExit) #==== 编辑 ====# editMenu = menuBar.addMenu('编辑') #剪切 aEditCut = QAction('剪切', self) aEditCut.setIcon(QIcon(':/ico/cut.png')) aEditCut.triggered.connect(self.onEditCut) editMenu.addAction(aEditCut) #复制 aEditCopy = QAction('复制', self) aEditCopy.setIcon(QIcon(':/ico/copy.png')) aEditCopy.triggered.connect(self.onEditCopy) editMenu.addAction(aEditCopy) #粘贴 aEditPaste = QAction('粘贴', self) aEditPaste.setIcon(QIcon(':/ico/paste.png')) aEditPaste.triggered.connect(self.onEditPaste) editMenu.addAction(aEditPaste) #==== 窗口排列方式 ====# windowMenu = menuBar.addMenu('窗口') #子窗口模式 aWndSubView = QAction('子窗口模式', self) aWndSubView.triggered.connect(lambda: self.onWinowdMode(0)) windowMenu.addAction(aWndSubView) #标签页模式 aWndTab = QAction('标签页模式', self) aWndTab.triggered.connect(lambda: self.onWinowdMode(1)) windowMenu.addAction(aWndTab) windowMenu.addSeparator() #平铺模式 aWndTile = QAction('平铺模式', self) aWndTile.triggered.connect(lambda: self.onWinowdMode(2)) windowMenu.addAction(aWndTile) #窗口级联模式 aWndCascade = QAction('窗口级联模式', self) aWndCascade.triggered.connect(lambda: self.onWinowdMode(3)) windowMenu.addAction(aWndCascade) def initToolBar(self): toolBar = self.addToolBar('') style = QApplication.style() min_width = 64 btnFileNew = QToolButton(self) btnFileNew.setText('新建文档') btnFileNew.setMinimumWidth(min_width) btnFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon)) btnFileNew.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileNew.clicked.connect(self.onFileNew) toolBar.addWidget(btnFileNew) btnFileOpen = QToolButton(self) btnFileOpen.setText('打开文档') btnFileOpen.setMinimumWidth(min_width) btnFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton)) btnFileOpen.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileOpen.clicked.connect(self.onFileOpen) toolBar.addWidget(btnFileOpen) btnFileCloseAll = QToolButton(self) btnFileCloseAll.setText('关闭全部') btnFileCloseAll.setMinimumWidth(min_width) btnFileCloseAll.setIcon(style.standardIcon( QStyle.SP_DialogCloseButton)) btnFileCloseAll.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileCloseAll.clicked.connect(self.onFileCloseAll) toolBar.addWidget(btnFileCloseAll) toolBar.addSeparator() btnEditCut = QToolButton(self) btnEditCut.setText('剪切') btnEditCut.setMinimumWidth(64) btnEditCut.setIcon(QIcon(':/ico/cut.png')) btnEditCut.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditCut.clicked.connect(self.onEditCut) toolBar.addWidget(btnEditCut) btnEditCopy = QToolButton(self) btnEditCopy.setText('复制') btnEditCopy.setMinimumWidth(64) btnEditCopy.setIcon(QIcon(':/ico/copy.png')) btnEditCopy.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditCopy.clicked.connect(self.onEditCopy) toolBar.addWidget(btnEditCopy) btnEditPaste = QToolButton(self) btnEditPaste.setText('粘贴') btnEditPaste.setMinimumWidth(64) btnEditPaste.setIcon(QIcon(':/ico/paste.png')) btnEditPaste.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditPaste.clicked.connect(self.onEditPaste) toolBar.addWidget(btnEditPaste) def msgCritical(self, strInfo): dlg = QMessageBox(self) dlg.setIcon(QMessageBox.Critical) dlg.setText(strInfo) dlg.show() def onFileNew(self): newDoc = QMdiSubWindow(self) newDoc.setWindowTitle('新文档 ' + str(self.newDocIndex)) self.newDocIndex += 1 newDoc.setWidget(QPlainTextEdit(newDoc)) self.mdiArea.addSubWindow(newDoc) newDoc.show() def onFileOpen(self): path, _ = QFileDialog.getOpenFileName(self, '打开文件', '', '文本文件 (*.txt)') if path: try: with open(path, 'rU') as f: text = f.read() except Exception as e: self.msgCritical(str(e)) else: openDoc = QMdiSubWindow(self) openDoc.setWindowTitle(path) txtEdit = QPlainTextEdit(openDoc) txtEdit.setPlainText(text) openDoc.setWidget(txtEdit) self.mdiArea.addSubWindow(openDoc) openDoc.show() def onFileCloseAll(self): self.mdiArea.closeAllSubWindows() def onEditCut(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.cut() def onEditCopy(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.copy() def onEditPaste(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.paste() def onWinowdMode(self, index): if index == 3: self.mdiArea.cascadeSubWindows() elif index == 2: self.mdiArea.tileSubWindows() elif index == 1: self.mdiArea.setViewMode(QMdiArea.TabbedView) else: self.mdiArea.setViewMode(QMdiArea.SubWindowView)
class MDIWindow(QMainWindow): count = 0 def __init__(self): super().__init__() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() self.current_dir = None file = bar.addMenu("File") file.addAction("New") file.addAction("cascade") file.addAction("Tiled") file.triggered[QAction].connect(self.WindowTrig) load = bar.addMenu("Load") load.addAction("2D") load.addAction("3D") load.triggered[QAction].connect(self.dir_open) self.setWindowTitle("MDI Application") self.base_wd = QMdiSubWindow() self.base_wd.plt_i = pg.PlotItem(labels={ 'bottom': ('slits', 'degrees'), 'left': ('Kin. Energy', 'eV') }) self.base_wd.plt_iv = pg.ImageView(view=self.base_wd.plt_i) self.base_wd.setWidget(self.base_wd.plt_iv) self.base_wd.setWindowTitle("plot window") self.mdi.addSubWindow(self.base_wd) self.base_wd.show() data_DockWidget = QDockWidget('data', self) data_DockWidget.setObjectName(('data window')) data_DockWidget.setAllowedAreas(Qt.RightDockWidgetArea) self.data_list = QListWidget() data_DockWidget.setWidget(self.data_list) self.addDockWidget(Qt.RightDockWidgetArea, data_DockWidget) self.data_list.itemClicked.connect(self.show_data) self.data_list.itemDoubleClicked.connect(self.get_data) def WindowTrig(self, p): if p.text() == "New": MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("Sub Window" + str(MDIWindow.count)) self.mdi.addSubWindow(sub) sub.show() if p.text() == "cascade": self.mdi.cascadeSubWindows() if p.text() == "Tiled": self.mdi.tileSubWindows() def dir_open(self, p): self.current_dir = dlg.File_dlg.openDirNameDialog(self) print(self.current_dir) if p.text() == "2D": print('2D') files_ls = glob.glob(self.current_dir + '/*.ibw') fls = [f[len(self.current_dir) + 1:] for f in files_ls] print(files_ls) self.data_list.addItems(fls) if p.text() == "3D": zip_ls = glob.glob(self.current_dir + '/*.zip') zp = [f[len(self.current_dir) + 1:] for f in zip_ls] print(zp) self.data_list.addItems(zp) def show_data(self, s): file_name = s.text() self.data_dict = ut.ibw2dict(self.current_dir + '/' + file_name) e_sc = self.data_dict['E_axis'][1] - self.data_dict['E_axis'][0] a_sc = self.data_dict['A_axis'][1] - self.data_dict['A_axis'][0] e_str = self.data_dict['E_axis'][0] a_str = self.data_dict['A_axis'][0] self.base_wd.plt_i.setRange(xRange=[self.data_dict['E_axis'][0], self.data_dict['E_axis'][-1]], \ yRange=[self.data_dict['A_axis'][0], self.data_dict['A_axis'][-1]], update=True, padding = 0) self.base_wd.plt_i.getViewBox().setLimits(xMin= e_str, xMax = self.data_dict['E_axis'][-1],\ yMin=self.data_dict['A_axis'][0], yMax=self.data_dict['A_axis'][-1]) self.base_wd.plt_iv.setImage( self.data_dict['data'], pos=[self.data_dict['E_axis'][0], self.data_dict['A_axis'][0]], scale=[e_sc, a_sc]) # self.base_wd.plt_iv.ui.histogram.hide() self.base_wd.plt_iv.ui.roiBtn.hide() self.base_wd.plt_iv.ui.menuBtn.hide() def get_data(self, s): file_name = s.text() self.data_dict = ut.ibw2dict(self.current_dir + '/' + file_name) MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.plt_i = pg.PlotItem(labels={ 'bottom': ('slits', 'degrees'), 'left': ('Kin. Energy', 'eV') }) sub.plt_iv = pg.ImageView(view=sub.plt_i) sub.setWidget(sub.plt_iv) sub.setWindowTitle(file_name) self.mdi.addSubWindow(sub) sub.show() e_sc = self.data_dict['E_axis'][1] - self.data_dict['E_axis'][0] a_sc = self.data_dict['A_axis'][1] - self.data_dict['A_axis'][0] e_str = self.data_dict['E_axis'][0] a_str = self.data_dict['A_axis'][0] e_end = self.data_dict['E_axis'][-1] a_end = self.data_dict['A_axis'][-1] sub.plt_i.setRange(xRange=[self.data_dict['E_axis'][0], self.data_dict['E_axis'][-1]], \ yRange=[self.data_dict['A_axis'][0], self.data_dict['A_axis'][-1]], update=True, padding = 0) sub.plt_i.getViewBox().setLimits(xMin= e_str, xMax = self.data_dict['E_axis'][-1],\ yMin=self.data_dict['A_axis'][0], yMax=self.data_dict['A_axis'][-1]) sub.plt_iv.setImage( self.data_dict['data'], pos=[self.data_dict['E_axis'][0], self.data_dict['A_axis'][0]], scale=[e_sc, a_sc]) sub.plt_iv.ui.roiBtn.clicked.connect(lambda status, a_s = a_str,a_e = a_end,e_s = e_str,e_e = e_end, e_w = e_sc*10, a_w = a_sc*10, iv = sub.plt_iv:\ self.add_lin_ROI(status, a_s, a_e, e_s, e_e, e_w, a_w, iv)) def add_lin_ROI(self, status, a_str, a_end, e_str, e_end, e_w, a_w, iv): if status: roi_edc = pg.LineSegmentROI( [[e_str, (a_str + a_end) / 2], [e_end, (a_str + a_end) / 2]], pen='r', removable=True) iv.addItem(roi_edc) roi_mdc = pg.LineSegmentROI( [[(e_str + e_end) / 2, a_str], [(e_str + e_end) / 2, a_end]], pen='b', removable=True) iv.addItem(roi_mdc) else: print(iv.getRoiPlot) iv.getRoiPlot.removeSegment() iv.removeItem(iv.getRoiPlot)
class Invoice(QMainWindow): '''Runs the main window of the invoice development Calls the table to be used for parts and labor from table_widget.py ''' invoice_count = 0 total_parts_ = 0 labor_supplies_ = 0 recent_open = False start_flag = False current_job = str labor_ = 0 parts_ = 0 supplies = 0 freight_ = 0 subtotal = 0 taxed = 0 totals = 0 tax = 0 partial = 0 finance = 0 new_total = 0 open_list = [] printed_list = {} def __init__(self): '''Initialize the window and get pertinent information read in: Set the window size Set the picture to be a BEI logo Read in the standard labor rates ''' super().__init__() self.size_policy = QSizePolicy.Expanding self.font = QFont() self.font.setPointSize(12) self.showMaximized() self.setWindowIcon(QIcon('BEI_Logo.png')) # backimage=QImage('BEI_Logo.png') self.setWindowTitle('Burl Equipment Inc. Invoices Beta') self.tray = QSystemTrayIcon(self) self.tray.setIcon(QIcon('BEI_Logo.png')) self.show() self.menu_bar() self.statusbar = QStatusBar() self.setStatusBar(self.statusbar) #this is the first time start up section, should only run the very #first time self.base_directory = str( Path(os.path.join(os.environ['USERPROFILE'], 'BEI_Invoices'))) base_entries = os.listdir(os.environ['USERPROFILE']) if 'BEI_Invoices' not in base_entries: initi.First_Run(self.base_directory) def menu_bar(self): '''Create the menu bar for the main window will include Name: Shortcut: Function called: File: New CTRL+N new_invoice_begin Open CTRL+O existing_invoice_open Save CTRL+S print_invoice Quit ALT save_invoice Print CTRL+P +F4 exit_system Edit: Change Labor Rates labor_rates View: View Totals view_totals View Labor Breakdown labor_breakdown Help: View Current Cheat Sheet cheat_sheet Add New Task to Cheat Sheet add_cheat_task ''' self.menuFile = self.menuBar().addMenu("&File") self.actionNew = QAction('&New', self) self.actionNew.setShortcut('Ctrl+N') self.actionNew.triggered.connect(self.new_invoice_begin) self.actionOpen = QAction("&Open", self) self.actionOpen.setShortcut('Ctrl+O') self.actionOpen.triggered.connect(self.existing_invoice_open) self.actionSave = QAction('&Save', self) self.actionSave.setShortcut('Ctrl+S') self.actionSave.setDisabled(True) self.actionSave.triggered.connect(self.save_invoice) # self.actionImport=QAction('&Import Old Job',self) # self.actionImport.triggered.connect(self.old_job) # self.actionImport.setShortcut('Ctrl+I') self.actionPrint = QAction('&Print', self) self.actionPrint.setShortcut('Ctrl+P') self.actionPrint.setDisabled(True) self.actionPrint.triggered.connect(self.print_invoice) self.printMenu = QMenu('Print Envelopes', self) self.actionEnvelope = QAction('&Print All Billed Customer Envelopes', self) self.actionEnvelope.triggered.connect(self.envelop_write) self.actionEnvelope.setShortcut('Ctrl+E') self.actionEnvelope.setDisabled(True) self.actionEnvelope1 = QAction( '&Print Single Billed Customer Envelope', self) self.actionEnvelope1.triggered.connect(self.envelop_write1) self.actionEnvelope1.setShortcut('Ctrl+R') self.actionBilledEnvelopes = QAction('&Print Check Envelope', self) self.actionBilledEnvelopes.triggered.connect(self.billing_envelopes) self.actionBilledEnvelopes.setShortcut('Ctrl+C') self.printMenu.addActions([ self.actionEnvelope, self.actionEnvelope1, self.actionBilledEnvelopes ]) self.actionQuit = QAction('&Exit', self) self.actionQuit.triggered.connect(self.closing) self.actionQuit.setShortcut('Alt+F4') self.menuFile.addActions([ self.actionNew, self.actionOpen, self.actionSave, self.actionPrint ]) self.menuFile.addMenu(self.printMenu) self.menuFile.addAction(self.actionQuit) self.menuEdit = self.menuBar().addMenu('&Edit') self.menuEdit_Change_In = QMenu('Change Basic Invoice Information', self) self.menuEdit_Change_Sy = QMenu('Change Operating Data', self) self.actionLaborRates = QAction('&Change Standard Labor Rates', self) self.actionLaborRates.triggered.connect(self.labor_rates) self.actionAddTechnician = QAction('&Add Technician', self) self.actionAddTechnician.triggered.connect(self.add_tech) self.actionChangeDate = QAction('&Change Invoice Date', self) self.actionChangeDate.triggered.connect(self.date_change) self.actionChangeCustomerAddress = QAction('&Change Customer Address', self) self.actionChangeCustomerAddress.triggered.connect(self.change_address) self.actionBasicInfo = QAction('&Change Basic Information', self) self.actionBasicInfo.triggered.connect(self.change_basic_info) self.actionBasicInfo.setDisabled(True) self.menuEdit_Change_In.addActions([self.actionBasicInfo]) self.menuEdit_Change_Sy.addActions([ self.actionLaborRates, self.actionAddTechnician, self.actionChangeDate, self.actionChangeCustomerAddress ]) self.menuEdit.addMenu(self.menuEdit_Change_In) self.menuEdit.addMenu(self.menuEdit_Change_Sy) self.menuView = self.menuBar().addMenu('&View') self.actionViewLaborBreakdown = QAction('&View Labor Breakdown', self) self.actionViewLaborBreakdown.setDisabled(True) self.actionViewLaborBreakdown.triggered.connect(self.breakdown) self.actionViewAllWindows = QAction('&View All Windows', self) self.actionViewAllWindows.setDisabled(True) self.actionViewAllWindows.triggered.connect(self.view_windows) self.actionViewCutomer = QAction('&View Customer Invoice', self) self.actionViewCutomer.triggered.connect(self.view_customer) self.actionViewCutomer = QAction('&View Customer Invoice', self) self.actionViewCutomer.triggered.connect(self.view_customer) self.actionViewCutomer.setEnabled(False) self.actionViewCompany = QAction('&View Company Invoice', self) self.actionViewCompany.triggered.connect(self.view_company) self.actionViewCompany.setEnabled(False) self.menuView.addActions([ self.actionViewLaborBreakdown, self.actionViewAllWindows, self.actionViewCutomer, self.actionViewCompany ]) self.actionJobNumbers = QAction('&More Job Numbers', self) self.actionJobNumbers.triggered.connect(self.new_job_nums) self.menuJobNumbers = self.menuBar().addMenu('Job Numbers') self.menuJobNumbers.addAction(self.actionJobNumbers) self.menuPayment = self.menuBar().addMenu('&Finance/Payments') self.actionPartialPayment = QAction('&Partial Payment', self) self.actionPartialPayment.triggered.connect(self.partial_payment) self.actionPartialPayment.setDisabled(True) self.actionFinanceCharges = QAction('&Add Finance Charges', self) self.actionFinanceCharges.triggered.connect(self.finance_charges) self.actionFinanceCharges.setDisabled(True) self.menuPayment.addActions( [self.actionPartialPayment, self.actionFinanceCharges]) self.menuHelp = self.menuBar().addMenu('&Help') self.actionViewCheatSheet = QAction('&View Cheat Sheet', self) self.actionViewCheatSheet.triggered.connect(self.cheat_sheet) self.actionNewCheat = QAction('&Add New Item to Cheat Sheet', self) self.actionNewCheat.triggered.connect(self.add_cheat_task) self.actionUpdate = QAction('&Update Application') self.actionUpdate.triggered.connect(self.updater) self.menuHelp.addActions([ self.actionViewCheatSheet, self.actionNewCheat, self.actionUpdate ]) def new_invoice_begin(self): '''Entering basic information: Job Number: Machine: Customer Name: ''' try: self.docked.close() self.docked2.close() self.totals_table.close() self.save_invoice() self.new_window = New_Invoice(12, self.base_directory) # self.new_window.basic_information() self.new_window.start.clicked.connect(self.job_num_insertion) self.new_window.customer_address_line_2.returnPressed.connect( self.new_window.information_) self.new_window.customer_address_line_2.returnPressed.connect( self.job_num_insertion) except: self.new_window = New_Invoice(12, self.base_directory) # self.new_window.basic_information() self.new_window.start.clicked.connect(self.job_num_insertion) self.new_window.customer_address_line_2.returnPressed.connect( self.new_window.information_) self.new_window.customer_address_line_2.returnPressed.connect( self.job_num_insertion) def job_num_insertion(self): '''Call the table with the job number given in the new invoice ''' self.reset_data() if not self.recent_open: self.recent_invoices() self.tax = self.new_window.tax self.customer = self.new_window.customer.replace('#', '') self.machine_text = self.new_window.machine_ self.current_job = self.new_window.job_num if self.current_job not in self.open_list: self.open_list.append(self.current_job) self.recently_opened_invoice.appendRow( QStandardItem(str(self.current_job))) self.invoice_count += 1 #make the folder for this invoice to be saved in self.job_dire = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), self.current_job) try: os.mkdir(self.job_dire) #save the basic information location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), self.current_job) self.table(self.new_window.job_num) basic = os.path.join(location, 'Basic_Info.csv') f = open(basic, 'w') f.write(str(self.current_job) + '\n') f.write(self.new_window.customer + '\n') f.write(self.new_window.machine_ + '\n') f.write('{},{}\n'.format(str(self.new_window.tax), self.new_window.tax_code)) f.write(self.new_window.line1 + '\n') f.write(self.new_window.line2 + '\n') f.close() except: buttonReply = QMessageBox.question( self, 'Confirm New Machine', 'Job Number {} already exist.\nDo you want to overwrite it?'. format(self.new_window.job_num), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if buttonReply == QMessageBox.Yes: self.table(self.new_window.job_num) location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), self.current_job) basic = os.path.join(location, 'Basic_Info.csv') f = open(basic, 'w') f.write(str(self.current_job) + '\n') f.write(self.new_window.customer + '\n') f.write(self.new_window.machine_ + '\n') f.write('{},{}\n'.format(str(self.new_window.tax), self.new_window.tax_code)) f.write(self.new_window.line1 + '\n') f.write(self.new_window.line2 + '\n') f.close() elif buttonReply == QMessageBox.No: self.current_job = str(self.current_job) self.read_in_data() def existing_invoice_open(self): '''Open an existing invoice ''' try: self.docked.close() self.docked2.close() self.save_invoice() except: True if not self.recent_open: self.recent_invoices() #get the saved invoices loc = os.path.join(self.base_directory, 'Saved_Invoices') saved_jobs = os.listdir(loc) self.existing = QWidget() self.existing.setWindowIcon(QIcon('BEI_Logo.png')) self.existing.setWindowTitle('Open Existing Invoice') self.open = QPushButton('Open', self) self.open.setFont(self.font) self.open.setSizePolicy(self.size_policy, self.size_policy) self.open.clicked.connect(self.reader) self.job_to_open = QLineEdit(self) self.job_to_open.setFont(self.font) self.job_to_open.setSizePolicy(self.size_policy, self.size_policy) self.job_to_open.setCompleter(QCompleter(saved_jobs)) # self.job_to_open.returnPressed.connect(self.reader) layout = QVBoxLayout() layout.addWidget(self.job_to_open) layout.addWidget(self.open) self.existing.setLayout(layout) self.existing.setGeometry(400, 400, 300, 100) self.existing.show() def reader(self): self.current_job = self.job_to_open.text() self.read_in_data() self.existing.close() def table(self, num): '''Setup the table for use with a new invoice ''' self.start_flag = True self.actionPrint.setEnabled(True) self.actionSave.setEnabled(True) self.actionViewLaborBreakdown.setEnabled(True) self.actionBasicInfo.setEnabled(True) self.actionViewAllWindows.setEnabled(True) self.actionViewCutomer.setEnabled(True) self.actionViewCompany.setEnabled(True) self.docked = QMdiSubWindow() self.docked.setWindowTitle('Invoice {}'.format(num)) self.num = num self.tabs = QTabWidget(self) self.parts = Parts_Tabs(num) self.tabs.addTab(self.parts, 'Parts') self.labor = Labor_Tabs(num) self.tabs.addTab(self.labor, 'Labor') self.docked.setWidget(self.tabs) self.parts.total.connect(self.calculate_totals) self.labor.labor_total.connect(self.calculate_totals) cust_display = QWidget(self) self.cust_label = QLabel('Customer: {}'.format(self.customer), self) self.cust_label.setFont(self.font) self.machine_label = QLabel('Machine: {}'.format(self.machine_text), self) self.machine_label.setFont(self.font) lay = QHBoxLayout() lay.addWidget(self.cust_label) lay.addWidget(self.machine_label) cust_display.setLayout(lay) #design and insert the totals table self.totals_table = Table(7, 2) self.totals_table.tableWidget.setItem(0, 0, QTableWidgetItem('Parts:')) self.totals_table.tableWidget.setItem(1, 0, QTableWidgetItem('Labor:')) self.totals_table.tableWidget.setItem(2, 0, QTableWidgetItem('Supplies:')) self.totals_table.tableWidget.setItem(3, 0, QTableWidgetItem('Freight:')) self.totals_table.tableWidget.setItem(4, 0, QTableWidgetItem('Subtotal:')) self.totals_table.tableWidget.setItem( 5, 0, QTableWidgetItem('Tax: {:.2f}%'.format(self.tax * 100))) self.totals_table.tableWidget.setItem(6, 0, QTableWidgetItem('Total:')) #set up the comments section self.comments = QTextEdit(self) self.comments.setFont(self.font) self.comments.setSizePolicy(self.size_policy, self.size_policy) self.comments.setText('Comments:\n') self.additional_docking = QWidget(self) layout = QVBoxLayout(self) layout.addWidget(cust_display) layout.addWidget(self.totals_table) layout.addWidget(self.comments) self.additional_docking.setLayout(layout) self.docked2 = QMdiSubWindow() self.docked2.setWidget(self.additional_docking) self.docked2.setWindowTitle('Information') self.mdi = QMdiArea() self.mdi.addSubWindow(self.docked2) self.mdi.addSubWindow(self.docked) self.mdi.tileSubWindows() self.setCentralWidget(self.mdi) # self.window_saved=self.saveState(1) def recent_invoices(self): '''Show a list of recently opened invoices ''' self.recent_open = True self.recent = QDockWidget('Recently opened invoices', self) self.recently_opened_invoice = QStandardItemModel() self.invoices_open = QListView(self) self.invoices_open.setFont(self.font) self.invoices_open.setSizePolicy(self.size_policy, self.size_policy) self.invoices_open.setModel(self.recently_opened_invoice) self.invoices_open.setEditTriggers(QAbstractItemView.NoEditTriggers) self.invoices_open.doubleClicked[QModelIndex].connect(self.recall) self.notes = QTextEdit(self) self.notes.setFont(self.font) self.notes.setSizePolicy(self.size_policy, self.size_policy) self.notes.setText('Notes:\n') self.running_info = QWidget(self) layout = QVBoxLayout(self) layout.addWidget(self.invoices_open) layout.addWidget(self.notes) self.running_info.setLayout(layout) self.recent.setWidget(self.running_info) self.addDockWidget(Qt.LeftDockWidgetArea, self.recent) def recall(self, index): item = self.recently_opened_invoice.itemFromIndex(index) job_number = item.text() self.docked.close() self.docked2.close() self.save_invoice() self.current_job = job_number self.read_in_data() def save_invoice(self, printing=False): '''Save both the parts and labor tables ''' if self.current_job == str: pass else: location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), self.current_job) parts_file = os.path.join(location, 'Parts.csv') #first read and write the parts information f = open(parts_file, 'w') row = [] for i in range(100): try: for j in range(8): if j == 0: if self.parts.parts_table.tableWidget.item( i, j).text() != '*': val = float( self.parts.parts_table.tableWidget.item( i, j).text()) elif self.parts.parts_table.tableWidget.item( i, j).text() == '*': val = self.parts.parts_table.tableWidget.item( i, j).text() row.append(val) else: try: val = self.parts.parts_table.tableWidget.item( i, j).text() row.append(val) except: row.append('') if '\n' in row[-1]: row[-1] = row[-1].split(sep='\n')[0] row[2] = row[2].replace(',', '.') f.write('{},{},{},{},{},{},{},{}\n'.format(*row)) row = [] except: break f.close() #save the total table total_location = os.path.join(location, 'Totals.csv') h = open(total_location, 'w') t_row = [ self.parts_, self.labor_, self.supplies, self.freight_, self.subtotal, self.taxed, self.totals ] for i in t_row: try: float(i) h.write('{:.2f}\n'.format(i)) except: h.write('0') h.close() #save the comments comments_location = os.path.join(location, 'Comments.csv') v = open(comments_location, 'w') v.write(self.comments.toPlainText()) v.close() #finally save the labor information #get the number of techs showing count = self.labor.counts for l in range(count): labor_location = os.path.join(location, 'tech{}.csv'.format(l)) o = open(labor_location, 'w') #get the data from the labor class tech_labor = self.labor.read_data_out(l) for k in range(len(tech_labor)): if '\n' in list(tech_labor[k][-1]): tech_labor[k][-1] = float(tech_labor[k][-1]) o.write('{},{},{},{},{},{},{},{}\n'.format(*tech_labor[k])) o.close() self.statusbar.showMessage('Invoice {} saved'.format(self.current_job), 5000) envelop_writer = EWriter(self.base_directory, self.current_job) envelop_writer.generate_latex() acrobat = 'Acrobat.exe' in (p.name() for p in psutil.process_iter()) reader = 'AcroRd32.exe' in (p.name() for p in psutil.process_iter()) if acrobat: lis = ['taskkill', '/F', '/IM', 'Acrobat.exe', '/T'] subprocess.call(lis) if reader: os.system('taskkill /F /IM "AcroRd32.exe" /T') if printing == False: comp_cust = Saver(self, self.base_directory, self.current_job) comp_cust.out.connect(self.failure) comp_cust.start() def failure(self, value): if value == 1: QMessageBox.information(self, 'Save Failure', 'Closing PDF and trying again', QMessageBox.Ok) # self.save_invoice() def add_tech(self): '''Adding a technician to the company: Changes to make: Add to the tabs Add standard labor rates Change stuff in the base invoice, not sure how this is going to work yet ''' text, okPressed = QInputDialog.getText(self, "Tech Name", "Tech name:", QLineEdit.Normal, "") if okPressed and text != '': regular, okPressed1 = QInputDialog.getDouble( self, "Regular Rate", "Regular Hourly Rate: $", 80, 0, 150, 2) if okPressed1: overtime, okPressed2 = QInputDialog.getDouble( self, "Overtime Rate", "Overtime Hourly Rate: $", 80, 0, 150, 2) if okPressed2: directory = str( Path( os.path.join( os.path.join(os.environ['USERPROFILE'], 'BEI_Invoices')), 'Basic_Information_Totals')) tech_data = open( str(Path(os.path.join(directory, 'Labor_Rates.csv'))), 'a') tech_data.write('{},{},{}\n'.format( text, regular, overtime)) tech_data.close() QMessageBox.information( self, 'Updated', 'Application must be restarted to apply these changes', QMessageBox.Ok) def read_in_data(self): self.actionPartialPayment.setEnabled(True) self.actionFinanceCharges.setEnabled(True) self.invoice_count += 1 if self.current_job not in self.open_list: self.open_list.append(self.current_job) self.recently_opened_invoice.appendRow( QStandardItem(self.current_job)) #open the basic information and read the tax percentage location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), '{}'.format(self.current_job)) e = open(os.path.join(location, 'Basic_Info.csv'), 'r') basic = e.readlines() e.close() self.customer, self.machine_text = basic[1].replace( '\n', ''), basic[2].replace('\n', '') self.tax = float(basic[3].split(sep=',')[0]) self.table(self.current_job) self.machine_label.setText('Machine: {}'.format(self.machine_text)) self.cust_label.setText('Customer: {}'.format(self.customer)) #read in the parts data from the file and hand it off the the parts_tab #class to be placed in the table location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), '{}'.format(self.current_job)) parts_location = os.path.join(location, 'Parts.csv') p_d = open(parts_location, 'r') p_data = p_d.readlines() p_d.close() parts_data = [p_data[i].split(sep=',') for i in range(len(p_data))] self.parts.read_in_data(parts_data) #read in the totals information totals_information = os.path.join(location, 'Totals.csv') t_d = open(totals_information, 'r') t_data = t_d.readlines() t_d.close() totals = [float(i) for i in t_data] #reset all the s self.reset_data() #try to read in payments and finance cahrges if they exist try: par_d = open(os.path.join(location, 'Payments.csv'), 'r') self.partial = float(par_d.readlines()[0]) par_d.close() except: self.partial = 0 try: fin_d = open(os.path.join(location, 'Finance.csv'), 'r') self.finance = float(fin_d.readlines()[0]) fin_d.close() except: self.finance = 0 self.parts_, self.labor_, self.supplies, self.freight_, self.subtotal, self.taxed, self.totals = totals #set the values into the totals table # self.totals=self.totals+self.finance-self.partial for i in range(len(totals)): self.totals_table.tableWidget.setItem( i, 1, QTableWidgetItem('${:,.2f}'.format(totals[i]))) #read and put the comments in place comments_location = os.path.join(location, 'Comments.csv') c_data = open(comments_location, 'r') com_data = c_data.readlines() c_data.close() combi = '' for i in com_data: combi += str(i) self.comments.setText(combi) #read in the labor data #determine the number of tech there are tech_num = 0 dir_ = os.listdir(location) for i in range(len(dir_)): if 'tech' in dir_[i]: tech_num += 1 for l in range(tech_num): loca = os.path.join(location, 'tech{}.csv'.format(l)) l_data = open(loca, 'r') lab_data = l_data.readlines() l_data.close() labor_data = [o.split(sep=',') for o in lab_data] self.labor.read_in_data(l, labor_data) def reset_data(self): self.parts_, self.labor_, self.supplies, self.freight_, self.subtotal, self.taxed, self.totals = [ 0, 0, 0, 0, 0, 0, 0 ] self.partial, self.finance = 0, 0 def labor_rates(self): '''Change the labor rates ''' self.changes = Labor_Rates(self.base_directory, self.font, self.size_policy) self.changes.show() def update_parts_total(self): self.parts_calculator() # self.parts_=round(self.parts.parts_total,2) # self.freight_=round(self.parts.freight_total,2) self.totals_table.tableWidget.setItem( 0, 1, QTableWidgetItem('${:,.2f}'.format(self.parts_))) self.totals_table.tableWidget.setItem( 3, 1, QTableWidgetItem('${:,.2f}'.format(self.freight_))) self.total_parts_ = self.parts_ + self.freight_ def parts_calculator(self): self.parts_ = 0 self.freight_ = 0 # self.parts.parts_sumation() # self.parts_=self.parts.parts_total # self.freight_=self.parts.freight_total+1 for i in range(100): try: self.parts_ += float( self.parts.parts_table.tableWidget.item(i, 5).text()) try: self.freight_ += float( self.parts.parts_table.tableWidget.item(i, 6).text()) except: self.freight_ += 0 except: True def update_labor(self): total_labor = 0 for i in range(self.labor.counts): total_labor += self.labor.find_tech_total(i) self.totals_table.tableWidget.setItem( 1, 1, QTableWidgetItem('${:,.2f}'.format(round(total_labor, 2)))) self.totals_table.tableWidget.setItem( 2, 1, QTableWidgetItem('${:,.2f}'.format(round(total_labor * 0.05, 2)))) self.supplies = round(total_labor * 0.05, 2) self.labor_ = total_labor self.labor_supplies_ = round(self.labor_, 2) + self.supplies def calculate_totals(self): '''Calculate the totals for the totals table and display it ''' self.update_labor() # self.parts.parts_sumation() self.update_parts_total() self.subtotal = self.labor_supplies_ + self.total_parts_ self.totals_table.tableWidget.setItem( 4, 1, QTableWidgetItem('${:,.2f}'.format(self.subtotal))) self.taxed = self.tax * self.subtotal self.totals_table.tableWidget.setItem( 5, 1, QTableWidgetItem('${:,.2f}'.format(self.taxed))) self.totals = self.subtotal + self.taxed + self.finance - self.partial self.totals_table.tableWidget.setItem( 6, 1, QTableWidgetItem('${:,.2f}'.format(self.totals))) def print_invoice(self): ''' Print the customer and company invoices ''' #make sure the invoice is saved self.save_invoice(printing=True) self.printed_list[self.current_job] = [ self.customer, self.machine_text ] try: pdf2.PDF_Builder(self.current_job, self.base_directory, 'Company').print_tex() pdf2.PDF_Builder(self.current_job, self.base_directory, 'Customer').print_tex() except: QMessageBox.information(self, 'Print Failure', 'Close file and try again', QMessageBox.Ok) #first check and see if the Envelopes directory has this #months print list envelope_date = EP(self.base_directory, self.customer, self.current_job) self.envelope_date = envelope_date.dater() self.actionEnvelope.setEnabled(True) self.actionEnvelope1.setEnabled(True) def closing(self): # self.save_invoice() self.close() def breakdown(self): '''view the labor break down ''' #open the labor rates to get the names loc = os.path.join(self.base_directory, 'Basic_Information_Totals') file_loc = os.path.join(loc, 'Labor_Rates.csv') f = open(file_loc, 'r') f_data = f.readlines() f.close() names = [] for i in range(len(f_data)): names.append(f_data[i].split(sep=',')[0]) #get the totals from the labor page individauls = self.labor.find_tech_individual() #combine the two lists into a single string combined = '' for i in range(len(individauls)): combined += '{}: ${:,.2f}\n'.format(names[i], individauls[i]) QMessageBox.information(self, 'Labor Breakdown', combined, QMessageBox.Ok) def date_change(self): '''change the data on the invoices for the month ''' self.date_changed = QWidget() self.date_changed.setWindowTitle('Change Invoice Date') self.date_changed.setWindowIcon(QIcon('BEI_Logo.png')) self.line = QLineEdit() self.line.setFont(self.font) self.line.setSizePolicy(self.size_policy, self.size_policy) self.save_date = QPushButton('Save Date') self.save_date.setFont(self.font) self.save_date.setSizePolicy(self.size_policy, self.size_policy) self.save_date.clicked.connect(self.saved_date) layout = QVBoxLayout() layout.addWidget(self.line) layout.addWidget(self.save_date) self.date_changed.setLayout(layout) d_location = os.path.join(self.base_directory, 'Basic_Information_Totals') self.date_location = os.path.join(d_location, 'Invoice_Date.txt') y = open(self.date_location, 'r') date = y.readlines() y.close() self.line.setText(date[0]) self.date_changed.show() def saved_date(self): ''' Save the new date ''' y = open(self.date_location, 'w') y.write(self.line.text()) y.close() self.date_changed.close() def new_job_nums(self): '''Run the class to create more job numbers ''' self.n_jobs = Job_Numbers() def cheat_sheet(self): '''Open the cheat sheet for viewing ''' self.chea = Read_Cheat_Sheet(self.font, self.size_policy, self.base_directory) def add_cheat_task(self): self.cheat = Write_Cheat_Sheet(self.font, self.size_policy, self.base_directory) def partial_payment(self): self.a = Partial_Payments(self.font, self.size_policy) self.a.add.clicked.connect(self.proce) def proce(self): try: self.a.process() self.this_payment = self.a.amount try: location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), '{}'.format(self.current_job)) payments = os.path.join(location, 'Payments.csv') f = open(payments, 'r') value = float(f.readlines()[0]) f.close() self.this_payment += value except: self.this_payment = self.this_payment self.comments.append( '''Partial payment of ${:,.2f} on {}, leaves a remaining balance of ${:,.2f}''' .format(self.a.amount, self.a.date, self.totals - self.a.amount)) f = open(payments, 'w') f.write(str(self.this_payment)) self.partial = self.this_payment self.calculate_totals() f.close() except: pass def finance_charges(self): self.charg = Finance_Charges(self.font, self.size_policy) self.charg.add.clicked.connect(self.fin_process) def fin_process(self): self.charg.process() self.finance += self.charg.amount self.comments.append( 'Finance Charge of ${:,.2f} applied on {}, kindly remit payment immediately.' .format(self.charg.amount, self.charg.date)) location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), '{}'.format(self.current_job)) fin_loc = os.path.join(location, 'Finance.csv') f = open(fin_loc, 'w') f.write(str(self.finance)) f.close() self.calculate_totals() def change_basic_info(self): #first read in the current status of the basic info location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), '{}'.format(self.current_job)) e = open(os.path.join(location, 'Basic_Info.csv'), 'r') basic = e.readlines() e.close() self.update_info = QWidget() self.update_info.setWindowTitle('Update Basic Information') self.update_info.setWindowIcon(QIcon('BEI_Logo.png')) mach = QLabel('Machine', self) mach.setFont(self.font) mach.setSizePolicy(self.size_policy, self.size_policy) tax = QLabel('Tax [%]', self) tax.setFont(self.font) tax.setSizePolicy(self.size_policy, self.size_policy) self.machine = QLineEdit(self) self.machine.setFont(self.font) self.machine.setSizePolicy(self.size_policy, self.size_policy) self.machine.setText(basic[2]) self.tax_value = QLineEdit(self) self.tax_value.setFont(self.font) self.tax_value.setSizePolicy(self.size_policy, self.size_policy) self.tax_value.setText( str(round(float(basic[3].split(sep=',')[0]) * 100, 2))) update = QPushButton('Update', self) update.setFont(self.font) update.setSizePolicy(self.size_policy, self.size_policy) update.clicked.connect(self.update_basic_values) layout = QGridLayout() layout.addWidget(mach, 0, 0) layout.addWidget(self.machine, 0, 1) layout.addWidget(tax, 1, 0) layout.addWidget(self.tax_value, 1, 1) layout.addWidget(update, 2, 0) self.update_info.setLayout(layout) self.update_info.show() def update_basic_values(self): self.update_info.close() location = os.path.join( os.path.join(self.base_directory, 'Saved_Invoices'), '{}'.format(self.current_job)) e = open(os.path.join(location, 'Basic_Info.csv'), 'r') basic = e.readlines() e.close() flag = False #change the information in basic[2] and basic[3] to match the new values if basic[2].split(sep='\n')[0] != self.machine.text(): old = basic[2].split(sep='\n')[0].replace(' ', '_') cust = basic[1].split(sep='\n')[0].replace(' ', '_') file_name = basic[0].split(sep='\n')[0] + '.pdf' flag = True basic[2] = self.machine.text() self.machine_text = basic[2] self.machine_label.setText('Machine: {}'.format(self.machine_text)) if float(basic[3].split( sep=',')[0]) != float(self.tax_value.text()) / 100: try: tax_code, ok = QInputDialog.getText( self, 'Update Tax Code', 'Tax Code: ', QLineEdit.Normal, basic[3].split(sep=',')[1].split(sep='\n')[0]) except: tax_code, ok = QInputDialog.getText(self, 'Update Tax Code', 'Tax Code: ', QLineEdit.Normal, "") basic[3] = '{},{}'.format( float(self.tax_value.text()) / 100, tax_code) self.tax = float(self.tax_value.text()) / 100 f = open(os.path.join(location, 'Basic_Info.csv'), 'w') for i in range(len(basic)): if '\n' not in basic[i]: f.write('{}\n'.format(basic[i])) else: f.write('{}'.format(basic[i])) f.close() #change the percent shown in the total value self.totals_table.tableWidget.setItem( 5, 0, QTableWidgetItem('Tax: {:.2f}%'.format(self.tax * 100))) #next update the totals self.calculate_totals() self.save_invoice() time.sleep(3) if flag: #depending on if the machine has been updated, get rid of the previous #version of the file and start change it to the new location location = os.path.join(os.path.expanduser('~/Desktop'), 'BEI_Invoices') old_location_cust_ = os.path.join( os.path.join(location, 'Customer'), cust) old_location_cust = os.path.join(old_location_cust_, old) old_final_cust = os.path.join(old_location_cust, file_name) old_location_comp_ = os.path.join( os.path.join(location, 'Company'), cust) old_location_comp = os.path.join(old_location_comp_, old) len_old = len(os.listdir(old_location_comp)) old_final_comp = os.path.join(old_location_comp, file_name) if len_old == 1: shutil.rmtree(old_location_comp) shutil.rmtree(old_location_cust) else: os.unlink(old_final_comp) os.unlink(old_final_cust) def view_windows(self): '''Used to re-initialize the totals and main window''' self.save_invoice() self.read_in_data() def closeEvent(self, event): if self.start_flag: self.save_invoice(printing=True) flag = self.save_no_threading() if flag == 0: reply = QMessageBox.question( self, 'Close Window', 'Do you want to close the application?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() else: event.ignore() else: event.accept() def updater(self): QMessageBox.information(self, 'Restart Required', 'Run BEI_Updater and Restart program', QMessageBox.Ok) self.close() def change_address(self): self.edi = EDI(self.font, self.size_policy, self.base_directory) def view_customer(self): flag = self.save_no_threading() if flag == 0: location = os.path.join(os.path.expanduser('~/Desktop'), 'BEI_Invoices') location = os.path.join(location, 'Customer') cust_location = os.path.join(location, self.customer.replace(' ', '_')) machine_location = os.path.join( cust_location, self.machine_text.replace(' ', '_')) job_location = os.path.join(machine_location, '{}.pdf'.format( self.current_job)).replace('&', '^&') print(job_location) subprocess.Popen(job_location, shell=True) else: pass def view_company(self): flag = self.save_no_threading() if flag == 0: location = os.path.join(os.path.expanduser('~/Desktop'), 'BEI_Invoices') location = os.path.join(location, 'Company') cust_location = os.path.join(location, self.customer.replace(' ', '_')) machine_location = os.path.join( cust_location, self.machine_text.replace(' ', '_')) job_location = os.path.join(machine_location, '{}.pdf'.format( self.current_job)).replace('&', '^&') subprocess.Popen(job_location, shell=True) else: pass def save_no_threading(self): self.save_invoice(printing=True) try: pdf2.PDF_Builder(self.current_job, self.base_directory, 'Company') pdf2.PDF_Builder(self.current_job, self.base_directory, 'Customer') return 0 except: QMessageBox.information(self, 'Opening Failure', 'Close file and try again', QMessageBox.Ok) time.sleep(1) return 1 def envelop_write(self): #navigate to the envelope folder loc = os.path.join( os.path.join(self.base_directory, 'Customer_Envelopes'), self.envelope_date + '.txt') f = open(loc, 'r') data = f.readlines() f.close() job_numbers = [] for i in data: job_numbers.append(i.split()[1]) QMessageBox.information( self, 'Envelope Printing', 'Load {} invoices into printer before clicking OK'.format( len(job_numbers)), QMessageBox.Ok) base = os.path.join(self.base_directory, 'Saved_Invoices') for i in range(len(job_numbers)): enve_loc = os.path.join(os.path.join(base, job_numbers[i]), 'envelope.pdf') os.startfile(enve_loc, 'print') def envelop_write1(self): #get the job number to print num, ok = QInputDialog.getText(self, 'Single Customer Envelope', 'Job Number to print:', QLineEdit.Normal, '') if num != '' and ok: writer = EWriter(self.base_directory, num) writer.generate_latex() QMessageBox.information( self, 'Envelope Printing', 'Load 1 invoices into printer before clicking OK', QMessageBox.Ok) writer.print_pdf() def billing_envelopes(self): self.billing_ = CE(self.base_directory)
class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) # TODO put these in a data model object and xml file self.column_order = [ SampleTypes.Time, SampleTypes.Pressure, SampleTypes.Temperature ] self.sample_types_requested_ordered = [] self.delimiter = ',' self.current_row = 0 self.setWindowTitle("P-T Tester") self.arduino_thread = None self.arduino_worker = None self.test_state = State.ReadyNotRan self.output_file = None self.ax = None self.ax_secondaries = dict() self.plot_colors = dict() # CONFIG DOCK self.config_dock = ConfigDock("Test Options", self) self.config_dock.start_button_slot(self.start_test) self.addDockWidget(Qt.LeftDockWidgetArea, self.config_dock) # FILE OUTPUT DOCK self.outfile_dock = OutfileDock("Output Selection", self) self.addDockWidget(Qt.LeftDockWidgetArea, self.outfile_dock) # TABLE Window # TODO TableWidget members private, put table editing functions in TableWidget, not here self.table_view_tbl = TableWidget(self) # PLOT window self.plot_window = PlotWindow(self) # MDI SETUP self.mdi_area = QMdiArea() self.setCentralWidget(self.mdi_area) self.mdi_area.addSubWindow(self.table_view_tbl) self.mdi_area.addSubWindow(self.plot_window) self.mdi_area.tileSubWindows() # set initial state of window self.update_status(State.ReadyNotRan) def initialize_plot(self): plt.ion() self.plot_colors[ SampleTypes.Pressure] = self.config_dock.pressure_color_name self.plot_colors[ SampleTypes.Temperature] = self.config_dock.temperature_color_name self.plot_window.figure.clear() self.ax = self.plot_window.figure.add_subplot(111) self.ax.set_xlabel('time (s)') self.ax.set_ylabel( SampleNames.names[self.sample_types_requested_ordered[0]], color=self.plot_colors[self.sample_types_requested_ordered[0]]) if len(self.sample_types_requested_ordered) > 1: for sample_type in self.sample_types_requested_ordered[1:]: self.ax_secondaries[sample_type] = self.ax.twinx() self.ax_secondaries[sample_type].set_ylabel( SampleNames.names[sample_type], color=self.plot_colors[sample_type]) def plot_sample(self, current_time, sample): # TODO why doesn't it show the line?, only markers are shown. marker = dict() marker[SampleTypes.Pressure] = self.config_dock.pressure_marker() marker[SampleTypes.Temperature] = self.config_dock.temp_marker() self.ax.plot( current_time, sample.values[self.sample_types_requested_ordered[0]], color=self.plot_colors[self.sample_types_requested_ordered[0]], marker=marker[self.sample_types_requested_ordered[0]], linewidth='2', linestyle='-') for sample_type in self.sample_types_requested_ordered[1:]: self.ax_secondaries[sample_type].plot(current_time, sample.values[sample_type],\ color=self.plot_colors[sample_type], marker=marker[sample_type]) def start_test(self): # is at least 1 type of reading set record_p = self.config_dock.record_pressure_is_checked() record_t = self.config_dock.record_temperature_is_checked() if not record_p and not record_t: QMessageBox.warning(self, "Invalid Test Parameters", "Must select at least 1 reading type", QMessageBox.Ok) return # check test parameters sample_rate = self.config_dock.sample_rate() nb_samples = self.config_dock.number_of_samples() if nb_samples <= 1: QMessageBox.warning( self, "Invalid Test Parameters", "Duration too short or sampling rate too large", QMessageBox.Ok) return if not self.config_dock.record_pressure_is_checked( ) and not self.config_dock.record_temperature_is_checked(): QMessageBox(self, "Invalid Selection", "At least one type of measurement must be selected", QMessageBox.Ok) return # check if there is already a test in progress if self.test_state == State.InProgress: return # worker thread will check connection, not done here # if test(s) were run before, table may contain data if self.test_state != State.ReadyNotRan and self.current_row > 1: choice = QMessageBox.question( self, "Starting New Test", "Starting new test will erase current data,\ do you want to continue?", QMessageBox.Yes | QMessageBox.No) if choice == QMessageBox.No: return else: self.table_view_tbl.clearContents() # check given folder+file can be created if not self.outfile_dock.is_file_ok(): return # if here, test can begin self.sample_types_requested_ordered = [] for samp_type in self.column_order: if samp_type == SampleTypes.Pressure and record_p: self.sample_types_requested_ordered.append( SampleTypes.Pressure) if samp_type == SampleTypes.Temperature and record_t: self.sample_types_requested_ordered.append( SampleTypes.Temperature) self.initialize_table() self.initialize_file() self.initialize_plot() # set up arduino worker and signal-slots self.initialize_worker(nb_samples, record_p, record_t, sample_rate) # reading of measurements starts here # TODO make sure QThread terminates successfully upon quitting app while second thread is running self.arduino_thread.start() # gui updates # timer is started by worker thread, not here # update_gui called by update_status self.update_status(State.InProgress) def initialize_worker(self, nb_samples, record_p, record_t, sample_rate): self.arduino_worker = ArduinoWorker(sample_rate, nb_samples, record_p, record_t) self.arduino_thread = QThread() self.arduino_worker.moveToThread(self.arduino_thread) self.arduino_thread.started.connect(self.arduino_worker.run) self.arduino_worker.sample_received.connect(self.process_sample) self.arduino_worker.timer_start.connect(self.update_test_timers) self.config_dock.stop_button_slot( self.arduino_thread.requestInterruption) self.arduino_worker.stopped.connect(self.stop_test) def update_gui(self, test_state): # config_dock runs its own update_gui through its update_label # so, no need to call it self.outfile_dock.update_gui(test_state) def update_test_timers(self): cur_time = QTime.currentTime() end_time = cur_time.addSecs(self.config_dock.duration()) self.config_dock.set_start_time(cur_time.toString("hh:mm:ss")) self.config_dock.set_end_time(end_time.toString("hh:mm:ss")) def initialize_table(self): number_of_columns = len( self.sample_types_requested_ordered) + 1 # +1 for time number_of_samples = self.config_dock.number_of_samples() self.table_view_tbl.setColumnCount(number_of_columns) self.table_view_tbl.setRowCount(number_of_samples) headers = [] headers.append(SampleNames.names[SampleTypes.Time]) for sample_type in self.sample_types_requested_ordered: headers.append(SampleNames.names[sample_type]) self.table_view_tbl.setHorizontalHeaderLabels(headers) self.table_view_tbl.clearContents() self.current_row = 0 def initialize_file(self): self.output_file = open(self.outfile_dock.full_path(), "w") headers = [] headers.append(SampleNames.names[SampleTypes.Time]) for sample_type in self.sample_types_requested_ordered: headers.append(SampleNames.names[sample_type]) for index, header in enumerate(headers): self.output_file.write(header) if index != len(headers) - 1: self.output_file.write(self.delimiter) self.output_file.write("\n") def process_sample(self, sample): current_time = self.current_row * self.config_dock.sample_rate() self.print_sample_qtable(current_time, sample) self.print_sample_csv(current_time, sample) self.plot_sample(current_time, sample) self.table_view_tbl.selectRow(self.current_row) self.current_row += 1 def print_sample_qtable(self, current_time, sample): self.table_view_tbl.setItem( self.current_row, 0, QTableWidgetItem("{:.2f}".format(current_time))) for i, sample_type in enumerate(self.sample_types_requested_ordered, start=1): self.table_view_tbl.setItem( self.current_row, i, QTableWidgetItem("{:.2f}".format(sample.values[sample_type]))) def print_sample_csv(self, current_time, sample): self.output_file.write("{:.2f}".format(current_time) + self.delimiter) for index, sample_type in enumerate( self.sample_types_requested_ordered): self.output_file.write("{:.2f}".format(sample.values[sample_type])) if index != len(self.sample_types_requested_ordered) - 1: self.output_file.write(self.delimiter) self.output_file.write("\n") logger.info( "P and T readings being written to csv: {:.2f}, {:.2f}".format( sample.values[SampleTypes.Pressure], sample.values[SampleTypes.Temperature])) def stop_test(self, state): if self.test_state == State.InProgress: self.update_status(state) self.test_state = state self.output_file.close() self.arduino_thread.quit() self.arduino_worker = None self.arduino_thread = None def update_status(self, test_state): self.test_state = test_state self.update_gui(test_state) self.config_dock.update_status(self.test_state)
class MainWindow(QMainWindow): _logname = 'MainWindow' _log = logging.getLogger(f'{_logname}') def __init__(self, LOGIN, isRunningOnPi=False, parent=None): super(MainWindow, self).__init__() self.main_widget = QWidget() self.mdi = QMdiArea() self.setWindowTitle('Brew Monitoring System') bar = self.menuBar() filebar = bar.addMenu("File") filebar.addAction("New User") filebar.addAction("New Brew") filebar.triggered.connect(self.fileaction) viewbar = bar.addMenu("View") viewbar.addAction("Mash and Boil Vessels") viewbar.addAction("Fermentation Vessels") viewbar.addAction("Past Brew Data") viewbar.triggered.connect(self.viewaction) windowbar = bar.addMenu("Window") windowbar.addAction("Cascade") windowbar.addAction("Tiled") windowbar.triggered.connect(self.windowaction) systembar = bar.addMenu("System") systembar.addAction("Check For Faults") self.mainwindow = MdiMainWindow(LOGIN, isRunningOnPi=isRunningOnPi, parent=self) quitButton = QPushButton("Quit") quitButton.clicked.connect(lambda: self.close()) quitLayout = QHBoxLayout() quitLayout.addStretch(10) quitLayout.addWidget(quitButton) splitter = QSplitter() splitter.setOrientation(2) splitter.addWidget(self.mainwindow) splitter.addWidget(self.mdi) layout = QVBoxLayout() #layout.addWidget(self.mainwindow) #layout.addWidget(self.mdi) layout.addWidget(splitter) #layout.addLayout(quitLayout) #self.main_widget.setLayout(layout) # self.main_widget.showFullScreen() self.main_widget.setLayout(layout) self.setMinimumSize(700, 500) self.resize(0, 0) self.setCentralWidget(self.main_widget) # self.showFullScreen() def fileaction(self, selected): selected = selected.text() if selected == "New User": self.mainwindow.newUserClicked() elif selected == "New Brew": self.mainwindow.startBrewClicked() def windowaction(self, selected): selected = selected.text() if selected == "Cascade": self.mdi.cascadeSubWindows() elif selected == "Tiled": self.mdi.tileSubWindows() def viewaction(self, selected): selected = selected.text() if selected == "Fermentation Vessels": self.mainwindow.fermentButtonClicked() elif selected == "Mash and Boil Vessels": self.mainwindow.mashBoilButtonClicked() elif selected == "Past Brew Data": self.mainwindow.viewDataClicked()
class MDIWindow(QMainWindow): count = 0 def __init__(self): super().__init__() self.data_dict = {} self.mdi = QMdiArea() self.setCentralWidget(self.mdi) # self.mdi.resize(950,950) bar = self.menuBar() self.current_dir = None self.opened_wd_names = [] file = bar.addMenu("File") file.addAction("New") file.addAction("cascade") file.addAction("Tiled") file.triggered[QAction].connect(self.WindowTrig) load = bar.addMenu("Load") load.addAction("2D") load.addAction("3D") load.triggered[QAction].connect(self.dir_open) toolbar = QToolBar() self.addToolBar(toolbar) bw_button_action = QAction('base_wnd', self) bw_button_action.setStatusTip('base window button') bw_button_action.triggered.connect(self.onclicktb) toolbar.addAction(bw_button_action) self.setWindowTitle("MDI Application") self.base_wd = QMdiSubWindow() self.base_wd.setAttribute(Qt.WA_DeleteOnClose, False) self.base_wd.resize(400, 400) self.base_wd.plt_i = pg.PlotItem(labels={ 'left': ('slits', 'degrees'), 'bottom': ('Kin. Energy', 'eV') }) self.base_wd.plt_iv = pg.ImageView(view=self.base_wd.plt_i) self.base_wd.setWidget(self.base_wd.plt_iv) self.base_wd.setWindowTitle("plot window") self.mdi.addSubWindow(self.base_wd) self.base_wd.show() data_DockWidget = QDockWidget('data', self) data_DockWidget.setObjectName(('data window')) data_DockWidget.setAllowedAreas(Qt.RightDockWidgetArea) self.data_list = QListWidget() data_DockWidget.setWidget(self.data_list) self.addDockWidget(Qt.RightDockWidgetArea, data_DockWidget) self.data_list.itemClicked.connect(self.show_data) self.data_list.itemDoubleClicked.connect(self.get_data) self.mdi.subWindowActivated.connect(self.get_data) def WindowTrig(self, p): if p.text() == "New": MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("Sub Window" + str(MDIWindow.count)) self.mdi.addSubWindow(sub) sub.show() if p.text() == "cascade": self.mdi.cascadeSubWindows() if p.text() == "Tiled": self.mdi.tileSubWindows() def dir_open(self, p): self.current_dir = dlg.File_dlg.openDirNameDialog(self) print(self.current_dir) if p.text() == "2D": print('2D') files_ls = glob.glob(self.current_dir + '/*.ibw') fls = [f[len(self.current_dir) + 1:] for f in files_ls] print(files_ls) self.data_list.addItems(fls) if p.text() == "3D": zip_ls = glob.glob(self.current_dir + '/*.zip') zp = [f[len(self.current_dir) + 1:] for f in zip_ls] print(zp) self.data_list.addItems(zp) def show_data(self, s): print('show data') file_name = s.text() self.data_dict = ut.ibw2dict(self.current_dir + '/' + file_name) e_sc = self.data_dict['E_axis'][1] - self.data_dict['E_axis'][0] a_sc = self.data_dict['A_axis'][1] - self.data_dict['A_axis'][0] e_str = self.data_dict['E_axis'][0] a_str = self.data_dict['A_axis'][0] self.base_wd.plt_i.setRange(xRange=[self.data_dict['E_axis'][0], self.data_dict['E_axis'][-1]], \ yRange=[self.data_dict['A_axis'][0], self.data_dict['A_axis'][-1]], update=True, padding = 0) self.base_wd.plt_i.getViewBox().setLimits(xMin= e_str, xMax = self.data_dict['E_axis'][-1],\ yMin=self.data_dict['A_axis'][0], yMax=self.data_dict['A_axis'][-1]) self.base_wd.plt_iv.setImage( self.data_dict['data'], pos=[self.data_dict['E_axis'][0], self.data_dict['A_axis'][0]], scale=[e_sc, a_sc]) # self.base_wd.plt_iv.ui.histogram.hide() self.base_wd.plt_iv.ui.roiBtn.hide() self.base_wd.plt_iv.ui.menuBtn.hide() def get_data(self, s): if isinstance(s, QMdiSubWindow) and str( s.objectName()) in self.opened_wd_names: sub = self.mdi.currentSubWindow() self.data_dict = ut.ibw2dict(self.current_dir + '/' + str(s.objectName())) elif isinstance(s, QListWidgetItem): file_name = s.text() self.opened_wd_names.append(file_name) MDIWindow.count = MDIWindow.count + 1 sub = QMdiSubWindow() sub.resize(550, 550) sub.setWindowTitle(file_name) sub.setObjectName(file_name) self.data_dict = ut.ibw2dict(self.current_dir + '/' + file_name) else: print(isinstance(s, QMdiSubWindow), isinstance(s, QListWidgetItem)) print(type(s)) return e_sc = self.data_dict['E_axis'][1] - self.data_dict['E_axis'][0] a_sc = self.data_dict['A_axis'][1] - self.data_dict['A_axis'][0] e_rg = self.data_dict['E_axis'][-1] - self.data_dict['E_axis'][0] a_rg = self.data_dict['A_axis'][-1] - self.data_dict['A_axis'][0] e_str = self.data_dict['E_axis'][0] a_str = self.data_dict['A_axis'][0] e_end = self.data_dict['E_axis'][-1] a_end = self.data_dict['A_axis'][-1] print(e_str, a_str) print(e_end, a_end) print(e_rg, a_rg) print(e_sc, a_sc) gr_v = pg.GraphicsView() l = pg.GraphicsLayout() gr_v.setCentralWidget(l) sub.setWidget(gr_v) self.mdi.addSubWindow(sub) sub.show() p1 = l.addPlot(x=[1, 2], y=[1, 2], name="Plot1", title="EDC", pen="r", row=0, col=0) # label1 = pg.LabelItem(justify='right') # p1.addItem(label1) plt_i = pg.PlotItem(labels={ 'left': ('slits', 'degrees'), 'bottom': ('Kin. Energy', 'eV') }) plt_i.setRange(xRange=[e_str, e_end], yRange=[a_str, a_end], update=True, padding=0) vb = plt_i.getViewBox() vb.setLimits(xMin=e_str, xMax=e_end, yMin=a_str, yMax=a_end) vb.setMouseMode(vb.RectMode) l.addItem(plt_i, row=1, col=0) img_i = pg.ImageItem(self.data_dict['data'], border=None) qrect = vb.viewRect() img_i.setRect(qrect) vb.addItem(img_i) vb.autoRange() # vb.invertX() vb.invertY() hist = pg.HistogramLUTItem(image=img_i) l.addItem(hist, row=0, col=1) p2 = l.addPlot(x=[1, 2], y=[2, 1], name="Plot2", title="MDC", pen="g", row=1, col=1) # label2 = pg.LabelItem(justify='left') # plt_i.addItem(label2) # cross hair vLine = pg.InfiniteLine(angle=90, movable=False) hLine = pg.InfiniteLine(angle=0, movable=False) p1.addItem(vLine, ignoreBounds=False) p1.addItem(hLine, ignoreBounds=False) vb1 = p1.vb pcv = plt_i.addLine(x=e_end, pen='r') pch = plt_i.addLine(y=a_str, pen='r') # lROI = pg.ROI(((e_str+e_end)/2,a_str), size=(5*e_sc,a_rg)) # vb.addItem(lROI) # slice, coor = lROI.getArrayRegion(self.data_dict['data'], img_i ,returnMappedCoords = True) # print('slice') # sl_sum=np.sum(slice, axis=0) # print(sl_sum[0:10]) # print(type(slice), slice.shape) # print(type(coor), coor.shape) # print(coor[1,0,0:10]) # p2.invertY() # p2.setYLink(plt_i) # p2.plot(y=coor[1,0,:], x=sl_sum) def onMouseMoved(point): p = vb.mapSceneToView(point) pcv.setValue(p.x()) pch.setValue(p.y()) # print(p.x(), p.y()) hROI = pg.ROI((e_str, p.y()), size=(e_rg, 5 * a_sc)) vb.addItem(hROI) hROI.hide() sl, co = hROI.getArrayRegion(self.data_dict['data'], img_i, returnMappedCoords=True) sl_sum = np.sum(sl, axis=1) p1.setXLink(plt_i) p1.plot(x=co[0, :, 0], y=sl_sum, clear=True) vROI = pg.ROI((p.x(), a_str), size=(5 * e_sc, a_rg)) vb.addItem(vROI) vROI.hide() slc, coo = vROI.getArrayRegion(self.data_dict['data'], img_i, returnMappedCoords=True) sl_sum = np.sum(slc, axis=0) p2.invertY() p2.setYLink(plt_i) p2.plot(y=coo[1, 0, :], x=sl_sum, clear=True) # label2.setText("{}-{}".format(p.x(), p.y())) img_i.scene().sigMouseMoved.connect(onMouseMoved) def onclicktb(self): self.base_wd.plt_i = pg.PlotItem(labels={ 'left': ('slits', 'degrees'), 'bottom': ('Kin. Energy', 'eV') }) self.base_wd.plt_iv = pg.ImageView(view=self.base_wd.plt_i) self.base_wd.setWidget(self.base_wd.plt_iv) self.base_wd.show()
class MainWindow(QMainWindow): CURVATURE, THICKNESS, MATERIAL, SEMIDIAM = range(4) count = 0 def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.seq_model = seq.SequentialModel() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() file = bar.addMenu("File") file.addAction("New") file.addAction("Open...") file.addSeparator() file.addAction("Save") file.addAction("Save As...") file.triggered[QAction].connect(self.file_action) view = bar.addMenu("View") view.addAction("Table") view.addAction("Lens View") view.triggered[QAction].connect(self.view_action) wnd = bar.addMenu("Window") wnd.addAction("Cascade") wnd.addAction("Tiled") wnd.triggered[QAction].connect(self.window_action) self.setWindowTitle("Ray Optics") self.show() self.open_file( "/Users/Mike/Developer/PyProjects/ray-optics/codev/test/ag_dblgauss.seq" ) def file_action(self, q): if q.text() == "New": MainWindow.count = MainWindow.count + 1 sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("subwindow" + str(MainWindow.count)) self.mdi.addSubWindow(sub) sub.show() if q.text() == "Open...": options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "CODE V Files (*.seq)", options=options) if fileName: logging.debug("open file: %s", fileName) self.open_file(fileName) def open_file(self, file_name): self.cur_filename = file_name self.is_changed = True cvp.read_lens(self.seq_model, file_name) self.create_lens_table() self.create_2D_lens_view() def view_action(self, q): print("view triggered") if q.text() == "Table": MainWindow.count = MainWindow.count + 1 self.create_lens_table() if q.text() == "Lens View": MainWindow.count = MainWindow.count + 1 self.create_2D_lens_view() def window_action(self, q): print("window triggered") if q.text() == "Cascade": self.mdi.cascadeSubWindows() if q.text() == "Tiled": self.mdi.tileSubWindows() def create_lens_table(self): # construct the top level widget widget = QWidget() # construct the top level layout layout = QVBoxLayout(widget) tableView = QTableView() tableView.setAlternatingRowColors(True) # table selection change # self.tableView.doubleClicked.connect(self.on_click) # Add table to box layout layout.addWidget(tableView) # set the layout on the widget widget.setLayout(layout) sub = self.mdi.addSubWindow(widget) sub.setWindowTitle("Surface Data Table") model = self.createSurfaceModel(self) tableView.setModel(model) for s in range(len(self.seq_model.surfs)): self.addSurface(model, self.seq_model.list_surface_and_gap(s)) tableView.setMinimumWidth(tableView.horizontalHeader().length() + tableView.horizontalHeader().height()) # The following line should work but returns 0 # tableView.verticalHeader().width()) sub.show() def create_2D_lens_view(self): self.scene2d = QGraphicsScene() self.create_element_model(self.scene2d) self.create_ray_model(self.scene2d) self.scene2d.setBackgroundBrush(QColor(237, 243, 254)) # light blue sceneRect2d = self.scene2d.sceneRect() print("Scene rect1:", sceneRect2d.width() / sceneRect2d.height(), sceneRect2d.x(), sceneRect2d.y(), sceneRect2d.width(), sceneRect2d.height()) # construct the top level widget widget = QWidget() # construct the top level layout layout = QVBoxLayout(widget) # set the layout on the widget widget.setLayout(layout) sub = self.mdi.addSubWindow(widget) sub.setWindowTitle("2D Lens View") view_width = 600 view_ht = 400 view_ratio = view_width / view_ht sub.setGeometry(100, 50, view_width, view_ht) self.gview2d = QGraphicsView(self.scene2d) # self.gview2d.setGeometry(100, 50, view_width, view_ht) scene_ratio = sceneRect2d.width() / sceneRect2d.height() oversize_fraction = 1.2 if scene_ratio > view_ratio: view_scale = view_width / (oversize_fraction * sceneRect2d.width()) else: view_scale = view_ht / (oversize_fraction * sceneRect2d.height()) print(view_ratio, scene_ratio, view_scale) frame_before = self.gview2d.frameGeometry() print("Frame before:", frame_before.x(), frame_before.y(), frame_before.width(), frame_before.height()) self.gview2d.scale(view_scale, view_scale) layout.addWidget(self.gview2d) sub.show() def createSurfaceModel(self, parent): model = QStandardItemModel(0, 4, parent) model.setHeaderData(self.CURVATURE, Qt.Horizontal, "Curvature") model.setHeaderData(self.THICKNESS, Qt.Horizontal, "Thickness") model.setHeaderData(self.MATERIAL, Qt.Horizontal, "Material") model.setHeaderData(self.SEMIDIAM, Qt.Horizontal, "Semi-Diameter") return model def create_element_model(self, gscene): clut = rgbt.RGBTable(filename='gui/red_blue64.csv', data_range=[10.0, 100.]) ele_model = ele.ElementModel() ele_model.elements_from_sequence(self.seq_model) pen = QPen() pen.setCosmetic(True) for e in ele_model.elements: poly = e.shape() polygon = QPolygonF() for p in poly: polygon.append(QPointF(p[0], p[1])) gpoly = QGraphicsPolygonItem() gpoly.setPolygon(polygon) # set element color based on V-number gc = float(e.medium.glass_code()) vnbr = round(100.0 * (gc - int(gc)), 3) ergb = clut.get_color(vnbr) gpoly.setBrush(QColor(*ergb)) gpoly.setPen(pen) t = e.tfrm[1] gpoly.setPos(QPointF(t[2], -t[1])) gscene.addItem(gpoly) def create_ray_model(self, gscene, start_surf=1): tfrms = self.seq_model.compute_global_coords(start_surf) rayset = self.seq_model.trace_boundary_rays() start_offset = 0.1 * gscene.sceneRect().width() if abs(tfrms[0][1][2]) > start_offset: tfrms[0] = self.seq_model.shift_start_of_rayset( rayset, start_offset) pen = QPen() pen.setCosmetic(True) for rays in rayset: poly1 = [] for i, r in enumerate(rays[3][0][0:]): rot, trns = tfrms[i] p = rot.dot(r[0]) + trns # print(i, r[0], rot, trns, p) poly1.append(QPointF(p[2], -p[1])) poly2 = [] for i, r in enumerate(rays[4][0][0:]): rot, trns = tfrms[i] p = rot.dot(r[0]) + trns # print(i, r[0], rot, trns, p) poly2.append(QPointF(p[2], -p[1])) poly2.reverse() poly1.extend(poly2) polygon = QPolygonF() for p in poly1: polygon.append(p) gpoly = QGraphicsPolygonItem() gpoly.setPolygon(polygon) gpoly.setBrush(QColor(254, 197, 254, 64)) # magenta, 25% gpoly.setPen(pen) gscene.addItem(gpoly) def addSurface(self, model, surf_gap): itemList = [] for i in range(4): item = QStandardItem() item.setData(surf_gap[i], Qt.DisplayRole) itemList.append(item) model.appendRow(itemList)
class MainWindow(QMainWindow): lampset = pyqtSignal(int, str, object) master_change = pyqtSignal(int) faderset = pyqtSignal(int, int) freq = 1 add_fac = 0.01 def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setFont(QFont('Sans Serif', 12)) self.setWindowTitle("Open-light-control") self.global_timer = QTimer(self) self.global_timer.timeout.connect(self.update_error_log) self.global_timer.start(500) if GlobalVar.serial_enable: self.serial_thread = SerialThread() self.serial_thread.keystroke.connect(self.map_keys) self.serial_thread.fadermove.connect(self.map_faders) self.serial_thread.encodermove.connect(self.map_encoders) self.faderset.connect(self.serial_thread.set_fader) self.serial_thread.start() self.abstract_thread = AbstractThread() self.lampset.connect(self.abstract_thread.set_lamp) self.lampset.connect(self.set_output) self.master_change.connect(self.abstract_thread.dmx_thread.set_master) self.abstract_thread.start() sortact = QAction('Sort', self) sortact.triggered.connect(self.sort) quitact = QAction('Quit', self) quitact.setShortcut('Ctrl+Q') quitact.triggered.connect(self.close) self.freezelabel = QLabel("Output freezed") # self.freezelabel.setDisabled(True) self.freezeact = QAction('Freeze Output', self) self.freezeact.setCheckable(True) self.freezeact.toggled.connect(self.toggle_freeze) self.freezeact.setShortcut('Ctrl+F') self.freezeact.toggle() self.menubar = self.menuBar() self.fileMenu = self.menubar.addMenu('File') self.fileMenu.addAction(quitact) self.toolsMenu = self.menubar.addMenu('Tools') self.toolsMenu.addAction(sortact) self.toolsMenu.addAction(self.freezeact) self.windowMenu = self.menubar.addMenu('Windows') self.menubar.setCornerWidget(self.freezelabel) self.statusbar = self.statusBar() # .showMessage('Ready') self.statusbarhistory = QLabel() self.statusbarhistory.setMinimumWidth(100) self.statuslinebar = QLineEdit() self.statuslinebar.setMinimumWidth(100) self.statuslinebar.returnPressed.connect(self.exec_program_line) self.statusbar.layout().addWidget(self.statusbarhistory) self.statusbar.layout().addWidget(self.statuslinebar, Qt.AlignRight) #self.statusbar.addWidget(self.statuslinebar) #self.statusbar.setLayout(self.statusbar_layout) # self.cuelist_thread = CuelistThread() # self.cuelist_thread.lampset.connect(lambda x,y,z: self.lampset.emit(x, y, z)) #self.cuelist_thread.go() #self.cuelist_thread.start() #self.cuelist_thread.quit() self.chase_timer = QTimer(self) self.chase_timer.timeout.connect(self.chase_send) self.mdi = QMdiArea() for cuelist in GlobalVar.cuelist_dict.keys(): setattr(self, "{0:s}_cue_thread".format(cuelist), CuelistThread()) getattr(self, "{0:s}_cue_thread".format(cuelist)).lampset.connect( self.lampset_relay) getattr(self, "{0:s}_cue_thread".format(cuelist)).set_cuelist( str(cuelist)) ### Essential subwindows self.create_error_log() self.create_master_fader() self.create_output() ### Serial Monitors self.create_encoders() self.create_faders() self.create_keys() ### Extras self.create_chase_test() self.create_color() # self.create_color_bar() self.create_xy_pad() self.create_pb_stueck() # self.create_pp_stueck() ### Show all self.master_slid_sub.show() self.output_sub.show() self.stueck_sub.show() # self.stock_sub.show() self.color_sub.show() # self.color1_sub.show() self.setCentralWidget(self.mdi) #pdb.set_trace() # self.change_led() ### Essential Functions def closeEvent(self, event): close = QMessageBox.question(self, "QUIT", "Are you sure want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if close == QMessageBox.Yes: event.accept() else: event.ignore() @pyqtSlot(bool) def toggle_freeze(self, toggled): if toggled: self.freezelabel.setText("Output freezed") GlobalVar.output_freeze[0] = True else: self.freezelabel.setText("") GlobalVar.output_freeze[0] = False self.abstract_thread.send_artnet_all_sock_relay() def sort(self): self.mdi.tileSubWindows() @pyqtSlot() def update_error_log(self): self.error_text.setPlainText("\n".join(GlobalVar.error_log_global)) self.error_text.verticalScrollBar().setValue( self.error_text.verticalScrollBar().maximum()) # @pyqtSlot(str) def key_pressed(self, key, pressed=True): try: if pressed: if GlobalVar.key_mapping[key][0] == "pad": if GlobalVar.key_mapping.get(key, ["", False])[3]: self.statuslinebar.insert( GlobalVar.key_mapping.get(key, [""])[2]) else: if GlobalVar.key_mapping.get(key, [""])[2] == "Enter": self.statuslinebar.returnPressed.emit() elif GlobalVar.key_mapping[key][0] == "cuelist": if GlobalVar.key_mapping[key][2] == "go": self.cuelist_go( getattr( self, "{0:s}_cue_thread)".format( GlobalVar.key_mapping[key][1]))) elif GlobalVar.key_mapping[key][0] == "command": getattr(self, GlobalVar.key_mapping[key][1])() else: raise (KeyError) except: GlobalVar.error_log_global.append( "Key press failed: num {0:s}".format(key)) def exec_program_line(self): text = self.statuslinebar.text() clear = False try: if "/" in text: nums = text.split("*")[0].split("c")[0] number = nums.split("/") numbers = "+".join([ str(x) for x in range(int(number[0]), int(number[1]) + 1) ]) text = numbers + text[text.index(nums) + len(nums):] except: GlobalVar.error_log_global.append( "Programmer Error: unkown command") try: if "*" in text: try: dims = text.split("*")[1] if len(text.split("*")) == 3: dim = 100 elif dims == "": dim = 0 clear = True elif dims == "0": dim = int(dims) else: dim = int(dims) if dim > 100: raise ValueError except ValueError: GlobalVar.error_log_global.append( "Programmer Error: value out of range") self.statuslinebar.clear() return if text.split("*")[0] == "": lamps = self.statusbarhistory.text() else: lamps = text.split("*")[0] if "+" in lamps: lamp = lamps.split("+") else: lamp = [lamps] for i in lamp: if clear: try: GlobalVar.in_use_programmer.pop(i) except KeyError: pass else: GlobalVar.in_use_programmer[i] = dim self.lampset.emit(int(i), "Dimmer", dim) elif "c" in text: if "+" in text.split("c")[0]: lamp = text.split("c")[0].split("+") else: lamp = [text.split("c")[0]] value = text.split("c")[1] if "r" in value: setting = "Red" set_value = int(value.split("r")[1]) elif "g" in value: setting = "Green" set_value = int(value.split("g")[1]) elif "b" in value: setting = "Blue" set_value = int(value.split("b")[1]) else: setting = "Color" set_value = value.replace("(", "").replace(")", "").split(",") if setting == "Color": for i in lamp: try: dim = GlobalVar.in_use_programmer[i] except KeyError: dim = 0 GlobalVar.in_use_programmer[i] = dim self.lampset.emit(int(i), "Red", int(set_value[0])) self.lampset.emit(int(i), "Green", int(set_value[1])) self.lampset.emit(int(i), "Blue", int(set_value[2])) else: for i in lamp: try: dim = GlobalVar.in_use_programmer[i] except KeyError: dim = 0 GlobalVar.in_use_programmer[i] = dim self.lampset.emit(int(i), setting, set_value) else: GlobalVar.error_log_global.append( "Programmer Error: unkown command") except: GlobalVar.error_log_global.append( "Programmer Error: unkown command") self.statusbarhistory.setText("+".join( GlobalVar.in_use_programmer.keys())) self.statuslinebar.clear() ## build / exec func def create_error_log(self): self.error_text = QTextEdit() self.error_text.setReadOnly(1) self.create_sub_area("error", "Error Log", [[self.error_text, 0, 0]]) def create_output(self): self.output_layout = QGridLayout() self.output_layout.addWidget(QLabel("Num"), 0, 0) self.output_layout.addWidget(QLabel("Dimmer"), 0, 1) self.output_layout.addWidget(QLabel("Color"), 0, 2) # self.output_layout.addWidget(QLabel("Gobo"),0,3) # self.output_layout.addWidget(QLabel("Pan"),0,4) # self.output_layout.addWidget(QLabel("Tilt"),0,5) line = 1 for num in list(GlobalVar.nr_to_typ.keys()): self.output_layout.addWidget(QLabel(str(num)), line, 0) if GlobalVar.typ_to_func[GlobalVar.nr_to_typ[num]]['Dimmer']: setattr(self, "output_{0:d}_Dimmer".format(num), QLabel('0')) self.output_layout.addWidget( getattr(self, "output_{0:d}_Dimmer".format(num)), line, 1) if not GlobalVar.typ_to_func[ GlobalVar.nr_to_typ[num]]['Color'] == False: setattr(self, "output_{0:d}_Color".format(num), QLabel('(0,0,0)')) self.output_layout.addWidget( getattr(self, "output_{0:d}_Color".format(num)), line, 2) # if not GlobalVar.typ_to_func[GlobalVar.nr_to_typ[num]]['Gobo'] == False: # setattr(self,"output_{0:d}_Gobo".format(num),QLabel('Open')) # self.output_layout.addWidget(getattr(self,"output_{0:d}_Gobo".format(num)),line,3) # if not GlobalVar.typ_to_func[GlobalVar.nr_to_typ[num]]['Pan'] == False: # setattr(self,"output_{0:d}_Pan".format(num),QLabel('Open')) # self.output_layout.addWidget(getattr(self,"output_{0:d}_Pan".format(num)),line,4) # if not GlobalVar.typ_to_func[GlobalVar.nr_to_typ[num]]['Tilt'] == False: # setattr(self,"output_{0:d}_Tilt".format(num),QLabel('Open')) # self.output_layout.addWidget(getattr(self,"output_{0:d}_Tilt".format(num)),line,4) line += 1 col_count = self.output_layout.columnCount() for col in range(col_count): self.output_layout.setColumnMinimumWidth(col, 70) self.output_layout.setColumnMinimumWidth(2, 100) self.output_widget = QWidget() self.output_widget.setLayout(self.output_layout) self.output_scroll = QScrollArea() self.output_scroll.setWidget(self.output_widget) self.output_sub = QMdiSubWindow() self.output_sub.setWidget(self.output_scroll) self.output_sub.setWindowTitle('Output') self.output_sub.setFont(QFont('Sans Serif', 10)) self.mdi.addSubWindow(self.output_sub) @pyqtSlot(int, str, object) def set_output(self, num, setting, value): try: if any(setting == x for x in ["Red", "Green", "Blue"]): self.text = getattr(self, "output_{0:d}_Color".format(num)).text() text = self.text.replace("(", "") text = text.replace(")", "") text = text.split(",") if setting == "Red": text[0] = str(round(value, 1)) elif setting == "Green": text[1] = str(round(value, 1)) elif setting == "Blue": text[2] = str(round(value, 1)) getattr( self, "output_{0:d}_Color".format(num)).setText("(" + ",".join(text) + ")") else: getattr(self, "output_{0:d}_{1:s}".format(num, setting)).setText( str(value)) except: pass def create_master_fader(self): master_list = [] self.master_slid = QSlider() self.master_slid.setMinimum(0) self.master_slid.setMaximum(100) self.master_slid.setStyleSheet(GlobalVar.slider_stylesheet) self.master_slid_max_button = QPushButton("Max") self.master_slid_min_button = QPushButton("Min") self.master_slid_resend_button = QPushButton("Resend") self.master_slid.valueChanged.connect(self.master_slid_fader) self.master_slid_max_button.clicked.connect(self.set_master_max) self.master_slid_min_button.clicked.connect(self.set_master_min) self.master_slid_resend_button.clicked.connect( self.abstract_thread.send_artnet_all_sock_relay) width_edit = QLineEdit() width_edit.setPlaceholderText(str(self.master_slid.size().width())) height_edit = QLineEdit() height_edit.setPlaceholderText(str(self.master_slid.size().height())) width_edit.returnPressed.connect(lambda: self.master_slid.resize( QSize(int(width_edit.text()), self.master_slid.size().height()))) height_edit.returnPressed.connect(lambda: self.master_slid.resize( QSize(self.master_slid.size().width(), int(height_edit.text())))) master_list.append([self.master_slid, 0, 0, 2, 1]) master_list.append([self.master_slid_max_button, 0, 1]) master_list.append([self.master_slid_min_button, 1, 1]) master_list.append([self.master_slid_resend_button, 0, 3]) master_list.append([width_edit, 0, 2]) master_list.append([height_edit, 1, 2]) self.create_sub_area("master_slid", "Master", master_list) @pyqtSlot(int) def master_slid_fader(self, i): self.master_change.emit(i) def set_master_max(self): self.master_slid.setValue(self.master_slid.maximum()) def set_master_min(self): self.master_slid.setValue(self.master_slid.minimum()) ### Serial Monitor Functions def create_keys(self): keys_list = [] for row in range(GlobalVar.rows): for col in range(GlobalVar.cols): num = (row * GlobalVar.cols) + col setattr( self, "key" + str(num), QPushButton( GlobalVar.key_mapping.get(str(num), ["", "None"])[1])) # getattr(self,"keys"+str(num)).setCheckable(True) getattr(self, "key" + str(num)).setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) getattr(self, "key" + str(num)).pressed.connect( partial(self.key_pressed, str(num))) keys_list.append([getattr(self, "key" + str(num)), row, col]) self.create_sub_area("keys", "Buttons", keys_list, width=85) def create_faders(self): faders_list = [] for fader in range(GlobalVar.faders): setattr(self, "fader" + str(fader), QLabel()) getattr(self, "fader" + str(fader)).setAlignment(Qt.AlignCenter) getattr(self, "fader" + str(fader)).setText('0') faders_list.append([getattr(self, "fader" + str(fader)), 0, fader]) self.create_sub_area("faders", "Faders", faders_list, width=50) def create_encoders(self): encoder_list = [] for encoder in range(GlobalVar.encoders): setattr(self, "encoder" + str(encoder), QLabel()) getattr(self, "encoder" + str(encoder)).setAlignment(Qt.AlignCenter) getattr(self, "encoder" + str(encoder)).setText('0') encoder_list.append( [getattr(self, "encoder" + str(encoder)), 0, encoder]) self.create_sub_area("encoders", "Encoders", encoder_list, width=30) @pyqtSlot(str, bool) def map_keys(self, key, pressed): try: self.key_pressed(key, pressed) except NameError: print("button {0:s} not found!".format(key)) @pyqtSlot(str, int) def map_faders(self, fader, value): try: fader_to_set = GlobalVar.fader_mapping[fader] + "_slid" getattr(self, fader_to_set).setValue( (value / 1023) * getattr(self, fader_to_set).maximum()) except KeyError: pass @pyqtSlot(str, int) def map_encoders(self, encoder, value): try: cur = int(getattr(self, "encoder" + encoder).text()) getattr(self, "encoder" + encoder).setText(str(cur - value)) except NameError: print("encoder {0:s} not found!".format(encoder)) ### Extra Functions def create_xy_pad(self): self.xy_pad = XY_Pad() self.xy_pad.position_changed.connect(self.xy_pad_sola_map) self.create_sub_area("xy_pad", "XY Pad", [[self.xy_pad, 0, 0]]) def create_chase_test(self): chase_list = [] self.chase_edit = QLineEdit() self.chase_edit.returnPressed.connect(self.chase_update) chase_list.append([self.chase_edit, 0, 0]) self.chase_abs_edit = QLineEdit() self.chase_abs_edit.returnPressed.connect(self.chase_abs_update) chase_list.append([self.chase_abs_edit, 1, 0]) self.chase_start = QPushButton("Start") self.chase_start.clicked.connect(lambda: self.chase_timer.start(10)) chase_list.append([self.chase_start, 0, 1]) self.chase_stop = QPushButton("Stop") self.chase_stop.clicked.connect(lambda: self.chase_timer.stop()) chase_list.append([self.chase_stop, 1, 1]) self.create_fader("led_ma", "LED Master", chase_list, 0, 2) self.create_sub_area("chase_test", "LED Chase", chase_list, width=70) @pyqtSlot(int) def led_ma_slid_fader(self, sli): for i in range(110, 116): self.lampset.emit(i, 'Dimmer', sli) @pyqtSlot(float, float) def xy_pad_sola_map(self, x, y): self.lampset.emit(100, "Pan", x * 100) self.lampset.emit(100, "Tilt", y * 100) def chase_update(self): temp_freq = float(self.chase_edit.text()) self.freq = temp_freq def chase_abs_update(self): self.add_fac = float(self.chase_abs_edit.text()) def chase_send(self): for i in [110, 111, 113, 115, 114, 112]: #i = 100 #if True: color_tup = colorsys.hsv_to_rgb( abs( math.sin(self.freq * time.time() + (self.add_fac * (i - 110)))), 1, 1) # 0))),1,1) self.lampset.emit(i, "Red", color_tup[0] * 255) self.lampset.emit(i, "Blue", color_tup[1] * 255) self.lampset.emit(i, "Green", color_tup[2] * 255) def create_color(self): self.color_dia0 = NewColorDialog(parent=self) # self.color_dia0.setOption(2) self.color_dia0.currentColorChanged.connect(self.test_colors) self.create_sub_area("color", "Color Changer LED", [[self.color_dia0, 0, 0]]) def create_color_bar(self): self.color_dia1 = ColorDialog(parent=self) # self.color_dia0.setOption(2) self.color_dia1.currentColorChanged.connect(self.test_colors1) self.create_sub_area("color1", "Color Changer Bar", [[self.color_dia1, 0, 0]]) @pyqtSlot(QColor) def test_colors(self, col): for i in range(110, 116): # 110,116 #i=100 #if True: self.lampset.emit(i, 'Red', col.red()) self.lampset.emit(i, 'Green', col.green()) self.lampset.emit(i, 'Blue', col.blue()) @pyqtSlot(QColor) def test_colors1(self, col): for i in range(20, 22): #i=100 #if True: self.lampset.emit(i, 'Red', col.red()) self.lampset.emit(i, 'Green', col.green()) self.lampset.emit(i, 'Blue', col.blue()) def faders_next_com(self): if GlobalVar.curr_page + 1 < len(GlobalVar.fader_map): next_page = GlobalVar.curr_page + 1 else: next_page = 0 for i in range(GlobalVar.faders): GlobalVar.fader_map[GlobalVar.curr_page][i] = getattr( self, "fader_{0:d}_ma_slid".format(i)).value() getattr(self, "fader_{0:d}_ma_slid".format(i)).setValue( GlobalVar.fader_map[next_page][i]) self.faderset.emit(i, GlobalVar.fader_map[next_page][i]) GlobalVar.curr_page = next_page def create_pb_stueck(self): stueck_list = [] self.create_fader("pub", "Pub", stueck_list, 0, 0) self.create_fader("back", "Backlight", stueck_list, 0, 1) self.create_fader("grund", "Grundlicht", stueck_list, 0, 2) self.create_fader("spot", "Spot", stueck_list, 0, 3) self.create_sub_area("stueck", "PB Stück", stueck_list, width=70) @pyqtSlot(int) def pub_slid_fader(self, i): self.lampset.emit(10, "Dimmer", i) self.lampset.emit(19, "Dimmer", i) self.lampset.emit(7, "Dimmer", i) @pyqtSlot(int) def back_slid_fader(self, i): self.lampset.emit(44, "Dimmer", i) @pyqtSlot(int) def grund_slid_fader(self, i): self.lampset.emit(22, "Dimmer", i) self.lampset.emit(21, "Dimmer", i) self.lampset.emit(17, "Dimmer", i) self.lampset.emit(14, "Dimmer", i) @pyqtSlot(int) def spot_slid_fader(self, i): self.lampset.emit(18, "Dimmer", i) def create_pp_stueck(self): stock_list = [] self.create_fader("L", "L", stock_list, 0, 0) self.create_fader("R", "R", stock_list, 0, 1) self.create_fader("led_d", "LED Dim", stock_list, 0, 2) self.create_fader("bar_d", "Bar Dim R", stock_list, 0, 3) self.create_sub_area("stock", "PP Stück", stock_list, width=70) @pyqtSlot(int) def L_slid_fader(self, i): self.lampset.emit(1, "Dimmer", i) @pyqtSlot(int) def R_slid_fader(self, i): self.lampset.emit(2, "Dimmer", i) @pyqtSlot(int) def led_d_slid_fader(self, i): self.lampset.emit(10, "Dimmer", i) @pyqtSlot(int) def bar_d_slid_fader(self, i): self.lampset.emit(20, "Dimmer", i) self.lampset.emit(21, "Dimmer", i) def create_sub_area(self, name, title, wid_list, width=None): setattr(self, name + "_layout", QGridLayout()) for wid in wid_list: if len(wid) == 3: wid.append(1) wid.append(1) getattr(self, name + "_layout").addWidget(wid[0], wid[1], wid[2], wid[3], wid[4]) if width: self.col_count = getattr(self, name + "_layout").columnCount() for col in range(self.col_count): getattr(self, name + "_layout").setColumnMinimumWidth(col, width) setattr(self, name + "_widget", QWidget()) getattr(self, name + "_widget").setLayout(getattr(self, name + "_layout")) setattr(self, name + "_sub", QMdiSubWindow()) getattr(self, name + "_sub").setWidget(getattr(self, name + "_widget")) getattr(self, name + "_sub").setWindowTitle(title) self.mdi.addSubWindow(getattr(self, name + "_sub")) getattr(self, name + "_sub").hide() setattr(self, name + "act", QAction(title, self)) getattr(self, name + "act").triggered.connect( lambda: getattr(self, name + "_sub").show()) self.windowMenu.addAction(getattr(self, name + "act")) # setattr(self,name+"_scroll",QScrollArea()) # getattr(self,name+"_scroll").setWidget(getattr(self,name+"_widget")) def create_fader(self, name, label, liste, start, start1, fad_max=100): setattr(self, name + "_slid", QSlider()) getattr(self, name + "_slid").setMinimumHeight(200) getattr(self, name + "_slid").setStyleSheet(GlobalVar.slider_stylesheet) getattr(self, name + "_slid").setMinimum(0) getattr(self, name + "_slid").setMaximum(fad_max) getattr(self, name + "_slid").valueChanged.connect( getattr(self, name + "_slid_fader")) liste.append([QLabel(label), start, start1]) liste.append([getattr(self, name + "_slid"), start + 1, start1]) @pyqtSlot(int, str, object) def lampset_relay(self, x, y, z): self.lampset.emit(x, y, z) def cuelist_go(self, cuelist): if cuelist.isRunning(): cuelist.go() else: cuelist.start() def unset_pub(self): if GlobalVar.fader_mapping["2"] == "pub": GlobalVar.fader_mapping["2"] = "spot" else: GlobalVar.fader_mapping["2"] = "pub" def change_led(self): self.lampset.emit(10, "Red", 255) self.lampset.emit(10, "Green", 191) self.lampset.emit(10, "Blue", 62) self.lampset.emit(11, "Red", 255) self.lampset.emit(20, "Red", 255) self.lampset.emit(20, "Green", 147) self.lampset.emit(20, "Blue", 52) self.lampset.emit(21, "Red", 255) self.lampset.emit(21, "Green", 147) self.lampset.emit(21, "Blue", 52)
class List_Mode_Viewer(QMainWindow): sync=False lis=False calibration=False def __init__(self): super().__init__() self.setWindowTitle('List Mode Viewer') self.font=QFont() self.font.setPointSize(12) self.size_policy=QSizePolicy.Expanding self.menu() self.geometry() self.showMaximized() # self.popup() self.show() def menu(self): self.menuFile=self.menuBar().addMenu('&File') self.load_new=QAction('&Load New Data') self.load_new.triggered.connect(self.loading) self.load_new.setShortcut('CTRL+N') self.save_file=QAction('&Save Spectrum') self.save_file.triggered.connect(self.save_spectrum) self.save_file.setShortcut('CTRL+S') self.save_file.setEnabled(False) self.save_roi=QAction('&Save ROI') self.save_roi.triggered.connect(self.save_roi_csv) self.save_roi.setEnabled(False) self.menuView=self.menuBar().addMenu('&View') self.view_pop=QAction('&Show Tools') self.view_pop.triggered.connect(self.popup_) self.view_pop.setShortcut('CTRL+U') self.view_pop.setEnabled(False) self.view_all=QAction('&Show Plots') self.view_all.triggered.connect(self.initiate) self.view_all.setShortcut('CTRL+V') self.view_variation=QAction('&Pulse Deviation') self.view_variation.triggered.connect(self.deviation) self.view_roi_arrive=QAction('&ROI Arrival Times') self.view_roi_arrive.triggered.connect(self.ROI_Arrivals) self.menuView.addActions([self.view_pop,self.view_all,self.view_variation, self.view_roi_arrive]) self.menuFile.addActions([self.load_new,self.save_file,self.save_roi]) def geometry(self): # self.central_widget=QWidget() self.region1_plot=QWidget() self.region1_figure=Figure() self.region1_canvas=FigureCanvas(self.region1_figure) self.region1_toolbar=NavigationToolbar(self.region1_canvas,self) layout=QVBoxLayout() layout.addWidget(self.region1_toolbar) layout.addWidget(self.region1_canvas) self.region1_plot.setLayout(layout) self.region1_ax=self.region1_canvas.figure.subplots() self.region2_plot=QWidget() self.region2_figure=Figure() self.region2_canvas=FigureCanvas(self.region2_figure) self.region2_toolbar=NavigationToolbar(self.region2_canvas,self) layout=QVBoxLayout() layout.addWidget(self.region2_toolbar) layout.addWidget(self.region2_canvas) self.region2_plot.setLayout(layout) self.region2_ax=self.region2_canvas.figure.subplots() self.region2_ax.set_title('Region 2') self.total_plot=QWidget() self.total_figure=Figure() self.total_canvas=FigureCanvas(self.total_figure) self.total_toolbar=NavigationToolbar(self.total_canvas,self) layout=QVBoxLayout() layout.addWidget(self.total_toolbar) layout.addWidget(self.total_canvas) self.total_plot.setLayout(layout) self.total_ax=self.total_canvas.figure.subplots() self.total_ax.set_title('Total') self.time_plot=QWidget() self.time_figure=Figure() self.time_canvas=FigureCanvas(self.time_figure) self.time_toolbar=NavigationToolbar(self.time_canvas,self) layout=QVBoxLayout() layout.addWidget(self.time_toolbar) layout.addWidget(self.time_canvas) self.time_plot.setLayout(layout) self.time_ax=self.time_canvas.figure.subplots() self.time_ax.set_title('Time') self.setup() def setup(self): sub1=QMdiSubWindow() sub1.setWidget(self.region1_plot) sub1.setWindowTitle('Region 1 Plot') sub2=QMdiSubWindow() sub2.setWidget(self.region2_plot) sub2.setWindowTitle('Region 2 Plot') sub3=QMdiSubWindow() sub3.setWidget(self.total_plot) sub3.setWindowTitle('Total Plot') sub4=QMdiSubWindow() sub4.setWidget(self.time_plot) sub4.setWindowTitle('Time Plot') self.mdi=QMdiArea() self.mdi.addSubWindow(sub1) self.mdi.addSubWindow(sub2) self.mdi.addSubWindow(sub3) self.mdi.addSubWindow(sub4) self.mdi.tileSubWindows() self.setCentralWidget(self.mdi) def loading(self): self.loader=QWidget() self.loader.setWindowFlags(Qt.WindowStaysOnTopHint) self.sync_label=QLabel('Sync Pulse Location: ') self.sync_label.setSizePolicy(self.size_policy,self.size_policy) self.sync_label.setFont(self.font) self.sync_location=QPushButton('Browse') self.sync_location.setSizePolicy(self.size_policy,self.size_policy) self.sync_location.setFont(self.font) self.sync_location.clicked.connect(self.sync_browse) self.detector_label=QLabel('Detector List Mode Data: ') self.detector_label.setSizePolicy(self.size_policy,self.size_policy) self.detector_label.setFont(self.font) self.detector_location=QPushButton('Browse') self.detector_location.setSizePolicy(self.size_policy,self.size_policy) self.detector_location.setFont(self.font) self.detector_location.clicked.connect(self.detector_browse) self.calibration_label=QLabel('Calibration:(Optional)') self.calibration_label.setSizePolicy(self.size_policy,self.size_policy) self.calibration_label.setFont(self.font) self.calibration_location=QPushButton('Browse') self.calibration_location.setSizePolicy(self.size_policy, self.size_policy) self.calibration_location.setFont(self.font) self.calibration_location.clicked.connect(self.calibration_browse) self.process_new=QPushButton('Process') self.process_new.setSizePolicy(self.size_policy,self.size_policy) self.process_new.setFont(self.font) self.process_new.clicked.connect(self.processing_new) self.process_new.setEnabled(False) layout=QGridLayout() layout.addWidget(self.sync_label,0,0) layout.addWidget(self.sync_location,0,1) layout.addWidget(self.detector_label,1,0) layout.addWidget(self.detector_location,1,1) layout.addWidget(self.calibration_label,2,0) layout.addWidget(self.calibration_location,2,1) layout.addWidget(self.process_new,3,0,1,2) self.loader.setLayout(layout) self.loader.setWindowTitle('Load New List Mode') self.loader.show() def sync_browse(self): self.sync_filename=QFileDialog.getOpenFileName(self, 'Sync File Location',"", 'Comma Seperated File (*.csv);;Text File (*.txt)') if self.sync_filename[0]!="": self.sync=True self.sync_location.setStyleSheet("background-color: green") if self.lis==True: self.process_new.setEnabled(True) def detector_browse(self): self.list_filename=QFileDialog.getOpenFileName(self, 'Listmode File Location',"", 'Comma Seperated File (*.csv);;Text File (*.txt)') if self.list_filename[0]!="": self.lis=True self.detector_location.setStyleSheet("background-color: green") if self.sync==True: self.process_new.setEnabled(True) def calibration_browse(self): self.calib_filename=QFileDialog.getOpenFileName(self, 'calibration File Location',"", 'Text File (*.txt);;Comma Seperated File (*.csv)') if self.calib_filename[0]!='': self.calibration=True f=open(self.calib_filename[0],'r') data=f.readlines() f.close() self.calibration_data=[] for i in range(len(data)): self.calibration_data.append(float(data[i])) del data del f def processing_new(self): try: self.loader.close() except: True self.setWindowTitle(self.list_filename[0]) self.save_file.setEnabled(True) self.save_roi.setEnabled(True) self.popup_() self.list_mode_processor=List_Mode() s=time.time() self.sync_time,sync_channel=Conversion.convert(self.sync_filename[0]) del sync_channel self.list_time,self.list_channel=Conversion.convert(self.list_filename[0]) print('Imported and conveted in {:.2f}s'.format(time.time()-s)) winsound.MessageBeep() delt=(self.sync_time[2]-self.sync_time[1]) #set the maximum value for the end time and start times maxe=int((self.list_time[-1]-self.list_time[0])*1e-6) self.end_time.setMaximum(maxe) self.end_time.setValue(maxe) self.start_time.setMaximum(maxe-1) self.start_time.setValue(0) self.offset.setMaximum(int(delt-self.duty_cycle.value()/100*delt)) self.offset.setMinimum(int(-(delt*self.duty_cycle.value()/100)*.5)) self.view_pop.setEnabled(True) self.updater() def popup_(self): self.popup=QWidget() self.popup.setWindowTitle('View Controls') self.popup.setSizePolicy(self.size_policy,self.size_policy) self.popup.setFont(self.font) start_label=QLabel('Start Time: s') start_label.setSizePolicy(self.size_policy, self.size_policy) start_label.setFont(self.font) self.s_label=start_label self.start_time=QSlider(Qt.Horizontal) self.start_time.setSizePolicy(self.size_policy,self.size_policy) self.start_time.setFont(self.font) self.start_time.setMinimum(0) self.start_time.setMaximum(100) self.start_time.setSingleStep(1) self.start_time.setValue(10) self.start_time.setTickInterval(100) self.start_time.setTickPosition(QSlider.TicksBelow) self.start_time.valueChanged.connect(self.start_updated) end_label=QLabel('End Time: s') end_label.setSizePolicy(self.size_policy, self.size_policy) end_label.setFont(self.font) self.e_label=end_label self.end_time=QSlider(Qt.Horizontal) self.end_time.setSizePolicy(self.size_policy,self.size_policy) self.end_time.setFont(self.font) self.end_time.setMinimum(1) self.end_time.setMaximum(100) self.end_time.setSingleStep(1) self.end_time.setValue(10) self.end_time.setTickInterval(100) self.end_time.setTickPosition(QSlider.TicksBelow) self.end_time.valueChanged.connect(self.end_updated) self.duty_label=QLabel('Duty Cycle [%]: ') self.duty_label.setSizePolicy(self.size_policy,self.size_policy) self.duty_label.setFont(self.font) self.duty_cycle=QSlider(Qt.Horizontal) self.duty_cycle.setSizePolicy(self.size_policy,self.size_policy) self.duty_cycle.setFont(self.font) self.duty_cycle.setMinimum(0) self.duty_cycle.setMaximum(100) self.duty_cycle.setSingleStep(1) self.duty_cycle.setValue(10) self.duty_cycle.setTickPosition(QSlider.TicksBelow) self.duty_cycle.valueChanged.connect(self.duty_changed) self.offset_label=QLabel('Offset from sync [us]: ') self.offset_label.setSizePolicy(self.size_policy,self.size_policy) self.offset_label.setFont(self.font) self.offset=QSlider(Qt.Horizontal) self.offset.setSizePolicy(self.size_policy,self.size_policy) self.offset.setFont(self.font) self.offset.setMinimum(0) self.offset.setMaximum(1000) self.offset.setSingleStep(1) self.offset.setValue(10) self.offset.setTickPosition(QSlider.TicksBelow) self.offset.setTickInterval(100) self.offset.setToolTip( 'After end of pulse to divide into Region 1 and 2 in micro seconds') self.offset.valueChanged.connect(self.offset_changed) self.update=QPushButton('Update') self.update.setSizePolicy(self.size_policy,self.size_policy) self.update.setFont(self.font) self.update.clicked.connect(self.updater) layout=QGridLayout() layout.addWidget(start_label,0,0) layout.addWidget(self.start_time,0,1) layout.addWidget(end_label, 1, 0) layout.addWidget(self.end_time,1,1) layout.addWidget(self.duty_label,2,0) layout.addWidget(self.duty_cycle,2,1) layout.addWidget(self.offset_label,3,0) layout.addWidget(self.offset,3,1) layout.addWidget(self.update,4,0) self.popup.setLayout(layout) self.duty_changed() self.offset_changed() self.popup.show() def start_updated(self): time=self.start_time.value() self.s_label.setText('Start Time: {:.2f}s'.format(time)) self.end_time.setMinimum(time+1) def end_updated(self): time=self.end_time.value() self.e_label.setText('End Time: {:.2f}s'.format(time)) def duty_changed(self): self.duty_label.setText('Duty Cycle: {:.2f}%'.format( self.duty_cycle.value())) def offset_changed(self): self.offset_label.setText('Offset from sync: {:.1f} us'.format( self.offset.value())) def updater(self): delta_time=self.duty_cycle.value()/100*( self.sync_time[2]-self.sync_time[1]) delt=(self.sync_time[2]-self.sync_time[1]) self.offset.setMinimum(-int((delt*self.duty_cycle.value()/100)*.75)) sync_width=delta_time delta_time+=self.offset.value() self.delta_time=delta_time start=float(self.start_time.value())*1e6 end=float(self.end_time.value())*1e6 sb,se=Timing.Splitter(self.sync_time,len(self.sync_time), start,end) cb,ce=Timing.Splitter(self.list_time,len(self.list_time), start,end) self.region1_spec,self.region2_spec,self.time=self.list_mode_processor.timing( delta_time,self.sync_time[sb:se], self.list_time[cb:ce],self.list_channel[cb:ce]) total=[] for i in range(len(list(self.region2_spec.values()))): total.append(list(self.region2_spec.values())[i]+list( self.region1_spec.values())[i]) self.region1_ax.clear() self.region2_ax.clear() self.total_ax.clear() self.region1_ax.set_title('Region 1') self.region2_ax.set_title('Region 2') self.total_ax.set_title('Total') r1_values=list(self.region1_spec.values())[:-1] r2_values=list(self.region2_spec.values())[:-1] uncal_keys=list(self.region1_spec.keys()) if self.calibration: self.region1_ax.set_xlim(self.calibration_data[0], 14) self.region2_ax.set_xlim(self.calibration_data[0], 14) self.total_ax.set_xlim(self.calibration_data[0],14) self.region1_ax.set_xlabel('Energy [MeV]') self.region2_ax.set_xlabel('Energy [MeV]') self.total_ax.set_xlabel('Energy [MeV]') self.region1_ax.plot(self.calibration_data,r1_values) self.region2_ax.plot(self.calibration_data,r2_values) self.total_ax.plot(self.calibration_data,total[:-1],label='Total') self.total_ax.plot(self.calibration_data,r1_values,label='Region 1') self.total_ax.plot(self.calibration_data,r2_values,label='Region 2') else: self.region1_ax.set_xlabel('Channel') self.region2_ax.set_xlabel('Channel') self.total_ax.set_xlabel('Channel') self.region1_ax.set_xlim(0,len(list(self.region1_spec.keys()))) self.region2_ax.set_xlim(0,len(list(self.region2_spec.keys()))) self.total_ax.set_xlim(0,len(list(self.region2_spec.keys()))) self.region1_ax.plot(uncal_keys[:-1],r1_values) self.region2_ax.plot(uncal_keys[:-1],r2_values) self.total_ax.plot(uncal_keys[:-1],total[:-1],label='Total') self.total_ax.plot(uncal_keys[:-1],r1_values,label='Region 1') self.total_ax.plot(uncal_keys[:-1],r2_values,label='Region 2') self.region1_ax.set_yscale('log') self.region2_ax.set_yscale('log') self.total_ax.set_yscale('log') self.region1_ax.set_ylabel('Counts') self.region2_ax.set_ylabel('Counts') self.total_ax.set_ylabel('Counts') self.total_ax.legend() self.region1_canvas.draw() self.region2_canvas.draw() self.total_canvas.draw() #plot the timing stuff self.time_ax.clear() self.time_ax.plot(self.time[1][:-1], self.time[0][:-1],'*',label='Time Distribution') height=max(self.time[0]) ys=[0,height,height,0] xs=[0,0,sync_width,sync_width] self.time_ax.plot(xs,ys, label='Sync Pulse, {:.1f}%'.format(self.duty_cycle.value())) self.time_ax.axvline(delta_time, label='Region divider, {:.1f}us\nafter pulse'.format(delta_time)) self.time_ax.set_xlabel(r'Time [$\mu$s]') self.time_ax.set_title('Time') self.time_ax.set_ylabel('Counts') self.time_ax.set_yscale('log') self.time_ax.legend() self.time_canvas.draw() winsound.MessageBeep() def save_spectrum(self): items=['Region 1','Region 2','Time Decay'] # self.updater() text,ok=QInputDialog.getItem(self,'Save Spectrum','Saving:', items,0,False) if ok and text: name=QFileDialog.getSaveFileName(self,'File Name','', 'Text File (*.txt);;Comma Seperated File (*.csv)') try: f=open(name[0],'w') if text==items[0] and name[0]!=" ": counts=list(self.region1_spec.values()) bins=list(self.region1_spec.keys()) for i in range(len(bins)-1): f.write('{}\n'.format(counts[i])) if text==items[1] and name[0]!='': counts=list(self.region2_spec.values()) bins=list(self.region2_spec.keys()) for i in range(len(bins)-1): f.write('{}\n'.format(counts[i])) if text==items[2] and name[0]!='': times=self.time[1][:-1] counts=self.time[0][:-1] f.write('Time[us],counts\n') for i in range(len(counts)): f.write('{:.9f},{}\n'.format(times[i],counts[i])) f.close() except: pass def initiate(self): self.geometry() self.updater() def deviation(self): Arrival_Spread(self.sync_filename[0]).process() def ROI_Arrivals(self): #check to see if a calibration file exists, if not make them get one if not self.calibration: self.calibration_browse() list_time=np.asarray(self.list_time) list_channel=np.asarray(self.list_channel) sync_time=np.asarray(self.sync_time) calibration=np.asarray(self.calibration_data) self.view=ROI_Viewer(list_time,list_channel,sync_time,calibration) self.view.processer.clicked.connect(self.roi_arrive) def roi_arrive(self): # while self.view.done!=True: # False self.time_ax.plot(self.view.bins,self.view.output,'*', label='ROI of {}MeV-{}MeV'.format(self.view.lower,self.view.upper)) self.time_ax.legend() self.time_canvas.draw() def ROI_Spectrum_Saving(self): '''Saving the roi pulses and their respective timing to later perform an integration to find the MDM''' if not self.calibration: self.calibration_browse() list_time=np.asarray(self.list_time) list_channel=np.asarray(self.list_channel) calibration=np.asarray(self.calibration_data) sync_time=np.asarray(self.sync_time) lower,ok=QInputDialog.getDouble(self, 'Lower ROI', 'Lower Bound (MeV)',9.6,0,14,4) upper,ok2=QInputDialog.getDouble(self, 'Upper ROI', 'Upper Bound (MeV)',10.9,0,14,4) if ok and ok2: # #outputs the pulses and associated times to save and integrate to # #calculate the detection probability at integrated time windows s=time.time() print('Begin processing data') pulses,times=Timing.ROI_Timing(sync_time,int(len(sync_time)), list_time,list_channel, self.delta_time,8192, calibration,upper,lower) print('Done processing in {:.2f}s'.format(time.time()-s)) return pulses,times def save_roi_csv(self): name,ok=QFileDialog.getSaveFileName(self,'Safe File Name','', 'Comma Seperated File (*.csv)') if ok: f=open(name,'w') f.write('Pulse_Height(MeV),Time(s)\n') pulse,time=self.ROI_Spectrum_Saving() for i in range(len(pulse)): f.write('{:.3f},{:.3f}\n'.format(pulse[i],time[i]*1e-6)) f.close() print('All finished')
class MDIWindow(QMainWindow): can_send_signal = pyqtSignal(object) PCAN_STATE_CONNECTED = 1 PCAN_STATE_DISCONNECTED = 0 def __init__(self): super().__init__() self.pcan_state = self.PCAN_STATE_DISCONNECTED self.can_row = 0 self.can_data = {} self.dbc_windows = {} self.dbc_send_windows = {} self.config = configparser.ConfigParser() self.recentDBCFiles = {} self.loadPreferences() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() recentDBCMenu = QMenu('Recent DBCs...', self) file = bar.addMenu("File") file.addAction("Connect") file.addAction("Open DBC") file.triggered[QAction].connect(self.fileMenuClicked) file.addMenu(recentDBCMenu) for dbcFile in self.recentDBCFiles.keys(): recentOpenAct = QAction(dbcFile, self) # var c is used to handle the triggered first arg recentOpenAct.triggered.connect( lambda c=dbcFile, f=dbcFile: self.loadDBCFile( self.recentDBCFiles[f])) recentDBCMenu.addAction(recentOpenAct) view = bar.addMenu("View") view.addAction("Cascade") view.addAction("TiledC") view.triggered[QAction].connect(self.viewMenuClicked) self.setWindowTitle("WiCAN " + VERSION) self.statusBar().showMessage('Disconnected') self.createCANTableSubWindow() self.can_thread = CANThread() self.can_send_signal.connect(self.can_thread.send) self.can_thread.can_recv_signal.connect(self.handleCANMessage) self.can_thread.can_status_signal.connect(self.handleCANStatus) self.can_thread.start() timer = QTimer(self) timer.timeout.connect(self.tick) timer.start(50) def createCANTableSubWindow(self): self.can_table = QTableWidget(50, 9) sub = QMdiSubWindow() sub.setWidget(self.can_table) sub.setWindowTitle("Raw CAN Frames") self.mdi.addSubWindow(sub) sub.setGeometry(0, 0, 400, 800) self.can_table.verticalHeader().hide() self.can_table.setHorizontalHeaderItem(0, QTableWidgetItem("ID")) self.can_table.setColumnWidth(0, 10) for i in range(1, 9): item = QTableWidgetItem(str(i - 1)) self.can_table.setHorizontalHeaderItem(i, item) self.can_table.setColumnWidth(i, 10) sub.show() def fileMenuClicked(self, menuitem): if menuitem.text() == "Connect": diag = ConnectDialog(self, self.last_connection) diag.show() diag.exec_() settings = diag.getSettings() self.CANConnect(settings) elif menuitem.text() == "Open DBC": self.loadDBCFileDialog() def viewMenuClicked(self, menuitem): if menuitem.text() == "Cascade": self.mdi.cascadeSubWindows() elif menuitem.text() == "Tiled": self.mdi.tileSubWindows() def loadPreferences(self): self.dbc_path = os.path.dirname(os.path.realpath(__file__)) inifile = self.config.read('wican.ini') if len(inifile) == 0: self.config['WiCAN'] = { 'CANAdaptor': 'PCAN', 'CANBAUD': '250k', 'CANPATH': '' } self.config['RecentDBCs'] = {} self.saveConfig() return can_adapter = self.config['WiCAN'].get('CANAdaptor', 'KVaser') can_baud = self.config['WiCAN'].get('CANBAUD', '250k') can_path = self.config['WiCAN'].get('CANPATH', '') self.last_connection = CANConnection(can_adapter, can_baud, can_path) for file, path in self.config.items("RecentDBCs"): if not os.path.exists(path): self.config.remove_option("RecentDBCs", file) else: self.recentDBCFiles[file] = path def saveConfig(self): with open('wican.ini', 'w') as configfile: self.config.write(configfile) configfile.close() def loadDBCFileDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog types = "DBC Files (*.dbc)" file_path, _ = QFileDialog.getOpenFileName(self, "Open DBC", self.dbc_path, types, options=options) if file_path: self.loadDBCFile(file_path) def loadDBCFile(self, file_path): dbc_win = DBCRecvWindow(file_path, self) sub = QMdiSubWindow() sub.setWidget(dbc_win) sub.setGeometry(100, 100, 500, 500) self.mdi.addSubWindow(sub) sub.show() dbc_send_win = DBCSendWindow(file_path, self) sub2 = QMdiSubWindow() sub2.setWidget(dbc_send_win) sub2.setGeometry(100, 100, 500, 500) self.mdi.addSubWindow(sub2) sub2.show() self.config["RecentDBCs"][os.path.basename(file_path)] = file_path self.recentDBCFiles[os.path.basename(file_path)] = file_path self.dbc_path = os.path.split(file_path)[0] self.saveConfig() @pyqtSlot(int) def handleCANStatus(self, status): if status == 1: self.statusBar().showMessage("Failed to find CAN device") self.pcan_state = self.PCAN_STATE_DISCONNECTED elif status == 0: self.statusBar().showMessage("CAN device connected") self.pcan_state = self.PCAN_STATE_CONNECTED elif status == 2: self.statusBar().showMessage("Disconnected") self.pcan_state = self.PCAN_STATE_DISCONNECTED def CANConnect(self, connection): bustype = connection.bustype bitrate = connection.bitrate path = connection.path self.config["WiCAN"]["canadaptor"] = bustype self.config["WiCAN"]["canbaud"] = bitrate self.config["WiCAN"]["canpath"] = path #TODO: clean up if bustype == 'PCAN': bustype = 'pcan' interface = 'PCAN_USBBUS1' elif bustype == 'KVaser': bustype = 'kvaser' interface = '0' elif bustype == 'Ixxat': bustype = 'ixxat' interface = '0' else: print("Unknown bustype: " + bustype) if bitrate == "125k": bitrate = 125000 elif bitrate == "250k": bitrate = 250000 elif bitrate == "500k": bitrate = 500000 elif bitrate == "1M": bitrate = 1000000 if self.pcan_state == self.PCAN_STATE_DISCONNECTED: self.statusBar().showMessage("Connecting...") self.can_thread.connect(bustype, interface, bitrate) elif self.pcan_state == self.PCAN_STATE_CONNECTED: self.statusBar().showMessage("Disconnecting...") self.can_thread.disconnect() @pyqtSlot(object) def handleCANMessage(self, msg): for file_name, window in self.dbc_windows.items(): window.handleCANMessage(msg) can_id_hex = msg.arbitration_id can_id_printable = hex(can_id_hex) if can_id_hex not in self.can_data.keys(): self.can_data[can_id_hex] = self.can_row can_id_item = QTableWidgetItem(can_id_printable) self.can_table.setItem(self.can_row, 0, can_id_item) row = self.can_row self.can_row += 1 else: row = self.can_data[can_id_hex] for c in range(0, len(msg.data)): item = QTableWidgetItem(hex(msg.data[c])) self.can_table.setItem(row, c + 1, item) def tick(self): for file_name, window in self.dbc_send_windows.items(): window.tick()
class MainWindow(QMainWindow): count = 0 def __init__(self, parent=None): super().__init__(parent) self.mdi = QMdiArea() self.setCentralWidget(self.mdi) self.app_manager = AppManager(None, gui_parent=self) self.mdi.subWindowActivated.connect(self.app_manager.on_view_activated) self.left = 100 self.top = 50 self.width = 1800 self.height = 1200 self.setGeometry(self.left, self.top, self.width, self.height) bar = self.menuBar() file = bar.addMenu("File") file.addAction("New") file.addAction("Open...") file.addSeparator() file.addAction("Save") file.addAction("Save As...") file.addAction("Close") file.triggered[QAction].connect(self.file_action) view = bar.addMenu("View") view.addAction("Spec Sheet") view.addAction("Optical Layout") view.addAction("Lens Table") view.addAction("Element Table") view.addAction("Lens View") view.addSeparator() view.addAction("Paraxial Model") view.addAction("Paraxial Height View") view.addAction("Paraxial Height View V2") view.addAction("Paraxial Slope View") view.addAction("Paraxial Ray Table") view.addAction("Ray Table") view.addSeparator() view.addAction("Ray Fans") view.addAction("OPD Fans") view.addAction("Spot Diagram") view.addAction("Wavefront Map") view.addAction("Astigmatism Curves") view.addAction("3rd Order Aberrations") view.addSeparator() view.triggered[QAction].connect(self.view_action) wnd = bar.addMenu("Window") wnd.addAction("Cascade") wnd.addAction("Tiled") wnd.addSeparator() dock.create_dock_windows(self) for pi in dock.panels.values(): wnd.addAction(pi.menu_action) wnd.triggered[QAction].connect(self.window_action) self.setWindowTitle("Ray Optics") self.show() self.new_model() self.add_ipython_subwindow() # pth = Path(__file__).resolve() # try: # root_pos = pth.parts.index('rayoptics') # except ValueError: # logging.debug("Can't find rayoptics: path is %s", pth) # else: # path = Path(*pth.parts[:root_pos+1]) # self.open_file(path / "codev/tests/asp46.seq") # self.open_file(path / "codev/tests/dar_test.seq") # self.open_file(path / "codev/tests/paraboloid.seq") # self.open_file(path / "codev/tests/paraboloid_f8.seq") # self.open_file(path / "codev/tests/schmidt.seq") # self.open_file(path / "codev/tests/questar35.seq") # self.open_file(path / "codev/tests/rc_f16.seq") # self.open_file(path / "codev/tests/ag_dblgauss.seq") # self.open_file(path / "codev/tests/threemir.seq") # self.open_file(path / "codev/tests/folded_lenses.seq") # self.open_file(path / "codev/tests/unfolded_lenses_w_ape.seq") # self.open_file(path / "codev/tests/lens_reflection_test.seq") # self.open_file(path / "codev/tests/dec_tilt_test.seq") # self.open_file(path / "codev/tests/landscape_lens.seq") # self.open_file(path / "codev/tests/mangin.seq") # self.open_file(path / "optical/tests/cell_phone_camera.roa") # self.open_file(path / "optical/tests/singlet_f3.roa") # try: # root_pos = pth.parts.index('ray-optics') # except ValueError: # logging.debug("Can't find ray-optics: path is %s", pth) # else: # path = Path(*pth.parts[:root_pos+1]) # self.open_file(path / "models/TwoMirror.roa") # self.open_file(path / "models/TwoSphericalMirror.roa") # self.open_file(path / "models/Sasian Triplet.roa") # self.open_file(path / "models/singlet_f5.roa") # self.open_file(path / "models/thinlens.roa") # self.open_file(path / "models/thin_triplet.roa") # self.open_file(path / "models/Cassegrain.roa") # self.open_file(path / "models/Ritchey_Chretien.roa") # finally: # self.add_ipython_subwindow() def add_subwindow(self, widget, model_info): sub_wind = self.mdi.addSubWindow(widget) self.app_manager.add_view(sub_wind, widget, model_info) MainWindow.count += 1 return sub_wind def delete_subwindow(self, sub_wind): self.app_manager.delete_view(sub_wind) self.mdi.removeSubWindow(sub_wind) MainWindow.count -= 1 def add_ipython_subwindow(self): try: create_ipython_console(self, 'iPython console', 600, 400) except MultipleInstanceError: logging.debug("Unable to open iPython console. " "MultipleInstanceError") except Exception as inst: print(type(inst)) # the exception instance print(inst.args) # arguments stored in .args print(inst) # __str__ allows args to be printed directly, pass # but may be overridden in exception subclasses def initial_window_offset(self): offset_x = 50 offset_y = 25 orig_x = (MainWindow.count - 1) * offset_x orig_y = (MainWindow.count - 1) * offset_y return orig_x, orig_y def file_action(self, q): if q.text() == "New": self.new_model() if q.text() == "Open...": options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "CODE V Files (*.seq);;Ray-Optics Files (*.roa)", options=options) if fileName: logging.debug("open file: %s", fileName) self.open_file(fileName) if q.text() == "Save As...": options = QFileDialog.Options() # options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", "", "Ray-Optics Files (*.roa);;All Files (*)", options=options) if fileName: logging.debug("save file: %s", fileName) self.save_file(fileName) if q.text() == "Close": self.close_model() def new_model(self): iid = cmds.create_new_ideal_imager(gui_parent=self, conjugate_type='infinite') self.refresh_app_ui() def open_file(self, file_name): self.cur_filename = file_name self.app_manager.set_model(open_model(file_name)) self.is_changed = True self.create_lens_table() cmds.create_live_layout_view(self.app_manager.model, gui_parent=self) # cmds.create_lens_layout_view(self.app_manager.model, gui_parent=self) # self.create_2D_lens_view() self.refresh_app_ui() def save_file(self, file_name): self.app_manager.model.save_model(file_name) self.cur_filename = file_name self.is_changed = False def close_model(self): """ NOTE: this does not check to save a modified model """ self.app_manager.close_model(self.delete_subwindow) def view_action(self, q): opt_model = self.app_manager.model if q.text() == "Spec Sheet": cmds.create_new_ideal_imager(opt_model=opt_model, gui_parent=self) if q.text() == "Optical Layout": cmds.create_live_layout_view(opt_model, gui_parent=self) if q.text() == "Lens View": cmds.create_lens_layout_view(opt_model, gui_parent=self) # self.create_2D_lens_view() if q.text() == "Lens Table": self.create_lens_table() if q.text() == "Element Table": model = cmds.create_element_table_model(opt_model) self.create_table_view(model, "Element Table") if q.text() == "Ray Fans": cmds.create_ray_fan_view(opt_model, "Ray", gui_parent=self) if q.text() == "OPD Fans": cmds.create_ray_fan_view(opt_model, "OPD", gui_parent=self) if q.text() == "Spot Diagram": cmds.create_ray_grid_view(opt_model, gui_parent=self) if q.text() == "Wavefront Map": cmds.create_wavefront_view(opt_model, gui_parent=self) if q.text() == "Astigmatism Curves": cmds.create_field_curves(opt_model, gui_parent=self) if q.text() == "3rd Order Aberrations": cmds.create_3rd_order_bar_chart(opt_model, gui_parent=self) if q.text() == "Paraxial Height View": cmds.create_paraxial_design_view(opt_model, 'ht', gui_parent=self) if q.text() == "Paraxial Height View V2": cmds.create_paraxial_design_view_v2(opt_model, 'ht', gui_parent=self) if q.text() == "Paraxial Slope View": cmds.create_paraxial_design_view(opt_model, 'slp', gui_parent=self) if q.text() == "Paraxial Ray Table": model = cmds.create_parax_table_model(opt_model) self.create_table_view(model, "Paraxial Ray Table") if q.text() == "Paraxial Model": model = cmds.create_parax_model_table(opt_model) self.create_table_view(model, "Paraxial Model") if q.text() == "Ray Table": self.create_ray_table(opt_model) def window_action(self, q): if q.text() == "Cascade": self.mdi.cascadeSubWindows() if q.text() == "Tiled": self.mdi.tileSubWindows() def create_lens_table(self): seq_model = self.app_manager.model.seq_model model = cmds.create_lens_table_model(seq_model) self.create_table_view(model, "Surface Data Table") def create_ray_table(self, opt_model): osp = opt_model.optical_spec pupil = [0., 1.] fi = 0 wl = osp.spectral_region.reference_wvl fld, wvl, foc = osp.lookup_fld_wvl_focus(fi, wl) ray, ray_op, wvl = trace.trace_base(opt_model, pupil, fld, wvl) # ray, ray_op, wvl, opd = trace.trace_with_opd(opt_model, pupil, # fld, wvl, foc) # cr = trace.RayPkg(ray, ray_op, wvl) # s, t = trace.trace_coddington_fan(opt_model, cr, foc) ray = [RaySeg(*rs) for rs in ray] model = cmds.create_ray_table_model(opt_model, ray) self.create_table_view(model, "Ray Table") def create_2D_lens_view(self): scene2d = QGraphicsScene() self.create_element_model(scene2d) self.create_ray_model(scene2d) scene2d.setBackgroundBrush(QColor(237, 243, 254)) # light blue sceneRect2d = scene2d.sceneRect() # construct the top level widget widget = QWidget() # construct the top level layout layout = QVBoxLayout(widget) # set the layout on the widget widget.setLayout(layout) sub = self.add_subwindow( widget, ModelInfo(self.app_manager.model, MainWindow.update_2D_lens_view, (scene2d, ))) sub.setWindowTitle("2D Lens View") view_width = 660 view_ht = 440 view_ratio = view_width / view_ht orig_x, orig_y = self.initial_window_offset() sub.setGeometry(orig_x, orig_y, view_width, view_ht) self.gview2d = QGraphicsView(scene2d) scene_ratio = sceneRect2d.width() / sceneRect2d.height() oversize_fraction = 1.2 if scene_ratio > view_ratio: view_scale = view_width / (oversize_fraction * sceneRect2d.width()) else: view_scale = view_ht / (oversize_fraction * sceneRect2d.height()) self.gview2d.scale(view_scale, view_scale) layout.addWidget(self.gview2d) sub.show() return sub def update_2D_lens_view(scene2d): for gi in scene2d.items(): gi.prepareGeometryChange() gi.update_shape() def create_element_model(self, gscene): ele_model = self.app_manager.model.ele_model ele_model.elements_from_sequence(self.app_manager.model.seq_model) for e in ele_model.elements: ge = OpticalElement(e) gscene.addItem(ge) def create_ray_model(self, gscene, start_surf=1): opt_model = self.app_manager.model img_dist = abs(opt_model.optical_spec.parax_data[2].img_dist) start_offset = 0.05 * (gscene.sceneRect().width() + img_dist) fov = opt_model.optical_spec.field_of_view for fi, f in enumerate(fov.fields): rb = RayBundle(opt_model, fi, start_offset) gscene.addItem(rb) def create_table_view(self, table_model, table_title, close_callback=None): # construct the top level widget widget = QWidget() # construct the top level layout layout = QVBoxLayout(widget) tableView = QTableView() tableView.setAlternatingRowColors(True) # Add table to box layout layout.addWidget(tableView) # set the layout on the widget widget.setLayout(layout) sub = self.add_subwindow( widget, ModelInfo(self.app_manager.model, cmds.update_table_view, (tableView, ))) sub.setWindowTitle(table_title) sub.installEventFilter(self) tableView.setModel(table_model) tableView.setMinimumWidth(tableView.horizontalHeader().length() + tableView.horizontalHeader().height()) # The following line should work but returns 0 # tableView.verticalHeader().width()) view_width = tableView.width() view_ht = tableView.height() orig_x, orig_y = self.initial_window_offset() sub.setGeometry(orig_x, orig_y, view_width, view_ht) # table data updated successfully table_model.update.connect(self.on_data_changed) sub.show() return sub def eventFilter(self, obj, event): if (event.type() == QEvent.Close): print('close event received:', obj) return False def refresh_gui(self): self.app_manager.refresh_gui() def refresh_app_ui(self): dock.update_dock_windows(self) def handle_ideal_imager_command(self, iid, command, specsheet): if command == 'Apply': opt_model = self.app_manager.model opt_model.set_from_specsheet(specsheet) self.refresh_gui() elif command == 'Close': for view, info in self.app_manager.view_dict.items(): if iid == info[0]: view.close() elif command == 'Update': opt_model = self.app_manager.model specsheet = opt_model.specsheet firstorder.specsheet_from_parax_data(opt_model, specsheet) iid.specsheet_dict[specsheet.conjugate_type] = specsheet iid.update_values() elif command == 'New': opt_model = cmds.create_new_optical_model_from_specsheet(specsheet) self.app_manager.set_model(opt_model) for view, info in self.app_manager.view_dict.items(): if iid == info[0]: w = iid mi = info[1] args = (iid, opt_model) new_mi = ModelInfo(model=opt_model, fct=mi.fct, args=args, kwargs=mi.kwargs) self.app_manager.view_dict[view] = w, new_mi self.refresh_gui() self.create_lens_table() cmds.create_live_layout_view(opt_model, gui_parent=self) cmds.create_paraxial_design_view(opt_model, 'ht', gui_parent=self) self.refresh_gui() @pyqtSlot(object, int) def on_data_changed(self, rootObj, index): self.refresh_gui()
class DemoMdi(QMainWindow): def __init__(self, parent=None): super(DemoMdi, self).__init__(parent) # 设置窗口标题 self.setWindowTitle( 'MDI with a dockWidget tree and a tab-view mdiArea') # 设置窗口大小 self.resize(800, 640) self.initUi() self.mytimer = QTimer(self) self.mytimer.start(1000) self.mytimer.timeout.connect(self.timerCallback) def initUi(self): self.initMenuBar() self.initToolBar() self.initDockTree() self.initStatusBar() self.mdiArea = QMdiArea(self) self.setCentralWidget(self.mdiArea) # set as tabbedView by default self.mdiArea.setViewMode(QMdiArea.TabbedView) self.mdiArea.setTabShape(QTabWidget.Triangular) self.mdiArea.setTabsClosable(True) self.mdiArea.setTabsMovable(True) # index of document self.newDocIndex = 1 def initDockTree(self): self.dockWind = QDockWidget(self) self.dockWind.setWindowTitle('QProfile Explorer') self.initTree() self.dockWind.setWidget(self.tree) self.dockWind.setFloating(False) # set floating = false self.addDockWidget(Qt.LeftDockWidgetArea, self.dockWind) # set the position at left side # remove all features of DockWidget like Closable, Moveable, Floatable, VerticalTitle etc. self.dockWind.setFeatures(QDockWidget.NoDockWidgetFeatures) def initTree(self): self.tree = QTreeWidget() self.tree.setColumnCount(1) #设置列数 #self.tree.setHeaderLabels(['QProfiler items']) #设置树形控件头部的标题 self.tree.setIndentation(20) # 项目的缩进 self.tree.setHeaderHidden(True) #设置根节点 Perfmon = myQTreeWidgetItem(sin(pi * perfmon_x)) Perfmon.setText(0, 'Perfmon') perfmon_00 = myQTreeWidgetItem(sin(2 * pi * perfmon_x)) perfmon_00.setText(0, 'perfmon_00') Perfmon.addChild(perfmon_00) perfmon_01 = QTreeWidgetItem() perfmon_01.setText(0, 'perfmon_01') Perfmon.addChild(perfmon_01) perfmon_02 = QTreeWidgetItem() perfmon_02.setText(0, 'perfmon_02') Perfmon.addChild(perfmon_02) perfmon_03 = QTreeWidgetItem() perfmon_03.setText(0, 'perfmon_03') Perfmon.addChild(perfmon_03) self.tree.addTopLevelItem(Perfmon) # CPU cpuLoad = QTreeWidgetItem() cpuLoad.setText(0, 'CPU') cpuLoad_1 = QTreeWidgetItem() cpuLoad_1.setText(0, 'core 1') cpuLoad.addChild(cpuLoad_1) cpuLoad_2 = QTreeWidgetItem() cpuLoad_2.setText(0, 'core 2') cpuLoad.addChild(cpuLoad_2) self.tree.addTopLevelItem(cpuLoad) # treeItem signal self.tree.itemClicked[QTreeWidgetItem, int].connect(self.treeItemClicked) def treeItemWindow_open(self, item): title = item.text(0) subWind = QMdiSubWindow(self) subWind.setAttribute(Qt.WA_DeleteOnClose) subWind.setWindowTitle(title) self.newDocIndex += 1 mainWid = QWidget() l = QtWidgets.QVBoxLayout(mainWid) txtWind = QPlainTextEdit(mainWid) txtWind.setPlainText(f"perfmon.x = {item.x}, \n y = {item.y}") figWind = MyCanvas(mainWid, width=5, height=4, dpi=100, treeWidgetItem=item) l.addWidget(figWind) l.addWidget(txtWind) l.setStretch(0, 3) # 设置第一列的伸展比例为 3 l.setStretch(1, 1) # 设置第二列的伸展比例为 1, 这样2列的伸展比为3:1 subWind.setWidget(mainWid) self.mdiArea.addSubWindow(subWind) subWind.show() def treeItemClicked(self, item, column): tab = self.get_treeItem_tab(item.text(column)) if tab is not None: tab.setFocus() else: if item.text(column) == 'Perfmon': self.treeItemWindow_open(item) else: newDoc = QMdiSubWindow(self) newDoc.setAttribute(Qt.WA_DeleteOnClose) newDoc.setWindowTitle(item.text(column)) self.newDocIndex += 1 newDoc.setWidget(QPlainTextEdit( item.text(column) * 10, newDoc)) self.mdiArea.addSubWindow(newDoc) newDoc.show() def get_treeItem_tab(self, title): for wind in self.mdiArea.subWindowList(): if title == wind.windowTitle(): return wind return None def initStatusBar(self): self.statusBar = self.statusBar() self.statusBar.showMessage('Ready to start ...', 0) def initMenuBar(self): menuBar = self.menuBar() style = QApplication.style() #==== 文件 ====# fileMenu = menuBar.addMenu('文件') #新建一个文档 aFileNew = QAction('新建文档', self) aFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon)) aFileNew.triggered.connect(self.onFileNew) fileMenu.addAction(aFileNew) #打开一个文档 aFileOpen = QAction('打开文档', self) aFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton)) aFileOpen.triggered.connect(self.onFileOpen) fileMenu.addAction(aFileOpen) #关闭一个文档 aFileCloseAll = QAction('关闭全部', self) aFileCloseAll.setIcon(style.standardIcon(QStyle.SP_DialogCloseButton)) aFileOpen.triggered.connect(self.onFileCloseAll) fileMenu.addAction(aFileCloseAll) #添加分割线 fileMenu.addSeparator() #退出 aFileExit = QAction('退出', self) aFileExit.triggered.connect(self.close) fileMenu.addAction(aFileExit) #==== 编辑 ====# editMenu = menuBar.addMenu('编辑') #剪切 aEditCut = QAction('剪切', self) aEditCut.setIcon(QIcon(':/ico/cut.png')) aEditCut.triggered.connect(self.onEditCut) editMenu.addAction(aEditCut) #复制 aEditCopy = QAction('复制', self) aEditCopy.setIcon(QIcon(':/ico/copy.png')) aEditCopy.triggered.connect(self.onEditCopy) editMenu.addAction(aEditCopy) #粘贴 aEditPaste = QAction('粘贴', self) aEditPaste.setIcon(QIcon(':/ico/paste.png')) aEditPaste.triggered.connect(self.onEditPaste) editMenu.addAction(aEditPaste) #==== 窗口排列方式 ====# windowMenu = menuBar.addMenu('窗口') #子窗口模式 aWndSubView = QAction('子窗口模式', self) aWndSubView.triggered.connect(lambda: self.onWinowdMode(0)) windowMenu.addAction(aWndSubView) #标签页模式 aWndTab = QAction('标签页模式', self) aWndTab.triggered.connect(lambda: self.onWinowdMode(1)) windowMenu.addAction(aWndTab) windowMenu.addSeparator() #平铺模式 aWndTile = QAction('平铺模式', self) aWndTile.triggered.connect(lambda: self.onWinowdMode(2)) windowMenu.addAction(aWndTile) #窗口级联模式 aWndCascade = QAction('窗口级联模式', self) aWndCascade.triggered.connect(lambda: self.onWinowdMode(3)) windowMenu.addAction(aWndCascade) def initToolBar(self): toolBar = self.addToolBar('ToolBar') style = QApplication.style() min_width = 64 btnFileNew = QToolButton(self) btnFileNew.setText('新建文档') btnFileNew.setMinimumWidth(min_width) btnFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon)) btnFileNew.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileNew.clicked.connect(self.onFileNew) toolBar.addWidget(btnFileNew) btnFileOpen = QToolButton(self) btnFileOpen.setText('打开文档') btnFileOpen.setMinimumWidth(min_width) btnFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton)) btnFileOpen.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileOpen.clicked.connect(self.onFileOpen) toolBar.addWidget(btnFileOpen) btnFileCloseAll = QToolButton(self) btnFileCloseAll.setText('关闭全部') btnFileCloseAll.setMinimumWidth(min_width) btnFileCloseAll.setIcon(style.standardIcon( QStyle.SP_DialogCloseButton)) btnFileCloseAll.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileCloseAll.clicked.connect(self.onFileCloseAll) toolBar.addWidget(btnFileCloseAll) toolBar.addSeparator() btnEditCut = QToolButton(self) btnEditCut.setText('剪切') btnEditCut.setMinimumWidth(64) btnEditCut.setIcon(QIcon(':/ico/cut.png')) btnEditCut.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditCut.clicked.connect(self.onEditCut) toolBar.addWidget(btnEditCut) btnEditCopy = QToolButton(self) btnEditCopy.setText('复制') btnEditCopy.setMinimumWidth(64) btnEditCopy.setIcon(QIcon(':/ico/copy.png')) btnEditCopy.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditCopy.clicked.connect(self.onEditCopy) toolBar.addWidget(btnEditCopy) btnEditPaste = QToolButton(self) btnEditPaste.setText('粘贴') btnEditPaste.setMinimumWidth(64) btnEditPaste.setIcon(QIcon(':/ico/paste.png')) btnEditPaste.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditPaste.clicked.connect(self.onEditPaste) toolBar.addWidget(btnEditPaste) def msgCritical(self, strInfo): dlg = QMessageBox(self) dlg.setIcon(QMessageBox.Critical) dlg.setText(strInfo) dlg.show() def onFileNew(self): newDoc = QMdiSubWindow(self) newDoc.setAttribute(Qt.WA_DeleteOnClose) newDoc.setWindowTitle('新文档 ' + str(self.newDocIndex)) self.newDocIndex += 1 newDoc.setWidget(QPlainTextEdit(newDoc)) self.mdiArea.addSubWindow(newDoc) newDoc.show() def onFileOpen(self): path, _ = QFileDialog.getOpenFileName(self, '打开文件', '', '文本文件 (*.txt, *.prf)') if path: try: with open(path, 'rU') as f: text = f.read() except Exception as e: self.msgCritical(str(e)) else: openDoc = QMdiSubWindow(self) openDoc.setWindowTitle(path) txtEdit = QPlainTextEdit(openDoc) txtEdit.setPlainText(text) openDoc.setWidget(txtEdit) self.mdiArea.addSubWindow(openDoc) openDoc.show() def onFileCloseAll(self): self.mdiArea.closeAllSubWindows() def onEditCut(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.cut() def onEditCopy(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.copy() def onEditPaste(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.paste() def onWinowdMode(self, index): if index == 3: self.mdiArea.cascadeSubWindows() elif index == 2: self.mdiArea.tileSubWindows() elif index == 1: self.mdiArea.setViewMode(QMdiArea.TabbedView) else: self.mdiArea.setViewMode(QMdiArea.SubWindowView) def timerCallback(self): self.statusBar.showMessage( f'Document Index = {self.newDocIndex}, subWind num ={len(self.mdiArea.subWindowList())}', 0)