class MdiAplicacion(QMainWindow): contador = 0 def __init__(self): super().__init__() self.inicializarGui() def inicializarGui(self): self.setWindowTitle('Ventanas MDI - Multiple Document Interface') self.mdi = QMdiArea() self.setCentralWidget(self.mdi) mbr_principal = self.menuBar() mnu_archivo = mbr_principal.addMenu('Archivo') mni_agregar_ventana = QAction('Agregar ventana', self) mni_agregar_ventana.triggered.connect(self.agregar_ventana) mni_salir = QAction('Salir', self) mni_salir.setShortcut('Ctrl+Q') mni_salir.triggered.connect(self.close) mnu_archivo.addAction(mni_agregar_ventana) mnu_archivo.addAction(mni_salir) def agregar_ventana(self): MdiAplicacion.contador += 1 subventana = QMdiSubWindow() subventana.setWindowTitle(f'Subventana {MdiAplicacion.contador}') self.mdi.addSubWindow(subventana) subventana.show()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) # create sub windows self.mdi_area = QMdiArea() start_widget = StartWidget() start_widget.start_signal.connect(self.create_arithmetic_widget) self.start_window = self.mdi_area.addSubWindow(start_widget) self.start_window.setWindowFlags(Qt.FramelessWindowHint) self.start_window.showMaximized() self.arithmetic_widget = None self.setCentralWidget(self.mdi_area) def create_arithmetic_widget(self, operand_range: tuple, operators: tuple): arithmetic_widget = ArithmeticWidget(operand_range, operators) arithmetic_widget.stop_signal.connect(self.reshow_start_widget) self.arithmetic_widget = self.mdi_area.addSubWindow(arithmetic_widget) self.arithmetic_widget.setWindowFlags(Qt.FramelessWindowHint) self.start_window.hide() self.arithmetic_widget.showMaximized() def move_to_center(self): """Move window to center desktop""" point = QPoint(self.width() // 2, self.height() // 2 + 100) pos = QApplication.desktop().availableGeometry().center() self.move(pos - point) def reshow_start_widget(self): self.mdi_area.activeSubWindow().close() self.start_window.showMaximized()
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 Demo(QMainWindow): def __init__(self): super(Demo, self).__init__() self.mdi_area = QMdiArea(self) # 1 self.setCentralWidget(self.mdi_area) self.toolbar = self.addToolBar('Tool Bar') self.new_action = QAction(QIcon('images/new.ico'), 'New', self) # 2 self.close_action = QAction(QIcon('images/close.ico'), 'Close', self) self.close_all_action = QAction(QIcon('images/close_all.ico'), 'Close All', self) self.mode1_action = QAction(QIcon('cascade.ico'), 'Cascade', self) self.mode2_action = QAction(QIcon('tile.ico'), 'Tile', self) self.new_action.triggered.connect(self.new_func) # 3 self.close_action.triggered.connect(self.mdi_area.closeActiveSubWindow) self.close_all_action.triggered.connect( self.mdi_area.closeAllSubWindows) self.mode1_action.triggered.connect(self.mdi_area.cascadeSubWindows) self.mode2_action.triggered.connect(self.mdi_area.tileSubWindows) self.toolbar.addAction(self.new_action) # 4 self.toolbar.addAction(self.close_action) self.toolbar.addAction(self.close_all_action) self.toolbar.addAction(self.mode1_action) self.toolbar.addAction(self.mode2_action) def new_func(self): text = QTextEdit() sub = QMdiSubWindow() sub.setWidget(text) self.mdi_area.addSubWindow(sub) sub.show()
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 MainWindow(QMainWindow): count = 0 def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.mdi = QMdiArea() self.setCentralWidget(self.mdi) bar = self.menuBar() file = bar.addMenu("File") file.addAction("New") file.triggered[QAction].connect(self.windowaction) self.setWindowTitle("MDI demo") @pyqtSlot(str) def windowclosed(self, text): print(text) def windowaction(self, q): if q.text() == "New": MainWindow.count = MainWindow.count + 1 sub = MdiSubWindow() sub.setWidget(QTextEdit()) sub.setAttribute(Qt.WA_DeleteOnClose) sub.setWindowTitle("subwindow" + str(MainWindow.count)) sub.sigClosed.connect(self.windowclosed) self.mdi.addSubWindow(sub) sub.show()
class App(QMainWindow): def __init__(self, parent=None): super(App, self).__init__(parent) self.title = "DndTool" self.width, self.height = 1280, 720 self.x, self.y = 100, 100 self.mdi = QMdiArea() self.menu = self.menuBar() self.status = self.statusBar() windows_menu = self.menu.addMenu('Windows') windows_menu.addAction("Players") windows_menu.addAction("Debug") windows_menu.triggered.connect(self.open_windows) self.setWindowTitle(self.title) self.setGeometry(self.x, self.y, self.width, self.height) self.setCentralWidget(self.mdi) self.show() def open_windows(self, action): if action.text() == "Players": sub = PlayersSubWindow() self.mdi.addSubWindow(sub) sub.show() elif action.text() == "Debug": print( [window.windowTitle() for window in self.mdi.subWindowList()])
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 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 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 main_win(QMainWindow, Ui_MainWindow): def __init__(self): super(main_win, self).__init__() self.setupUi(self) self.my_setupUi() # 优化界面 # self.trigger_connection()#信号与槽连接 # self.view_dock_closeEvent()#重写dock关闭函数 def my_setupUi(self): self.tree.setColumnCount(1) # 设置列数 self.tree.header().hide() self.mdi = QMdiArea() self.tabWidget = QTabWidget() self.tabWidget.setTabsClosable(True) #self.mdi.setViewMode(QMdiArea.TabbedView) # 设置为Tab多页显示模式 #self.mdi.setTabsClosable(1) self.text_browser = QTextEdit( self) # QtextEditClick继承自QTextEdit,内置于信息输出视图 self.text_browser.setReadOnly(True) # 仅作为信息输出,设置“只读”属性 self.serial_info = QTextEdit( self) # QtextEditClick继承自QTextEdit,内置于信息输出视图 self.serial_info.setReadOnly(True) #self.loadFile(self.filename) # 1.3创建信息输出视图 self.dock_serial = QDockWidget('Serial monitor', self) # self.dock_connection.setText("connnnn") # self.dock_serial.setMinimumSize(600, 150) self.dock_serial.setWidget(self.serial_info) self.dockBuilt = QDockWidget('Built output', self) # self.dockBuilt.setFearures(DockWidgetClosable) self.dockBuilt.setFeatures(QDockWidget.DockWidgetClosable | QDockWidget.DockWidgetMovable) self.dockBuilt.setMinimumSize(600, 150) # 宽=600,高= 150 self.dockBuilt.setWidget(self.text_browser) self.tabifyDockWidget(self.dockBuilt, self.dock_serial) splitter1 = QSplitter(Qt.Vertical) splitter1.addWidget(self.mdi) # 多文档视图占布局右上 splitter1.addWidget(self.dockBuilt) # 信息输出视图占布局右下 # self.addDockWidget(Qt.LeftDockWidgetArea, self.dockProject) # 工程视图占布局左边 self.setCentralWidget(splitter1) self.tabifyDockWidget(self.dock_connection, self.dock_serial) self.tree.setIconSize(QSize(25, 25)) self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection) self.sub = QMdiSubWindow() self.sub.setWidget(self.tabWidget) self.mdi.addSubWindow(self.sub) self.sub.showMaximized() self.sub.setWindowFlags(Qt.FramelessWindowHint) self.set_tree()
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.sub_window_1 = QMdiSubWindow() self.sub_window_1.setWidget(QTextEdit("<h1>Hello World!</h1>")) self.sub_window_2 = QMdiSubWindow() self.sub_window_2.setWidget(QPushButton('Click!')) self.mdi_area = QMdiArea() self.mdi_area.addSubWindow(self.sub_window_1) self.mdi_area.addSubWindow(self.sub_window_2) self.setCentralWidget(self.mdi_area)
class mainwindow(QMainWindow): def __init__(self): super(mainwindow, self).__init__() # 主界面的布局 layout = QHBoxLayout() w = QWidget() w.setLayout(layout) self.setCentralWidget(w) # 显示弹出的子窗口 self.new_subwindow_btn = QPushButton("新建") layout.addWidget(self.new_subwindow_btn) self.new_subwindow_btn.clicked.connect(self.count) # 读取子窗口数据 self.read_content_btn = QPushButton("读取") layout.addWidget(self.read_content_btn) self.read_content_btn.clicked.connect(self.get_content) # 创建多文档区域 self.mdi = QMdiArea() layout.addWidget(self.mdi) # 要被创建的子窗口 self.w = QWidget() def count(self): # 使用 mdi 类来管理,判断当前窗口是否包含对象 # 如果不包含,创建。包含,不创建。 if self.mdi.currentSubWindow() == None: sub = QMdiSubWindow() # 每次 sub 页面关闭,都会销毁包含进来的对象 # 所以将包含的 半径、角度 等对象封装为一个类,或每次都新建 self.w = PopWindow() sub.setWidget(self.w.get_window()) sub.setWindowTitle("弹出窗口") # 如果不包含,弹出对话框可以浮动于任何位置,否则只能位于 mdi 区域内 self.mdi.addSubWindow(sub) sub.show() def get_content(self): if self.mdi.currentSubWindow() != None: angle, radis, dis = self.w.get_angle(), self.w.get_radis( ), self.w.get_dis() print(angle, radis, dis)
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()
class mainwindow(QMainWindow): def __init__(self): super(mainwindow, self).__init__() layout = QHBoxLayout() w = QWidget() w.setLayout(layout) self.setCentralWidget(w) self.new_subwindow_btn = QPushButton("新建") layout.addWidget(self.new_subwindow_btn) self.new_subwindow_btn.clicked.connect(self.count) self.mdi = QMdiArea() layout.addWidget(self.mdi) def count(self): subwindow_widget = QWidget() subwindow_layout = QGridLayout() self.sub = QMdiSubWindow() radis_label = QLabel("半径") radis_line = QLineEdit() subwindow_layout.addWidget(radis_label, 0, 0) subwindow_layout.addWidget(radis_line, 0, 1) angle_label = QLabel("角度") angle_line = QLineEdit() subwindow_layout.addWidget(angle_label, 1, 0) subwindow_layout.addWidget(angle_line, 1, 1) radis_label = QLabel("螺距") radis_line = QLineEdit() subwindow_layout.addWidget(radis_label, 2, 0) subwindow_layout.addWidget(radis_line, 2, 1) subwindow_widget.setLayout(subwindow_layout) self.sub.setWidget(subwindow_widget) self.sub.setWindowTitle("Sub Window") self.mdi.addSubWindow(self.sub) self.sub.show() def re_open(self): self.new_subwindow_btn.setEnabled(True)
class NetMainWindow(QMainWindow): def __init__(self): super(NetMainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createStatusBar() self.readSettings() self.setWindowTitle('Net') def newScan(self): child = IpRangeScanGUI(self) self.mdiArea.addSubWindow(child) child.show() return child def createActions(self): self.newIPScanAct = QAction("&Start a Scan", self, shortcut=QKeySequence.New, statusTip="Start a Scan", triggered=self.newScan) def createMenus(self): self.serverMenu = self.menuBar().addMenu('&IP Scan') self.serverMenu.addAction(self.newIPScanAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def readSettings(self): settings = QSettings('Trolltech', 'MDI Example') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(500, 500)) self.move(pos) self.resize(size) def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window)
class GuiMenu(Menu): def eventFilter(self, source, event): if event.type() == QtCore.QEvent.MouseMove: newClickPosition = source.mapTo(self.mdi, event.pos()) for s in self.mdi.subWindowList(): if source == s.widget() and self.moving: newWidgetX = self.prevWidgetPosition.x() + newClickPosition.x() - self.downPosition.x() newWidgetY = self.prevWidgetPosition.y() + newClickPosition.y() - self.downPosition.y() s.move(newWidgetX, newWidgetY) elif event.type() == QtCore.QEvent.MouseButtonPress: if (source.cursor().shape() == QtCore.Qt.SizeVerCursor or source.cursor().shape() == QtCore.Qt.SizeHorCursor): # resizing hack: resizing works as some mdi feature, just don't set moving parameters here. pass else: self.downPosition = source.mapTo(self.mdi, event.pos()) self.prevWidgetPosition = source.mapTo(self.mdi, source.pos()) self.moving = True elif event.type() == QtCore.QEvent.MouseButtonRelease: self.moving = False return QWidget.eventFilter(self, source, event) def __init__(self): a = QTextEdit('some gui settings') b = QTextEdit('some gui settings too') widgets = [a, b] self.mdi = QMdiArea() self.moving = False layout = QVBoxLayout() layout.addWidget(self.mdi) super().__init__('GUI', layout) for w in widgets: self.mdi.addSubWindow(w, QtCore.Qt.FramelessWindowHint) w.setEnabled(False) w.setMouseTracking(True) w.installEventFilter(self) # only called on subwindows -> no collision detection needed
class Example(QMainWindow): def __init__(self, parent=parent): super().__init__(self, parent) screen_resolution = app.desktop().screenGeometry() width, height = screen_resolution.width(), screen_resolution.height() self.initUI() def initUI(self): self.statusBar().showMessage('Ready') self.btn = QPushButton("Button", self) self.btn.setToolTip("Test Button") self.btn.setIcon(QIcon("/home/hsa/PycharmProjects/test12/home-6x.png")) #btn.setIconSize(QSize(24,24)) self.btn.move(50, 50) self.btn.clicked.connect(self.on_clicked) self.setGeometry(0, 0, width, height) self.setWindowTitle('MainWindow') #self.mysubwindow() def mysubwindow(self): self.mdi = QMdiArea() self.setCentralWidget(self.mdi) self.sub = QMdiSubWindow() self.sub.setGeometry(10, 10, width - 30, height - 100) self.tabbar = QTabBar() self.sub.setWidget(self.tabbar) self.tabbar.addTab("Test1") self.tabbar.addTab("Test2") self.sub.setWindowTitle("subwindow") self.mdi.addSubWindow(self.sub) self.sub.show() @pyqtSlot() def on_clicked(self): print("Button clicked")
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 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()
class KOWRaidEditor(QMainWindow): def __init__(self, parent=None): super(KOWRaidEditor, self).__init__(parent) self.setWindowTitle("Raid Editor v0.1") self.setGeometry(100, 100, 1280, 960) #changed basic geom to 1280*960 self.center() self.mdi = QMdiArea() self.setCentralWidget(self.mdi) # todo: file import/export system 추가 필요 self.init_ui() self._methods = [] self._popup = QDialog() # test self.test_data() # self.__top_splitter = self.create_top_group_box() self.__top_splitter.setStretchFactor(1, 1) # main_layout = QVBoxLayout() main_layout.addWidget(self.__top_splitter) central_widget = QWidget() central_widget.setLayout(main_layout) #self.setCentralWidget(central_widget) // central widget 교체 def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def test_data(self): self._methods.append(PyJavaMethod("test1", "sfsfsfs")) self._methods.append(PyJavaMethod("test2", "gasdfwqerasdf")) self._methods.append(PyJavaMethod("test3", "basdfinerlih")) self._methods.append(PyJavaMethod("test4", "gqwerasdf\nqfwe")) self._methods.append( PyJavaMethod( "test5", "sfsfsfs\nhgqwrgaswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwdf\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhgqw\nhg3qw\nhgq412w" )) def init_ui(self): # menu bar act_newfile = QAction(QIcon('newfile.png'), '새로운 기사', self) act_newfile.setShortcut('Ctrl+N') act_newfile.setStatusTip('Create New File') act_newfile.triggered.connect(self.new_windows) # todo: link action creating new document act_openfile = QAction(QIcon('openfile.png'), '기사 불러오기', self) act_openfile.setShortcut('Ctrl+O') act_openfile.setStatusTip('Open Existing Article') act_savefile = QAction(QIcon('savefile.png'), '기사 저장하기', self) act_savefile.setShortcut('Ctrl+S') act_savefile.setStatusTip('Save Current Article') act_export = QAction(QIcon('export.png'), '&Export', self) act_export.setShortcut('Ctrl+E') act_export.setStatusTip('Export to Java SourceCode') act_export.triggered.connect(self.export_java) act_import = QAction(QIcon('import.png'), '&Import', self) act_import.setShortcut('Ctrl+I') act_import.setStatusTip('Import lua code') act_import.triggered.connect(self.import_lua) status_bar = self.statusBar() status_bar.showMessage('글자 수 표시 장소') menu_bar = self.menuBar() menu_bar.setNativeMenuBar(False) print(menu_bar.height()) menu_file = menu_bar.addMenu('&File') menu_file.addAction(act_newfile) menu_file.addAction(act_openfile) menu_file.addAction(act_export) menu_file.addAction(act_import) #tool bar fontbox = QFontComboBox(self) fontbox.InsertPolicy() fontbox.setMinimumContentsLength(3) format_bar = self.addToolBar('Format') format_bar.addAction(act_newfile) format_bar.addAction(act_openfile) format_bar.addAction(act_savefile) format_bar.addSeparator() format_bar.addWidget(fontbox) def import_lua(self): return def export_java(self): return def new_windows(self): sub = QMdiSubWindow() sub.setWidget(QTextEdit()) sub.setWindowTitle("New Article") sub.setGeometry(100, 100, 600, 320) self.mdi.addSubWindow(sub) sub.show() def create_top_group_box(self): top_splitter = QSplitter() top_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # 좌 top_left_group_box = QGroupBox("레이드 함수 목록") top_left_group_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) names = list(map(lambda i: i.get_name(), self._methods)) model = QStringListModel(names) view = QListView() view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) view.setModel(model) view.clicked.connect(self.on_show_method) add_function = QPushButton("함수 추가") layout = QVBoxLayout() layout.addWidget(add_function) layout.addWidget(view) top_left_group_box.setLayout(layout) # 우 top_right_group_box = QGroupBox("레이드 함수") # 우 - 텍스트 tab_method_body = QTabWidget() tab_block = QWidget() tab_plain = QWidget() self._edit_method_body_plain = QTextEdit() tab_hbox_plain = QHBoxLayout() tab_hbox_plain.setContentsMargins(5, 5, 5, 5) tab_hbox_plain.addWidget(self._edit_method_body_plain) self._edit_method_body_block = PyJavaCode() self._edit_method_body_block.set_plain_view( self._edit_method_body_plain) tab_hbox_block = QHBoxLayout() tab_hbox_block.setContentsMargins(5, 5, 5, 5) tab_hbox_block.addWidget(self._edit_method_body_block) tab_block.setLayout(tab_hbox_block) tab_plain.setLayout(tab_hbox_plain) tab_method_body.addTab(tab_block, "&Block") tab_method_body.addTab(tab_plain, "&Plain") # 배치 label_method_name = QLabel("name") self._edit_method_name = QLineEdit() label_method_body = QLabel("body") self._edit_method_body = tab_method_body # layout = QVBoxLayout() layout.addWidget(label_method_name) layout.addWidget(self._edit_method_name) layout.addWidget(label_method_body) layout.addWidget(self._edit_method_body) top_right_group_box.setLayout(layout) # test # top_left_group_box.setStyleSheet("color: blue;" # "background-color: #87CEFA;" # "border-style: dashed;" # "border-width: 3px;" # "border-color: #1E90FF") # # top_right_group_box.setStyleSheet("color: blue;" # "background-color: #87CEFA;" # "border-style: dashed;" # "border-width: 3px;" # "border-color: #1E90FF") # spliter top_splitter.addWidget(top_left_group_box) top_splitter.addWidget(top_right_group_box) return top_splitter def on_show_method(self, index): method = self._methods[index.row()] self._edit_method_name.setText(method.get_name()) self.plain_to_block(method.get_body()) self._edit_method_body_plain.setText(method.get_body()) # pos_x = QCursor.pos().x() - self.pos().x() pos_y = QCursor.pos().y() - self.pos().y() - 30 if self._popup: self._popup.hide() # self._popup = method.display_overlay(self, pos_x, pos_y) def plain_to_block(self, plain): self._edit_method_body_block.clear_widgets() for row in plain.split('\n'): self._edit_method_body_block.append_plain(row)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI Test") def closeEvent(self, event): self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): event.ignore() else: self.writeSettings() event.accept() def newFile(self): child = self.createMdiChild() child.newFile() child.show() def open(self): fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findMdiChild(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createMdiChild() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): if self.activeMdiChild() and self.activeMdiChild().save(): self.statusBar().showMessage("File saved", 2000) def saveAs(self): if self.activeMdiChild() and self.activeMdiChild().saveAs(): self.statusBar().showMessage("File saved", 2000) def cut(self): if self.activeMdiChild(): self.activeMdiChild().cut() def copy(self): if self.activeMdiChild(): self.activeMdiChild().copy() def paste(self): if self.activeMdiChild(): self.activeMdiChild().paste() def about(self): QMessageBox.about( self, "About MDI", "The <b>MDI</b> example demonstrates how to write multiple " "document interface applications using Qt.") def updateMenus(self): hasMdiChild = (self.activeMdiChild() is not None) #self.saveAct.setEnabled(hasMdiChild) #self.saveAsAct.setEnabled(hasMdiChild) self.pasteAct.setEnabled(hasMdiChild) self.closeAct.setEnabled(hasMdiChild) self.closeAllAct.setEnabled(hasMdiChild) self.tileAct.setEnabled(hasMdiChild) self.cascadeAct.setEnabled(hasMdiChild) self.nextAct.setEnabled(hasMdiChild) self.previousAct.setEnabled(hasMdiChild) self.separatorAct.setVisible(hasMdiChild) hasSelection = (self.activeMdiChild() is not None and self.activeMdiChild().textCursor().hasSelection()) self.cutAct.setEnabled(hasSelection) self.copyAct.setEnabled(hasSelection) def updateWindowMenu(self): self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.tileAct) self.windowMenu.addAction(self.cascadeAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeMdiChild()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createMdiChild(self): child = MdiChild() self.mdiArea.addSubWindow(child) child.copyAvailable.connect(self.cutAct.setEnabled) child.copyAvailable.connect(self.copyAct.setEnabled) return child # showntell def createMdiChild15(self): child = MdiChild_ShowNTell() self.mdiArea.addSubWindow(child) return child #MNIST def show_n_tell(self): print('show_n_tell....') child = self.createMdiChild15() print('self.createMdiChild15') #child.resize(830,480) #print('self.createMdiChild15') child.show print('child.show()') def createActions(self): self.cutAct = QAction( QIcon(':/images/cut.png'), "Cu&t", self, shortcut=QKeySequence.Cut, statusTip="Cut the current selection's contents to the clipboard", triggered=self.cut) self.copyAct = QAction( QIcon(':/images/copy.png'), "&Copy", self, shortcut=QKeySequence.Copy, statusTip="Copy the current selection's contents to the clipboard", triggered=self.copy) self.pasteAct = QAction( QIcon(':/images/paste.png'), "&Paste", self, shortcut=QKeySequence.Paste, statusTip= "Paste the clipboard's contents into the current selection", triggered=self.paste) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.tileAct = QAction("&Tile", self, statusTip="Tile the windows", triggered=self.mdiArea.tileSubWindows) self.cascadeAct = QAction("&Cascade", self, statusTip="Cascade the windows", triggered=self.mdiArea.cascadeSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction( "Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) # 메뉴 ACTION을 다이나믹하게 연결해준다 self.MenuActRef = { 'NewFileAct': 0, 'OpnFileAct': 0, 'SavFileAct': 0, 'SavASFileAct': 0, 'AboutAct': 0, 'AboutQTAct': 0, 'ExitAct': 0, 'SwitchLayout': 0, 'ShowNTell': 0 } # ******* Create the File Menu ******* self.NewFileAct = QAction(QIcon(':/images/new.png'), '&New File', self) self.NewFileAct.setShortcut("Ctrl+N") self.NewFileAct.setStatusTip('Create a New File') self.NewFileAct.triggered.connect(self.newFile) self.MenuActRef['NewFileAct'] = self.NewFileAct #self.newAct = QAction(QIcon(':/images/new.png'), "&New", self, # shortcut=QKeySequence.New, statusTip="Create a new file", # triggered=self.newFile) # ******* Open File Menu Items ******* self.OpnFileAct = QAction(QIcon(':/images/open.png'), '&Open File', self) self.OpnFileAct.setShortcut("Ctrl+O") self.OpnFileAct.setStatusTip('Open an Existing File') self.OpnFileAct.triggered.connect(self.open) self.MenuActRef['OpnFileAct'] = self.OpnFileAct #self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self, # shortcut=QKeySequence.Open, statusTip="Open an existing file", # triggered=self.open) # ******* Save File Menu Items ******* self.SavFileAct = QAction(QIcon(':/images/save.png'), '&Save File', self) self.SavFileAct.setShortcut("Ctrl+S") self.SavFileAct.setStatusTip('Save Current File') self.SavFileAct.triggered.connect(self.save) self.MenuActRef['SavFileAct'] = self.SavFileAct #self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self, # shortcut=QKeySequence.Save, # statusTip="Save the document to disk", triggered=self.save) # ******* SaveAS File Menu Items ******* self.SavASFileAct = QAction('Save &As File', self) self.SavASFileAct.setShortcut("Ctrl+A") self.SavASFileAct.setStatusTip('Save Current File under a new name ') self.SavASFileAct.triggered.connect(self.saveAs) self.MenuActRef['SavASFileAct'] = self.SavASFileAct #self.saveAsAct = QAction("Save &As...", self, # shortcut=QKeySequence.SaveAs, # statusTip="Save the document under a new name", # triggered=self.saveAs) # ******* About Menu Items ******* self.aboutAct = QAction("&About", self) self.aboutAct.setStatusTip("Show the application's About box") self.aboutAct.triggered.connect(self.about) self.MenuActRef['AboutAct'] = self.aboutAct # ******* About QT Menu Items ******* self.aboutAct = QAction("About &Qt", self) self.aboutAct.setStatusTip("Show the Qt library's About box") self.aboutAct.triggered.connect(QApplication.instance().aboutQt) self.MenuActRef['AboutQTAct'] = self.aboutAct # ******* Exit Menu Items ******* self.exitAct = QAction("E&xit", self) self.exitAct.setStatusTip("Exit the application") self.exitAct.triggered.connect(QApplication.instance().closeAllWindows) self.MenuActRef['ExitAct'] = self.exitAct #self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit, # statusTip="Exit the application", # triggered=QApplication.instance().closeAllWindows) # ******* Switch layout Items ******* self.SwitchLayout = QAction("&Switch layout direction", self) self.SwitchLayout.setStatusTip("Switch layout direction") self.SwitchLayout.triggered.connect(self.switchLayoutDirection) self.MenuActRef['SwitchLayout'] = self.SwitchLayout #self.SwitchLayout = QAction("&Switch layout direction", self, # statusTip="Switch layout direction", # triggered=self.switchLayoutDirection) # ******* Switch layout Items ******* self.ShowNTell = QAction("&ShowNTell", self) self.ShowNTell.setStatusTip("&ShowNTell") self.ShowNTell.triggered.connect(self.show_n_tell) self.MenuActRef['ShowNTell'] = self.ShowNTell #self.ShowNTell = QAction("&show_n_tell", self, # statusTip="show_n_tell", # triggered=self.show_n_tell) def createMenus(self): # 메뉴를 다이나믹하게 생성하고 연결함 self.MenuLayout = { 0: { 'addMenu': '&File', 'addToolMenu': 'File' }, 1: { 'addDynamic': 'NewFileAct', 'addToolbar': 'NewFileAct' }, 2: { 'addDynamic': 'OpnFileAct', 'addToolbar': 'OpnFileAct' }, 3: { 'addDynamic': 'SavFileAct', 'addToolbar': 'SavFileAct' }, 4: { 'addDynamic': 'SavASFileAct' }, 5: { 'addSeparator': '' }, 6: { 'addDynamic': 'SwitchLayout' }, 7: { 'addDynamic': 'ExitAct' }, 8: { 'addMenu': '&Edit' }, 9: { 'addAction': self.cutAct }, 10: { 'addAction': self.copyAct }, 11: { 'addAction': self.pasteAct }, 12: { 'addMenu': '&Window' }, 13: { 'updateMenu': '' }, 14: { 'addSeparator': '' }, 15: { 'addMenu': '&Help' }, 16: { 'addDynamic': 'AboutAct' }, 17: { 'addDynamic': 'AboutQTAct' }, 18: { 'addMenu': '&MNIST' }, 19: { 'addDynamic': 'ShowNTell' } } for idx in self.MenuLayout: item = self.MenuLayout[idx] if 'addMenu' in item.keys(): self.windowMenu = self.menuBar().addMenu(item['addMenu']) elif 'addAction' in item.keys(): self.windowMenu.addAction(item['addAction']) elif 'addSeparator' in item.keys(): self.windowMenu.addSeparator() elif 'updateMenu' in item.keys(): self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) # 메뉴 ACTION을 다이나믹하게 elif 'addDynamic' in item.keys(): self.windowMenu.addAction(self.MenuActRef[item['addDynamic']]) def createToolBars(self): for idx in self.MenuLayout: item = self.MenuLayout[idx] if 'addToolMenu' in item.keys(): self.fileToolBar = self.addToolBar(item['addToolMenu']) elif 'addToolbar' in item.keys(): self.fileToolBar.addAction(self.MenuActRef[item['addDynamic']]) #self.fileToolBar = self.addToolBar("File") #self.fileToolBar.addAction(self.MenuActRef['NewFileAct']) #self.fileToolBar.addAction(self.MenuActRef['OpnFileAct']) #self.fileToolBar.addAction(self.MenuActRef['SavFileAct']) #self.fileToolBar.addAction(self.newAct) #self.fileToolBar.addAction(self.openAct) #self.fileToolBar.addAction(self.saveAct) self.editToolBar = self.addToolBar("Edit") self.editToolBar.addAction(self.cutAct) self.editToolBar.addAction(self.copyAct) self.editToolBar.addAction(self.pasteAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def readSettings(self): settings = QSettings('NH-Soft', 'MDI Example') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(400, 400)) self.move(pos) self.resize(size) def writeSettings(self): settings = QSettings('NH-Soft', 'MDI Example') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeMdiChild(self): activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() return None def findMdiChild(self, fileName): canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def switchLayoutDirection(self): if self.layoutDirection() == Qt.LeftToRight: QApplication.setLayoutDirection(Qt.RightToLeft) else: QApplication.setLayoutDirection(Qt.LeftToRight) def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window)
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 MyMainWindow(QMainWindow): window_index = 1 def __init__(self): super(QMainWindow, self).__init__() self.mdi_area = QMdiArea() self.setCentralWidget(self.mdi_area) self.__init_menu() def __init_menu(self): menu_bar = self.menuBar() file_menu = menu_bar.addMenu('File') open_action = QAction('Open', self) open_action.setShortcut('Ctrl+O') open_action.triggered.connect(self.__on_open) file_menu.addAction(open_action) exit_action = QAction('Exit', self) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) window_menu = menu_bar.addMenu('Windows') close_current_action = QAction('Close Current Window', self) close_current_action.triggered.connect(self.mdi_area.closeActiveSubWindow) window_menu.addAction(close_current_action) close_all_action = QAction('Close All', self) close_all_action.triggered.connect(self.mdi_area.closeAllSubWindows) window_menu.addAction(close_all_action) cascade_action = QAction('Cascade Windows', self) cascade_action.triggered.connect(self.mdi_area.cascadeSubWindows) window_menu.addAction(cascade_action) tile_action = QAction('Title Windows', self) tile_action.triggered.connect(self.mdi_area.tileSubWindows) window_menu.addAction(tile_action) self.__window_list_menu = window_menu.addMenu('Window List') self.__window_list_menu.aboutToShow.connect(self.__window_list_menu_about_to_show) def __window_list_menu_about_to_show(self): self.__window_list_menu.clear() windows = self.mdi_area.subWindowList() index = 1 for window in windows: action = QAction(str(index) + '. ' + window.windowTitle(), self.__window_list_menu) action.setProperty('WindowObject', window) action.triggered.connect(self.__on_select_window) self.__window_list_menu.addAction(action) index += 1 def __on_select_window(self): action = self.sender() window = action.property('WindowObject') self.mdi_area.setActiveSubWindow(window) def __on_open(self): window = QMdiSubWindow() window.setAttribute(Qt.WA_DeleteOnClose, True) window.setWindowTitle('Sub Window ' + str(MyMainWindow.window_index)) window.resize(300, 200) self.mdi_area.addSubWindow(window) window.show() MyMainWindow.window_index += 1
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI") def closeEvent(self, event): self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): event.ignore() else: self.writeSettings() event.accept() def newFile(self): child = self.createMdiChild() child.newFile() child.show() def open(self): fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findMdiChild(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createMdiChild() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): if self.activeMdiChild() and self.activeMdiChild().save(): self.statusBar().showMessage("File saved", 2000) def saveAs(self): if self.activeMdiChild() and self.activeMdiChild().saveAs(): self.statusBar().showMessage("File saved", 2000) def cut(self): if self.activeMdiChild(): self.activeMdiChild().cut() def copy(self): if self.activeMdiChild(): self.activeMdiChild().copy() def paste(self): if self.activeMdiChild(): self.activeMdiChild().paste() def about(self): QMessageBox.about(self, "About MDI", "The <b>MDI</b> example demonstrates how to write multiple " "document interface applications using Qt.") def updateMenus(self): hasMdiChild = (self.activeMdiChild() is not None) self.saveAct.setEnabled(hasMdiChild) self.saveAsAct.setEnabled(hasMdiChild) self.pasteAct.setEnabled(hasMdiChild) self.closeAct.setEnabled(hasMdiChild) self.closeAllAct.setEnabled(hasMdiChild) self.tileAct.setEnabled(hasMdiChild) self.cascadeAct.setEnabled(hasMdiChild) self.nextAct.setEnabled(hasMdiChild) self.previousAct.setEnabled(hasMdiChild) self.separatorAct.setVisible(hasMdiChild) hasSelection = (self.activeMdiChild() is not None and self.activeMdiChild().textCursor().hasSelection()) self.cutAct.setEnabled(hasSelection) self.copyAct.setEnabled(hasSelection) def updateWindowMenu(self): self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.tileAct) self.windowMenu.addAction(self.cascadeAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeMdiChild()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createMdiChild(self): child = MdiChild() self.mdiArea.addSubWindow(child) child.copyAvailable.connect(self.cutAct.setEnabled) child.copyAvailable.connect(self.copyAct.setEnabled) return child def createActions(self): self.newAct = QAction(QIcon(':/images/new.png'), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", triggered=self.save) self.saveAsAct = QAction("Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", triggered=self.saveAs) self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.cutAct = QAction(QIcon(':/images/cut.png'), "Cu&t", self, shortcut=QKeySequence.Cut, statusTip="Cut the current selection's contents to the clipboard", triggered=self.cut) self.copyAct = QAction(QIcon(':/images/copy.png'), "&Copy", self, shortcut=QKeySequence.Copy, statusTip="Copy the current selection's contents to the clipboard", triggered=self.copy) self.pasteAct = QAction(QIcon(':/images/paste.png'), "&Paste", self, shortcut=QKeySequence.Paste, statusTip="Paste the clipboard's contents into the current selection", triggered=self.paste) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.tileAct = QAction("&Tile", self, statusTip="Tile the windows", triggered=self.mdiArea.tileSubWindows) self.cascadeAct = QAction("&Cascade", self, statusTip="Cascade the windows", triggered=self.mdiArea.cascadeSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction("Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.aboutQtAct = QAction("About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newAct) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() action = self.fileMenu.addAction("Switch layout direction") action.triggered.connect(self.switchLayoutDirection) self.fileMenu.addAction(self.exitAct) self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.cutAct) self.editMenu.addAction(self.copyAct) self.editMenu.addAction(self.pasteAct) self.windowMenu = self.menuBar().addMenu("&Window") self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File") self.fileToolBar.addAction(self.newAct) self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.saveAct) self.editToolBar = self.addToolBar("Edit") self.editToolBar.addAction(self.cutAct) self.editToolBar.addAction(self.copyAct) self.editToolBar.addAction(self.pasteAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def readSettings(self): settings = QSettings('Trolltech', 'MDI Example') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(400, 400)) self.move(pos) self.resize(size) def writeSettings(self): settings = QSettings('Trolltech', 'MDI Example') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeMdiChild(self): activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() return None def findMdiChild(self, fileName): canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def switchLayoutDirection(self): if self.layoutDirection() == Qt.LeftToRight: QApplication.setLayoutDirection(Qt.RightToLeft) else: QApplication.setLayoutDirection(Qt.LeftToRight) def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window)
class appWindow(QMainWindow): """ Application entry point, subclasses QMainWindow and implements the main widget, sets necessary window behaviour etc. """ def __init__(self, parent=None): super(appWindow, self).__init__(parent) #create the menu bar self.createMenuBar() self.mdi = QMdiArea(self) #create area for files to be displayed self.mdi.setObjectName('mdi area') #create toolbar and add the toolbar plus mdi to layout self.createToolbar() #set flags so that window doesnt look weird self.mdi.setOption(QMdiArea.DontMaximizeSubWindowOnActivation, True) self.mdi.setTabsClosable(True) self.mdi.setTabsMovable(True) self.mdi.setDocumentMode(False) #declare main window layout self.setCentralWidget(self.mdi) # self.resize(1280, 720) #set collapse dim self.mdi.subWindowActivated.connect(self.tabSwitched) self.readSettings() def createMenuBar(self): # Fetches a reference to the menu bar in the main window, and adds actions to it. titleMenu = self.menuBar() #fetch reference to current menu bar self.menuFile = titleMenu.addMenu('File') #File Menu newAction = self.menuFile.addAction("New", self.newProject) openAction = self.menuFile.addAction("Open", self.openProject) saveAction = self.menuFile.addAction("Save", self.saveProject) newAction.setShortcut(QKeySequence.New) openAction.setShortcut(QKeySequence.Open) saveAction.setShortcut(QKeySequence.Save) self.menuEdit = titleMenu.addMenu('Edit') undoAction = self.undo = self.menuEdit.addAction( "Undo", lambda x=self: x.activeScene.painter.undoAction.trigger()) redoAction = self.redo = self.menuEdit.addAction( "Redo", lambda x=self: x.activeScene.painter.redoAction.trigger()) undoAction.setShortcut(QKeySequence.Undo) redoAction.setShortcut(QKeySequence.Redo) self.menuEdit.addAction( "Show Undo Stack", lambda x=self: x.activeScene.painter.createUndoView(self)) self.menuEdit.addSeparator() self.menuEdit.addAction("Add new symbols", self.addSymbolWindow) self.menuGenerate = titleMenu.addMenu('Generate') #Generate menu imageAction = self.menuGenerate.addAction("Image", self.saveImage) reportAction = self.menuGenerate.addAction("Report", self.generateReport) imageAction.setShortcut(QKeySequence("Ctrl+P")) reportAction.setShortcut(QKeySequence("Ctrl+R")) def createToolbar(self): #place holder for toolbar with fixed width, layout may change self.toolbar = toolbar(self) self.toolbar.setObjectName("Toolbar") # self.addToolBar(Qt.LeftToolBarArea, self.toolbar) self.addDockWidget(Qt.LeftDockWidgetArea, self.toolbar) self.toolbar.toolbuttonClicked.connect(self.toolButtonClicked) self.toolbar.populateToolbar() def toolButtonClicked(self, object): # To add the corresponding symbol for the clicked button to active scene. if self.mdi.currentSubWindow(): currentDiagram = self.mdi.currentSubWindow().tabber.currentWidget( ).painter if currentDiagram: graphic = getattr(shapes, object['object'])(*map( lambda x: int(x) if x.isdigit() else x, object['args'])) graphic.setPos(50, 50) currentDiagram.addItemPlus(graphic) def addSymbolWindow(self): # Opens the add symbol window when requested from utils.custom import ShapeDialog ShapeDialog(self).exec() def newProject(self): #call to create a new file inside mdi area project = FileWindow(self.mdi) project.setObjectName("New Project") self.mdi.addSubWindow(project) if not project.tabList: # important when unpickling a file instead project.newDiagram() #create a new tab in the new file project.fileCloseEvent.connect( self.fileClosed) #closed file signal to switch to sub window view if self.count > 1: #switch to tab view if needed self.mdi.setViewMode(QMdiArea.TabbedView) project.show() def openProject(self): #show the open file dialog to open a saved file, then unpickle it. name = QFileDialog.getOpenFileNames(self, 'Open File(s)', '', 'Process Flow Diagram (*pfd)') if name: for files in name[0]: with open(files, 'r') as file: projectData = load(file) project = FileWindow(self.mdi) self.mdi.addSubWindow(project) #create blank window and set its state project.__setstate__(projectData) project.resizeHandler() project.fileCloseEvent.connect(self.fileClosed) project.show() if self.count > 1: # self.tabSpace.setVisible(True) self.mdi.setViewMode(QMdiArea.TabbedView) def saveProject(self): #serialize all files in mdi area for j, i in enumerate(self.activeFiles ): #get list of all windows with atleast one tab if i.tabCount: name = QFileDialog.getSaveFileName( self, 'Save File', f'New Diagram {j}', 'Process Flow Diagram (*.pfd)') i.saveProject(name) else: return False return True def saveImage(self): #place holder for future implementaion pass def generateReport(self): #place holder for future implementaion pass def tabSwitched(self, window): #handle window switched edge case if window and window.tabCount: window.resizeHandler() def resizeEvent(self, event): #overload resize to also handle resize on file windows inside for i in self.mdi.subWindowList(): i.resizeHandler() self.toolbar.resize() super(appWindow, self).resizeEvent(event) def closeEvent(self, event): #save alert on window close if len(self.activeFiles) and not dialogs.saveEvent(self): event.ignore() else: event.accept() self.writeSettings() def fileClosed(self, index): #checks if the file tab menu needs to be removed if self.count <= 2: self.mdi.setViewMode(QMdiArea.SubWindowView) def writeSettings(self): # write window state on window close settings.beginGroup("MainWindow") settings.setValue("maximized", self.isMaximized()) if not self.isMaximized(): settings.setValue("size", self.size()) settings.setValue("pos", self.pos()) settings.endGroup() def readSettings(self): # read window state when app launches settings.beginGroup("MainWindow") self.resize(settings.value("size", QSize(1280, 720))) self.move(settings.value("pos", QPoint(320, 124))) if settings.value("maximized", False, type=bool): self.showMaximized() settings.endGroup() #useful one liner properties for getting data @property def activeFiles(self): return [i for i in self.mdi.subWindowList() if i.tabCount] @property def count(self): return len(self.mdi.subWindowList()) @property def activeScene(self): return self.mdi.currentSubWindow().tabber.currentWidget() #Key input handler def keyPressEvent(self, event): #overload key press event for custom keyboard shortcuts if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_A: #todo implement selectAll for item in self.mdi.activeSubWindow().tabber.currentWidget( ).items: item.setSelected(True) #todo copy, paste, undo redo else: return event.accept() elif event.key() == Qt.Key_Q: if self.mdi.activeSubWindow() and self.mdi.activeSubWindow( ).tabber.currentWidget(): for item in self.mdi.activeSubWindow().tabber.currentWidget( ).painter.selectedItems(): item.rotation -= 1 elif event.key() == Qt.Key_E: if self.mdi.activeSubWindow() and self.mdi.activeSubWindow( ).tabber.currentWidget(): for item in self.mdi.activeSubWindow().tabber.currentWidget( ).painter.selectedItems(): item.rotation += 1
class Example(QMainWindow): def __init__(self): super().__init__() self.InitUI() def InitUI(self): self.setWindowTitle('扑克牌模拟') self.mid = QMdiArea() self.setCentralWidget(self.mid) sendOnecardAct = QAction(QIcon('./res/sendOnecard.ico'), '发1张牌', self) sendOnecardAct.triggered.connect(self.sendOnecard) sendFivecardsAct = QAction(QIcon('./res/sendFivecard.ico'), '随机5张牌', self) sendFivecardsAct.triggered.connect(self.sendFivecards) clearcardAct = QAction(QIcon('./res/clear.ico'), '清除牌', self) clearcardAct.triggered.connect(self.clearCards) foldcardAct = QAction(QIcon('./res/fold.ico'), '收牌', self) foldcardAct.triggered.connect(self.foldCards) toolbar = self.addToolBar('工具栏') toolbar.addAction(sendOnecardAct) toolbar.addAction(sendFivecardsAct) toolbar.addAction(clearcardAct) toolbar.addAction(foldcardAct) toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) def sendOnecard(self): randomflag = self.randomsend(1) subcard = QMdiSubWindow() subcard.setWidget(Card(randomflag)) self.mid.addSubWindow(subcard) subcard.setWindowFlags(Qt.WindowMinimizeButtonHint) subcard.show() def sendFivecards(self): randomflag = self.randomsend(5) for card in randomflag: subcard = QMdiSubWindow() subcard.setWidget(Card(card)) self.mid.addSubWindow(subcard) subcard.setWindowFlags(Qt.WindowMinimizeButtonHint) subcard.show() def clearCards(self): self.mid.closeAllSubWindows() def foldCards(self): self.mid.cascadeSubWindows() def randomsend(self, num): cardlist = [ "2", "3", "4", "5", "6", "7", "8", "9", "10", "a", "j", "joker", "k", "q" ] if num == 1: return random.choice(cardlist) elif num == 5: return random.sample(cardlist, 5)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI") def closeEvent(self, event): self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): event.ignore() else: self.writeSettings() event.accept() def newFile(self): child = self.createMdiChild() child.newFile() child.show() def open(self): fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findMdiChild(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createMdiChild() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): if self.activeMdiChild() and self.activeMdiChild().save(): self.statusBar().showMessage("File saved", 2000) def saveAs(self): if self.activeMdiChild() and self.activeMdiChild().saveAs(): self.statusBar().showMessage("File saved", 2000) def cut(self): if self.activeMdiChild(): self.activeMdiChild().cut() def copy(self): if self.activeMdiChild(): self.activeMdiChild().copy() def paste(self): if self.activeMdiChild(): self.activeMdiChild().paste() def about(self): QMessageBox.about( self, "About MDI", "The <b>MDI</b> example demonstrates how to write multiple " "document interface applications using Qt.") def updateMenus(self): hasMdiChild = (self.activeMdiChild() is not None) self.saveAct.setEnabled(hasMdiChild) self.saveAsAct.setEnabled(hasMdiChild) self.pasteAct.setEnabled(hasMdiChild) self.closeAct.setEnabled(hasMdiChild) self.closeAllAct.setEnabled(hasMdiChild) self.tileAct.setEnabled(hasMdiChild) self.cascadeAct.setEnabled(hasMdiChild) self.nextAct.setEnabled(hasMdiChild) self.previousAct.setEnabled(hasMdiChild) self.separatorAct.setVisible(hasMdiChild) hasSelection = (self.activeMdiChild() is not None and self.activeMdiChild().textCursor().hasSelection()) self.cutAct.setEnabled(hasSelection) self.copyAct.setEnabled(hasSelection) def updateWindowMenu(self): self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.tileAct) self.windowMenu.addAction(self.cascadeAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeMdiChild()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createMdiChild(self): child = MdiChild() self.mdiArea.addSubWindow(child) child.copyAvailable.connect(self.cutAct.setEnabled) child.copyAvailable.connect(self.copyAct.setEnabled) return child def createActions(self): self.newAct = QAction(QIcon(':/images/new.png'), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", triggered=self.save) self.saveAsAct = QAction( "Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", triggered=self.saveAs) self.exitAct = QAction( "E&xit", self, shortcut=QKeySequence.Quit, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.cutAct = QAction( QIcon(':/images/cut.png'), "Cu&t", self, shortcut=QKeySequence.Cut, statusTip="Cut the current selection's contents to the clipboard", triggered=self.cut) self.suggestAct = QAction( "Suggest", self, shortcut=QKeySequence.Copy, statusTip="Cut the current selection's contents to the clipboard", triggered=self.copy) self.copyAct = QAction( QIcon(':/images/copy.png'), "&Copy", self, shortcut=QKeySequence.Copy, statusTip="Copy the current selection's contents to the clipboard", triggered=self.copy) self.pasteAct = QAction( QIcon(':/images/paste.png'), "&Paste", self, shortcut=QKeySequence.Paste, statusTip= "Paste the clipboard's contents into the current selection", triggered=self.paste) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.tileAct = QAction("&Tile", self, statusTip="Tile the windows", triggered=self.mdiArea.tileSubWindows) self.cascadeAct = QAction("&Cascade", self, statusTip="Cascade the windows", triggered=self.mdiArea.cascadeSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction( "Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.aboutQtAct = QAction("About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newAct) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() action = self.fileMenu.addAction("Switch layout direction") action.triggered.connect(self.switchLayoutDirection) self.fileMenu.addAction(self.exitAct) self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.cutAct) self.editMenu.addAction(self.copyAct) self.editMenu.addAction(self.pasteAct) self.windowMenu = self.menuBar().addMenu("&Window") self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File") self.fileToolBar.addAction(self.newAct) self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.saveAct) self.editToolBar = self.addToolBar("Edit") self.editToolBar.addAction(self.cutAct) self.editToolBar.addAction(self.copyAct) self.editToolBar.addAction(self.pasteAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def readSettings(self): settings = QSettings('Trolltech', 'MDI Example') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(400, 400)) self.move(pos) self.resize(size) def writeSettings(self): settings = QSettings('Trolltech', 'MDI Example') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeMdiChild(self): activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() return None def findMdiChild(self, fileName): canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def switchLayoutDirection(self): if self.layoutDirection() == Qt.LeftToRight: QApplication.setLayoutDirection(Qt.RightToLeft) else: QApplication.setLayoutDirection(Qt.LeftToRight) def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window) def contextMenuEvent(self, event): menu = QMenu(self) menu.addAction(self.cutAct) menu.addAction(self.copyAct) menu.addAction(self.pasteAct) menu.addAction(self.suggestAct) menu.exec_(event.globalPos())
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self._version = __version__ self.setWindowIcon(QIcon(":/logo.png")) self.setWindowTitle("Tasmota Device Manager {}".format(self._version)) self.unknown = [] self.env = TasmotaEnvironment() self.device = None self.topics = [] self.mqtt_queue = [] self.fulltopic_queue = [] # ensure TDM directory exists in the user directory if not os.path.isdir("{}/TDM".format(QDir.homePath())): os.mkdir("{}/TDM".format(QDir.homePath())) self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.devices = QSettings("{}/TDM/devices.cfg".format(QDir.homePath()), QSettings.IniFormat) self.setMinimumSize(QSize(1000, 600)) # configure logging logging.basicConfig(filename="{}/TDM/tdm.log".format(QDir.homePath()), level=self.settings.value("loglevel", "INFO"), datefmt="%Y-%m-%d %H:%M:%S", format='%(asctime)s [%(levelname)s] %(message)s') logging.info("### TDM START ###") # load devices from the devices file, create TasmotaDevices and add the to the envvironment for mac in self.devices.childGroups(): self.devices.beginGroup(mac) device = TasmotaDevice(self.devices.value("topic"), self.devices.value("full_topic"), self.devices.value("friendly_name")) device.debug = self.devices.value("debug", False, bool) device.p['Mac'] = mac.replace("-", ":") device.env = self.env self.env.devices.append(device) # load device command history self.devices.beginGroup("history") for k in self.devices.childKeys(): device.history.append(self.devices.value(k)) self.devices.endGroup() self.devices.endGroup() self.device_model = TasmotaDevicesModel(self.env) self.setup_mqtt() self.setup_main_layout() self.add_devices_tab() self.build_mainmenu() # self.build_toolbars() self.setStatusBar(QStatusBar()) pbSubs = QPushButton("Show subscriptions") pbSubs.setFlat(True) pbSubs.clicked.connect(self.showSubs) self.statusBar().addPermanentWidget(pbSubs) self.queue_timer = QTimer() self.queue_timer.timeout.connect(self.mqtt_publish_queue) self.queue_timer.start(250) self.auto_timer = QTimer() self.auto_timer.timeout.connect(self.auto_telemetry) self.load_window_state() if self.settings.value("connect_on_startup", False, bool): self.actToggleConnect.trigger() self.tele_docks = {} self.consoles = [] def setup_main_layout(self): self.mdi = QMdiArea() self.mdi.setActivationOrder(QMdiArea.ActivationHistoryOrder) self.mdi.setTabsClosable(True) self.setCentralWidget(self.mdi) def setup_mqtt(self): self.mqtt = MqttClient() self.mqtt.connecting.connect(self.mqtt_connecting) self.mqtt.connected.connect(self.mqtt_connected) self.mqtt.disconnected.connect(self.mqtt_disconnected) self.mqtt.connectError.connect(self.mqtt_connectError) self.mqtt.messageSignal.connect(self.mqtt_message) def add_devices_tab(self): self.devices_list = ListWidget(self) sub = self.mdi.addSubWindow(self.devices_list) sub.setWindowState(Qt.WindowMaximized) self.devices_list.deviceSelected.connect(self.selectDevice) self.devices_list.openConsole.connect(self.openConsole) self.devices_list.openRulesEditor.connect(self.openRulesEditor) self.devices_list.openTelemetry.connect(self.openTelemetry) self.devices_list.openWebUI.connect(self.openWebUI) def load_window_state(self): wndGeometry = self.settings.value('window_geometry') if wndGeometry: self.restoreGeometry(wndGeometry) def build_mainmenu(self): mMQTT = self.menuBar().addMenu("MQTT") self.actToggleConnect = QAction(QIcon(":/disconnect.png"), "Connect") self.actToggleConnect.setCheckable(True) self.actToggleConnect.toggled.connect(self.toggle_connect) mMQTT.addAction(self.actToggleConnect) mMQTT.addAction(QIcon(), "Broker", self.setup_broker) mMQTT.addAction(QIcon(), "Autodiscovery patterns", self.patterns) mMQTT.addSeparator() mMQTT.addAction(QIcon(), "Clear obsolete retained LWTs", self.clear_LWT) mMQTT.addSeparator() mMQTT.addAction(QIcon(), "Auto telemetry period", self.auto_telemetry_period) self.actToggleAutoUpdate = QAction(QIcon(":/auto_telemetry.png"), "Auto telemetry") self.actToggleAutoUpdate.setCheckable(True) self.actToggleAutoUpdate.toggled.connect(self.toggle_autoupdate) mMQTT.addAction(self.actToggleAutoUpdate) mSettings = self.menuBar().addMenu("Settings") mSettings.addAction(QIcon(), "BSSId aliases", self.bssid) mSettings.addSeparator() mSettings.addAction(QIcon(), "Preferences", self.prefs) # mExport = self.menuBar().addMenu("Export") # mExport.addAction(QIcon(), "OpenHAB", self.openhab) def build_toolbars(self): main_toolbar = Toolbar(orientation=Qt.Horizontal, iconsize=24, label_position=Qt.ToolButtonTextBesideIcon) main_toolbar.setObjectName("main_toolbar") def initial_query(self, device, queued=False): for c in initial_commands(): cmd, payload = c cmd = device.cmnd_topic(cmd) if queued: self.mqtt_queue.append([cmd, payload]) else: self.mqtt.publish(cmd, payload, 1) def setup_broker(self): brokers_dlg = BrokerDialog() if brokers_dlg.exec_( ) == QDialog.Accepted and self.mqtt.state == self.mqtt.Connected: self.mqtt.disconnect() def toggle_autoupdate(self, state): if state == True: if self.mqtt.state == self.mqtt.Connected: for d in self.env.devices: self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8) self.auto_timer.setInterval( self.settings.value("autotelemetry", 5000, int)) self.auto_timer.start() else: self.auto_timer.stop() def toggle_connect(self, state): if state and self.mqtt.state == self.mqtt.Disconnected: self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) self.mqtt.connectToHost() elif not state and self.mqtt.state == self.mqtt.Connected: self.mqtt_disconnect() def auto_telemetry(self): if self.mqtt.state == self.mqtt.Connected: for d in self.env.devices: self.mqtt.publish(d.cmnd_topic('STATUS'), payload=8) def mqtt_connect(self): self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) if self.mqtt.state == self.mqtt.Disconnected: self.mqtt.connectToHost() def mqtt_disconnect(self): self.mqtt.disconnectFromHost() def mqtt_connecting(self): self.statusBar().showMessage("Connecting to broker") def mqtt_connected(self): self.actToggleConnect.setIcon(QIcon(":/connect.png")) self.actToggleConnect.setText("Disconnect") self.statusBar().showMessage("Connected to {}:{} as {}".format( self.broker_hostname, self.broker_port, self.broker_username if self.broker_username else '[anonymous]')) self.mqtt_subscribe() def mqtt_subscribe(self): # clear old topics self.topics.clear() custom_patterns.clear() # load custom autodiscovery patterns self.settings.beginGroup("Patterns") for k in self.settings.childKeys(): custom_patterns.append(self.settings.value(k)) self.settings.endGroup() # expand fulltopic patterns to subscribable topics for pat in default_patterns: # tasmota default and SO19 self.topics += expand_fulltopic(pat) # check if custom patterns can be matched by default patterns for pat in custom_patterns: if pat.startswith("%prefix%") or pat.split('/')[1] == "%prefix%": continue # do nothing, default subcriptions will match this topic else: self.topics += expand_fulltopic(pat) for d in self.env.devices: # if device has a non-standard pattern, check if the pattern is found in the custom patterns if not d.is_default() and d.p['FullTopic'] not in custom_patterns: # if pattern is not found then add the device topics to subscription list. # if the pattern is found, it will be matched without implicit subscription self.topics += expand_fulltopic(d.p['FullTopic']) # passing a list of tuples as recommended by paho self.mqtt.subscribe([(topic, 0) for topic in self.topics]) @pyqtSlot(str, str) def mqtt_publish(self, t, p): self.mqtt.publish(t, p) def mqtt_publish_queue(self): for q in self.mqtt_queue: t, p = q self.mqtt.publish(t, p) self.mqtt_queue.pop(self.mqtt_queue.index(q)) def mqtt_disconnected(self): self.actToggleConnect.setIcon(QIcon(":/disconnect.png")) self.actToggleConnect.setText("Connect") self.statusBar().showMessage("Disconnected") def mqtt_connectError(self, rc): reason = { 1: "Incorrect protocol version", 2: "Invalid client identifier", 3: "Server unavailable", 4: "Bad username or password", 5: "Not authorized", } self.statusBar().showMessage("Connection error: {}".format(reason[rc])) self.actToggleConnect.setChecked(False) def mqtt_message(self, topic, msg): # try to find a device by matching known FullTopics against the MQTT topic of the message device = self.env.find_device(topic) if device: if topic.endswith("LWT"): if not msg: msg = "Offline" device.update_property("LWT", msg) if msg == 'Online': # known device came online, query initial state self.initial_query(device, True) else: # forward the message for processing device.parse_message(topic, msg) if device.debug: logging.debug("MQTT: %s %s", topic, msg) else: # unknown device, start autodiscovery process if topic.endswith("LWT"): self.env.lwts.append(topic) logging.info("DISCOVERY: LWT from an unknown device %s", topic) # STAGE 1 # load default and user-provided FullTopic patterns and for all the patterns, # try matching the LWT topic (it follows the device's FullTopic syntax for p in default_patterns + custom_patterns: match = re.fullmatch( p.replace("%topic%", "(?P<topic>.*?)").replace( "%prefix%", "(?P<prefix>.*?)") + ".*$", topic) if match: # assume that the matched topic is the one configured in device settings possible_topic = match.groupdict().get('topic') if possible_topic not in ('tele', 'stat'): # if the assumed topic is different from tele or stat, there is a chance that it's a valid topic # query the assumed device for its FullTopic. False positives won't reply. possible_topic_cmnd = p.replace( "%prefix%", "cmnd").replace( "%topic%", possible_topic) + "FullTopic" logging.debug( "DISCOVERY: Asking an unknown device for FullTopic at %s", possible_topic_cmnd) self.mqtt_queue.append([possible_topic_cmnd, ""]) elif topic.endswith("RESULT") or topic.endswith( "FULLTOPIC"): # reply from an unknown device # STAGE 2 full_topic = loads(msg).get('FullTopic') if full_topic: # the device replies with its FullTopic # here the Topic is extracted using the returned FullTopic, identifying the device parsed = parse_topic(full_topic, topic) if parsed: # got a match, we query the device's MAC address in case it's a known device that had its topic changed logging.debug( "DISCOVERY: topic %s is matched by fulltopic %s", topic, full_topic) d = self.env.find_device(topic=parsed['topic']) if d: d.update_property("FullTopic", full_topic) else: logging.info( "DISCOVERY: Discovered topic=%s with fulltopic=%s", parsed['topic'], full_topic) d = TasmotaDevice(parsed['topic'], full_topic) self.env.devices.append(d) self.device_model.addDevice(d) logging.debug( "DISCOVERY: Sending initial query to topic %s", parsed['topic']) self.initial_query(d, True) self.env.lwts.remove(d.tele_topic("LWT")) d.update_property("LWT", "Online") def export(self): fname, _ = QFileDialog.getSaveFileName(self, "Export device list as...", directory=QDir.homePath(), filter="CSV files (*.csv)") if fname: if not fname.endswith(".csv"): fname += ".csv" with open(fname, "w", encoding='utf8') as f: column_titles = [ 'mac', 'topic', 'friendly_name', 'full_topic', 'cmnd_topic', 'stat_topic', 'tele_topic', 'module', 'module_id', 'firmware', 'core' ] c = csv.writer(f) c.writerow(column_titles) for r in range(self.device_model.rowCount()): d = self.device_model.index(r, 0) c.writerow([ self.device_model.mac(d), self.device_model.topic(d), self.device_model.friendly_name(d), self.device_model.fullTopic(d), self.device_model.commandTopic(d), self.device_model.statTopic(d), self.device_model.teleTopic(d), # modules.get(self.device_model.module(d)), self.device_model.module(d), self.device_model.firmware(d), self.device_model.core(d) ]) def bssid(self): BSSIdDialog().exec_() def patterns(self): PatternsDialog().exec_() # def openhab(self): # OpenHABDialog(self.env).exec_() def showSubs(self): QMessageBox.information(self, "Subscriptions", "\n".join(sorted(self.topics))) def clear_LWT(self): dlg = ClearLWTDialog(self.env) if dlg.exec_() == ClearLWTDialog.Accepted: for row in range(dlg.lw.count()): itm = dlg.lw.item(row) if itm.checkState() == Qt.Checked: topic = itm.text() self.mqtt.publish(topic, retain=True) self.env.lwts.remove(topic) logging.info("MQTT: Cleared %s", topic) def prefs(self): dlg = PrefsDialog() if dlg.exec_() == QDialog.Accepted: update_devices = False devices_short_version = self.settings.value( "devices_short_version", True, bool) if devices_short_version != dlg.cbDevShortVersion.isChecked(): update_devices = True self.settings.setValue("devices_short_version", dlg.cbDevShortVersion.isChecked()) update_consoles = False console_font_size = self.settings.value("console_font_size", 9) if console_font_size != dlg.sbConsFontSize.value(): update_consoles = True self.settings.setValue("console_font_size", dlg.sbConsFontSize.value()) console_word_wrap = self.settings.value("console_word_wrap", True, bool) if console_word_wrap != dlg.cbConsWW.isChecked(): update_consoles = True self.settings.setValue("console_word_wrap", dlg.cbConsWW.isChecked()) if update_consoles: for c in self.consoles: c.console.setWordWrapMode(dlg.cbConsWW.isChecked()) new_font = QFont(c.console.font()) new_font.setPointSize(dlg.sbConsFontSize.value()) c.console.setFont(new_font) self.settings.sync() def auto_telemetry_period(self): curr_val = self.settings.value("autotelemetry", 5000, int) period, ok = QInputDialog.getInt( self, "Set AutoTelemetry period", "Values under 5000ms may cause increased ESP LoadAvg", curr_val, 1000) if ok: self.settings.setValue("autotelemetry", period) self.settings.sync() @pyqtSlot(TasmotaDevice) def selectDevice(self, d): self.device = d @pyqtSlot() def openTelemetry(self): if self.device: tele_widget = TelemetryWidget(self.device) self.addDockWidget(Qt.RightDockWidgetArea, tele_widget) self.mqtt_publish(self.device.cmnd_topic('STATUS'), "8") @pyqtSlot() def openConsole(self): if self.device: console_widget = ConsoleWidget(self.device) self.mqtt.messageSignal.connect(console_widget.consoleAppend) console_widget.sendCommand.connect(self.mqtt.publish) self.addDockWidget(Qt.BottomDockWidgetArea, console_widget) console_widget.command.setFocus() self.consoles.append(console_widget) @pyqtSlot() def openRulesEditor(self): if self.device: rules = RulesWidget(self.device) self.mqtt.messageSignal.connect(rules.parseMessage) rules.sendCommand.connect(self.mqtt_publish) self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.addSubWindow(rules) rules.setWindowState(Qt.WindowMaximized) rules.destroyed.connect(self.updateMDI) self.mqtt_queue.append((self.device.cmnd_topic("ruletimer"), "")) self.mqtt_queue.append((self.device.cmnd_topic("rule1"), "")) self.mqtt_queue.append((self.device.cmnd_topic("Var"), "")) self.mqtt_queue.append((self.device.cmnd_topic("Mem"), "")) @pyqtSlot() def openWebUI(self): if self.device and self.device.p.get('IPAddress'): url = QUrl("http://{}".format(self.device.p['IPAddress'])) try: webui = QWebEngineView() webui.load(url) frm_webui = QFrame() frm_webui.setWindowTitle("WebUI [{}]".format( self.device.p['FriendlyName1'])) frm_webui.setFrameShape(QFrame.StyledPanel) frm_webui.setLayout(VLayout(0)) frm_webui.layout().addWidget(webui) frm_webui.destroyed.connect(self.updateMDI) self.mdi.addSubWindow(frm_webui) self.mdi.setViewMode(QMdiArea.TabbedView) frm_webui.setWindowState(Qt.WindowMaximized) except NameError: QDesktopServices.openUrl( QUrl("http://{}".format(self.device.p['IPAddress']))) def updateMDI(self): if len(self.mdi.subWindowList()) == 1: self.mdi.setViewMode(QMdiArea.SubWindowView) self.devices_list.setWindowState(Qt.WindowMaximized) def closeEvent(self, e): self.settings.setValue("version", self._version) self.settings.setValue("window_geometry", self.saveGeometry()) self.settings.setValue("views_order", ";".join(self.devices_list.views.keys())) self.settings.beginGroup("Views") for view, items in self.devices_list.views.items(): self.settings.setValue(view, ";".join(items[1:])) self.settings.endGroup() self.settings.sync() for d in self.env.devices: mac = d.p.get('Mac') topic = d.p['Topic'] full_topic = d.p['FullTopic'] friendly_name = d.p['FriendlyName1'] if mac: self.devices.beginGroup(mac.replace(":", "-")) self.devices.setValue("topic", topic) self.devices.setValue("full_topic", full_topic) self.devices.setValue("friendly_name", friendly_name) for i, h in enumerate(d.history): self.devices.setValue("history/{}".format(i), h) self.devices.endGroup() self.devices.sync() e.accept()
class MainWindow(QMainWindow): """This create the main window of the application""" def __init__(self): super(MainWindow, self).__init__() # remove close & maximize window buttons #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint) self.setMinimumSize(500, 666) #self.setMaximumSize(1000,666) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.child = None self.createActions() self.createMenus() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("LEKTURE") mytoolbar = QToolBar() #self.toolbar = self.addToolBar() mytoolbar.addAction(self.newAct) mytoolbar.addAction(self.openAct) mytoolbar.addAction(self.saveAct) mytoolbar.addAction(self.saveAsAct) mytoolbar.addSeparator() mytoolbar.addAction(self.outputsAct) mytoolbar.addAction(self.scenarioAct) self.scenarioAct.setVisible(False) mytoolbar.setMovable(False) mytoolbar.setFixedWidth(60) self.addToolBar(Qt.LeftToolBarArea, mytoolbar) def closeEvent(self, scenario): """method called when the main window wants to be closed""" self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): scenario.ignore() else: self.writeSettings() scenario.accept() def newFile(self): """creates a new project""" child = self.createProjekt() child.newFile() child.show() self.child = child def open(self): """open a project""" fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findProjekt(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createProjekt() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): """called when user save a project""" if self.activeProjekt() and self.activeProjekt().save(): self.statusBar().showMessage("File saved", 2000) else: self.statusBar().showMessage("Error when trying to save the file") def saveAs(self): """called when user save AS a project""" if self.activeProjekt() and self.activeProjekt().saveAs(): self.statusBar().showMessage("File saved", 2000) else: self.statusBar().showMessage("Error when trying to save the file") def openFolder(self): """called when user calls 'reveal in finder' function""" if self.activeProjekt() and self.activeProjekt().openFolder(): self.statusBar().showMessage("File revealed in Finder", 2000) def about(self): """called when user wants to know a bit more on the app""" import sys python_version = str(sys.version_info[0]) python_version_temp = sys.version_info[1:5] for item in python_version_temp: python_version = python_version + "." + str(item) QMessageBox.about(self, "About Lekture", "pylekture build " + str(pylekture.__version__ + "\n" + \ "python version " + str(python_version))) def updateMenus(self): """update menus""" hasProjekt = (self.activeProjekt() is not None) self.saveAct.setEnabled(hasProjekt) self.saveAsAct.setEnabled(hasProjekt) self.outputsAct.setEnabled(hasProjekt) self.scenarioAct.setEnabled(hasProjekt) self.openFolderAct.setEnabled(hasProjekt) self.closeAct.setEnabled(hasProjekt) self.closeAllAct.setEnabled(hasProjekt) self.nextAct.setEnabled(hasProjekt) self.previousAct.setEnabled(hasProjekt) self.separatorAct.setVisible(hasProjekt) def updateWindowMenu(self): """unpates menus on the window toolbar""" self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeProjekt()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createProjekt(self): """create a new project""" child = Projekt() self.mdiArea.addSubWindow(child) self.child = child return child def createActions(self): """create all actions""" self.newAct = QAction("&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction("&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", triggered=self.save) self.saveAsAct = QAction("Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", triggered=self.saveAs) self.openFolderAct = QAction("Open Project Folder", self, statusTip="Reveal Project in Finder", triggered=self.openFolder) self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.outputsAct = QAction("Outputs", self, statusTip="Open the outputs panel", triggered=self.openOutputsPanel) self.scenarioAct = QAction("Scenario", self, statusTip="Open the scenario panel", triggered=self.openScenarioPanel) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction("Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) def createMenus(self): """create all menus""" self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newAct) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.openFolderAct) self.fileMenu.addAction(self.exitAct) self.viewMenu = self.menuBar().addMenu("&View") self.viewMenu.addAction(self.outputsAct) self.viewMenu.addAction(self.scenarioAct) self.windowMenu = self.menuBar().addMenu("&Window") self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) def createStatusBar(self): """create the status bar""" self.statusBar().showMessage("Ready") def readSettings(self): """read the settings""" settings = QSettings('Pixel Stereo', 'lekture') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(1000, 650)) self.move(pos) self.resize(size) def writeSettings(self): """write settings""" settings = QSettings('Pixel Stereo', 'lekture') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeProjekt(self): """return the active project object""" activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() else: return None def findProjekt(self, fileName): """return the project""" canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def setActiveSubWindow(self, window): """set the active sub window""" if window: self.mdiArea.setActiveSubWindow(window) def openOutputsPanel(self): """switch to the outputs editor""" if self.child: project = self.activeProjekt() project.scenario_events_group.setVisible(False) project.outputs_group.setVisible(True) self.scenarioAct.setVisible(True) self.outputsAct.setVisible(False) def openScenarioPanel(self): """switch to the scenario editors""" if self.child: project = self.activeProjekt() project.outputs_group.setVisible(False) project.scenario_events_group.setVisible(True) self.scenarioAct.setVisible(False) self.outputsAct.setVisible(True)