Пример #1
0
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()
Пример #2
0
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()
Пример #3
0
class MainWindow(QMainWindow):
    """Handle all subwindows."""
    def __init__(self, s=None, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.settings = s
        self.actions = {}
        self.views = {}

        self.toolbar = None
        self.init_ui()
        self.init_actions()
        self.init_menu()

        self.add_view(CensusView(), 'census')
        self.add_view(GedcomView(), 'gedcom')
        self.add_view(DNAView(), 'dna')

        open_windows = self.settings.get('window')
        for window, state in open_windows.items():
            if state:
                self.actions[window].trigger()

    def init_ui(self):
        """Initialize UI."""
        main_size = self.settings.get('main.size')
        print(main_size)
        if main_size:
            self.resize(*main_size)
        else:
            self.resize(640, 480)

        self.central_widget = QMdiArea()

        self.setCentralWidget(self.central_widget)

    def init_menu(self):
        """Initialize the main menu."""
        menubar = self.menuBar()
        menubar.setNativeMenuBar(False)
        filemenu = menubar.addMenu('&File')
        filemenu.addAction(self.actions['preferences'])
        filemenu.addAction(self.actions['exit'])

        self.viewmenu = menubar.addMenu('&View')
        windowmenu = menubar.addMenu("&Window")
        windowmenu.addAction(self.actions['cascade'])
        windowmenu.addAction(self.actions['tile'])

        helpmenu = menubar.addMenu("&Help")
        helpmenu.addAction(self.actions['about'])

    def view_chosen(self, t):
        data = t.data()
        self.central_widget.set_current(data)
        self.settings.set('current_view', data)

    def init_toolbar(self):
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(self.actions['exit'])

    def init_actions(self):
        self.actions['exit'] = QAction(QIcon(ICONS['exit']), '&Exit', self)
        self.actions['exit'].setShortcut('Ctrl+Q')
        self.actions['exit'].menuRole = QAction.QuitRole
        self.actions['exit'].triggered.connect(self.close)

        self.actions['preferences'] = QAction("Preferences", self)
        self.actions['preferences'].menuRole = QAction.PreferencesRole
        self.actions['preferences'].triggered.connect(self.preferences)

        self.actions['about'] = QAction("&About", self)
        self.actions['about'].menuRole = QAction.AboutRole
        self.actions['about'].triggered.connect(self.about)

        self.actions['tile'] = QAction("Tile", self)
        self.actions['tile'].triggered.connect(
            self.central_widget.tileSubWindows)

        self.actions['cascade'] = QAction("Cascade", self)
        self.actions['cascade'].triggered.connect(
            self.central_widget.cascadeSubWindows)

    def toggle_view(self, actionname):
        action = self.actions[actionname]
        if action.isChecked():
            self.open_window(self.views[actionname])
            self.settings.set("window.{}".format(actionname), True)
        else:
            self.close_window(self.views[actionname])
            self.settings.set("window.{}".format(actionname), False)

    def about(self):
        about = QMessageBox()
        about.setTextFormat(Qt.RichText)
        about.setText("Created by Greger Stolt Nilsen")
        about.setInformativeText("<br />".join([
            "Icons from <a href='https://icons8.com/'>Icons8</a>",
            "Source at <a href='https://github.com/gregersn/gensplorer'>GitHub</a>"
        ]))
        about.exec_()

    def preferences(self):
        preferences = dialogs.Preferences(self.settings)
        preferences.exec_()

    def add_view(self, w, name):
        sub = QMdiSubWindow()
        sub.setWidget(w)
        self.views[name] = sub

        self.actions[name] = QAction(
            w.display_name if hasattr(w, 'display_name') else name,
            self,
            checkable=True)
        self.actions[name].setData(name)
        self.actions[name].triggered.connect(lambda: self.toggle_view(name))

        self.viewmenu.addAction(self.actions[name])

    def add_window(self, w):
        sub = QMdiSubWindow()
        sub.setWidget(w)
        self.central_widget.addSubWindow(sub)
        sub.show()

    def open_window(self, w):
        print("Opening window", w)
        self.central_widget.addSubWindow(w)
        w.show()

    def close_window(self, w):
        print("Closing window", w)
        self.central_widget.removeSubWindow(w)

    def closeEvent(self, event):
        print("Close event")

        self.settings.set("main.size",
                          (self.size().width(), self.size().height()))

        self.settings.save()
Пример #4
0
class Calibri(QMainWindow):

    def __init__(self):
        super().__init__()
        self.title = 'Calibri DataLogger'
        self.width = 800
        self.height = 600

        self.initUI()

        self.createActions()
        self.createTrayIcon()

        self.trayIcon.show()


    def initUI(self):
        self.driver_widget = Driver()
        self.device_widget = Device(self.driver_widget)
        self.about_widget = About()
        self.settings_widget = Settings()
        self.leakTest_widget = LeakTest()
        self.diagram_widget = Diagram()
        self.license_widget = License()
        self.dwt_widget = DWT(self.driver_widget)

        self.centralWidget = QMdiArea(self)
        self.setCentralWidget(self.centralWidget)

        self.sub1 = QMdiSubWindow()
        self.sub1.setWidget(self.driver_widget)
        self.centralWidget.addSubWindow(self.sub1)
        self.sub1.show()

        self.sub2 = QMdiSubWindow()
        self.sub2.setWidget(self.device_widget)
        self.centralWidget.addSubWindow(self.sub2)
        self.sub2.show()
        self.setWindowTitle("menu demo")
        self.showMaximized()

        self.setWindowTitle(self.title)
        self.setGeometry(0, 0, self.width, self.height)
        qtRectangle = self.frameGeometry()

        centerPoint = QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        self.move(qtRectangle.topLeft())


        mainMenu = self.menuBar()
        mainMenu.setNativeMenuBar(False)
        fileMenu = mainMenu.addMenu('File')
        viewMenu = mainMenu.addMenu('View')
        helpMenu = mainMenu.addMenu('Help')

        saveButton = QAction('Save last configuration ...', self)
        saveButton.setShortcut('Ctrl+S')
        saveButton.triggered.connect(self.connectDevice)
        fileMenu.addAction(saveButton)

        loadButton = QAction('Load last configuration ...', self)
        loadButton.setShortcut('Ctrl+L')
        loadButton.triggered.connect(self.connectDevice)
        fileMenu.addAction(loadButton)

        connectButton = QAction('Connect to ...', self)
        connectButton.setShortcut('Ctrl+C')
        #connectButton.setStatusTip('Connect device')
        connectButton.triggered.connect(self.connectDevice)
        fileMenu.addAction(connectButton)

        disconnectButton = QAction('Disconnect all', self)
        disconnectButton.setShortcut('Ctrl+D')
        #connectButton.setStatusTip('Connect device')
        disconnectButton.triggered.connect(self.disconnectAll)
        fileMenu.addAction(disconnectButton)

        fileMenu.addSeparator()

        exitButton = QAction('Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.setStatusTip('Exit application')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        diagramButton = QAction('Show diagram', self)
        diagramButton.triggered.connect(self.openDiagram)
        viewMenu.addAction(diagramButton)

        leakTestButton = QAction('Leak test...', self)
        leakTestButton.triggered.connect(self.openLeakTest)
        viewMenu.addAction(leakTestButton)

        settingsButton = QAction('Settings', self)
        settingsButton.triggered.connect(self.openSettings)
        viewMenu.addAction(settingsButton)

        dwtButton = QAction('DWT', self)
        dwtButton.triggered.connect(self.openDWT)
        viewMenu.addAction(dwtButton)
        """
        helpButton = QAction(QIcon('icon.png'), 'Help', self)
        helpButton.setShortcut('Ctrl+H')
        helpButton.setStatusTip('Help')
        helpButton.triggered.connect(self.close)
        helpMenu.addAction(helpButton)
        """
        aboutButton = QAction(QIcon('icon.png'), 'About program', self)
        aboutButton.setShortcut('Ctrl+A')
        aboutButton.setStatusTip('About program')
        aboutButton.triggered.connect(self.openAbout)
        helpMenu.addAction(aboutButton)

        licenseButton = QAction(QIcon('icon.png'), 'Licence info', self)
        licenseButton.setShortcut('Ctrol+L')
        licenseButton.setStatusTip('License inforamation')
        licenseButton.triggered.connect(self.openLicense)
        helpMenu.addAction(licenseButton)

        self.statusBar().showMessage('DPI620 connected; PACE1000 connected')

        """
        wid = QWidget(self)
        self.setCentralWidget(wid)


        grid = QGridLayout()
        grid.setSpacing(10)
        wid.setLayout(grid)

        title = QLabel('Title')
        author = QLabel('Author')
        review = QLabel('Review')

        titleEdit = QLineEdit()
        authorEdit = QLineEdit()
        reviewEdit = QTextEdit()

        clearButton = QPushButton('Send', self)


        grid.addWidget(title, 1, 0)
        grid.addWidget(titleEdit, 1, 1)

        grid.addWidget(author, 2, 0)
        grid.addWidget(authorEdit, 2, 1)

        grid.addWidget(review, 3, 0)
        grid.addWidget(reviewEdit, 3, 1, 5, 1)

        grid.addWidget(clearButton, 9, 1)
        """

        # button.move(self.frameGeometry().width()-110, self.frameGeometry().height()-50)
        self.setGeometry(0, 0, 800, 600)
        qtRectangle = self.frameGeometry()

        centerPoint = QDesktopWidget().availableGeometry().center()
        qtRectangle.moveCenter(centerPoint)
        self.move(qtRectangle.topLeft())

        self.setWindowTitle('Calibri')
        #self.setWindowIcon(QIcon('plus.gif'))
        self.show()

    def connectDevice(self):
        print("connected")

    def disconnectAll(self):
        print("disconnect all")
        #self.centralWidget.removeSubWindow(self.settings_sub)

        self.centralWidget.removeSubWindow(self.settings_sub)
        self.centralWidget.removeSubWindow(self.about_sub)
        self.centralWidget.removeSubWindow(self.leakTest_sub)
        self.centralWidget.removeSubWindow(self.diagram_sub)


    def openAbout(self):
        self.about_sub = QMdiSubWindow()
        self.about_sub.setWidget(self.about_widget)
        self.centralWidget.addSubWindow(self.about_sub)
        self.about_sub.show()

    def openSettings(self):
        self.settings_sub = QMdiSubWindow()
        self.settings_sub.setWidget(self.settings_widget)
        self.centralWidget.addSubWindow(self.settings_sub)
        self.settings_sub.show()

    def openLeakTest(self):
        self.leakTest_sub = QMdiSubWindow()
        self.leakTest_sub.setWidget(self.leakTest_widget)
        self.centralWidget.addSubWindow(self.leakTest_sub)
        self.leakTest_sub.show()

    def openDiagram(self):
        self.diagram_sub = QMdiSubWindow()
        self.diagram_sub.setWidget(self.leakTest_widget)
        self.centralWidget.addSubWindow(self.leakTest_sub)
        self.leakTest_sub.show()

    def openLicense(self):
        self.license_sub = QMdiSubWindow()
        self.license_sub.setWidget(self.license_widget)
        self.centralWidget.addSubWindow(self.license_sub)
        self.license_sub.show()

    def openDWT(self):
        self.dwt_sub = QMdiSubWindow()
        self.dwt_sub.setWidget(self.dwt_widget)
        self.centralWidget.addSubWindow(self.dwt_sub)
        self.dwt_sub.show()

    @pyqtSlot()
    def on_click(self):
        print('PyQt5 button click')

    def closeEvent(self, event):
        buttonReply = QMessageBox.question(self, 'Calibri', "Do you like to quit Calibri?",
                                           QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if buttonReply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
        self.show()

    def createTrayIcon(self):
        self.trayIconMenu = QMenu(self)
        self.trayIconMenu.addAction(self.minimizeAction)
        self.trayIconMenu.addAction(self.maximizeAction)
        self.trayIconMenu.addAction(self.restoreAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)

        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setContextMenu(self.trayIconMenu)

        icon = QIcon("icon.png")
        self.trayIcon.setIcon(icon)
        self.setWindowIcon(icon)

    def createActions(self):
        self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide)
        self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized)
        self.restoreAction = QAction("&Restore", self, triggered=self.showNormal)
        self.quitAction = QAction("&Quit", self, triggered=QApplication.instance().quit)

    def createTrayIcon2(self):
        # Create the icon
        icon = QIcon("icon.png")

        self.clipboard = QApplication.clipboard()
        self.dialog = QColorDialog()

        # Create the menu
        menu = QMenu(self)

        action1 = QAction("Hex")
        #action1.triggered.connect(copy_color_hex)
        menu.addAction(action1)

        action2 = QAction("RGB")
        #action2.triggered.connect(copy_color_rgb)
        menu.addAction(action2)

        menu.addSeparator()

        action3 = QAction("HSV")
        #action3.triggered.connect(copy_color_hsv)
        menu.addAction(action3)

        """
        menu.addAction(self.minimizeAction)
        menu.addAction(self.maximizeAction)
        menu.addAction(self.restoreAction)
        menu.addSeparator()
        menu.addAction(self.quitAction)
        """

        # Create the tray
        self.trayIcon = QSystemTrayIcon(self)
        self.trayIcon.setContextMenu(menu)

        self.trayIcon.setIcon(icon)
        self.setWindowIcon(icon)

        # Add the menu to the tray

    def copy_color_hex():
        if dialog.exec_():
            color = dialog.currentColor()
            clipboard.setText(color.name())


    def copy_color_rgb():
        if dialog.exec_():
            color = dialog.currentColor()
            clipboard.setText("rgb(%d, %d, %d)" % (
                color.red(), color.green(), color.blue()
            ))


    def copy_color_hsv():
        if dialog.exec_():
            color = dialog.currentColor()
            clipboard.setText("hsv(%d, %d, %d)" % (
                color.hue(), color.saturation(), color.value()
            ))
Пример #5
0
class MainWindow(QMainWindow):
    count = 0

    def __init__(self, parent=None):
        super().__init__(parent)
        self.mdi = QMdiArea()
        self.setCentralWidget(self.mdi)

        self.app_manager = AppManager(None, gui_parent=self)
        self.mdi.subWindowActivated.connect(self.app_manager.on_view_activated)

        self.left = 100
        self.top = 50
        self.width = 1800
        self.height = 1200
        self.setGeometry(self.left, self.top, self.width, self.height)

        bar = self.menuBar()

        file = bar.addMenu("File")
        file.addAction("New")
        file.addAction("Open...")
        file.addSeparator()
        file.addAction("Save")
        file.addAction("Save As...")
        file.addAction("Close")
        file.triggered[QAction].connect(self.file_action)

        view = bar.addMenu("View")
        view.addAction("Spec Sheet")
        view.addAction("Optical Layout")
        view.addAction("Lens Table")
        view.addAction("Element Table")
        view.addAction("Lens View")
        view.addSeparator()
        view.addAction("Paraxial Model")
        view.addAction("Paraxial Height View")
        view.addAction("Paraxial Height View V2")
        view.addAction("Paraxial Slope View")
        view.addAction("Paraxial Ray Table")
        view.addAction("Ray Table")
        view.addSeparator()
        view.addAction("Ray Fans")
        view.addAction("OPD Fans")
        view.addAction("Spot Diagram")
        view.addAction("Wavefront Map")
        view.addAction("Astigmatism Curves")
        view.addAction("3rd Order Aberrations")
        view.addSeparator()
        view.triggered[QAction].connect(self.view_action)

        wnd = bar.addMenu("Window")
        wnd.addAction("Cascade")
        wnd.addAction("Tiled")
        wnd.addSeparator()

        dock.create_dock_windows(self)
        for pi in dock.panels.values():
            wnd.addAction(pi.menu_action)

        wnd.triggered[QAction].connect(self.window_action)

        self.setWindowTitle("Ray Optics")
        self.show()

        self.new_model()
        self.add_ipython_subwindow()

#        pth = Path(__file__).resolve()
#        try:
#            root_pos = pth.parts.index('rayoptics')
#        except ValueError:
#            logging.debug("Can't find rayoptics: path is %s", pth)
#        else:
#            path = Path(*pth.parts[:root_pos+1])
#            self.open_file(path / "codev/tests/asp46.seq")
#            self.open_file(path / "codev/tests/dar_test.seq")
#            self.open_file(path / "codev/tests/paraboloid.seq")
#            self.open_file(path / "codev/tests/paraboloid_f8.seq")
#            self.open_file(path / "codev/tests/schmidt.seq")
#            self.open_file(path / "codev/tests/questar35.seq")
#            self.open_file(path / "codev/tests/rc_f16.seq")
#            self.open_file(path / "codev/tests/ag_dblgauss.seq")
#            self.open_file(path / "codev/tests/threemir.seq")
#            self.open_file(path / "codev/tests/folded_lenses.seq")
#            self.open_file(path / "codev/tests/unfolded_lenses_w_ape.seq")
#            self.open_file(path / "codev/tests/lens_reflection_test.seq")
#            self.open_file(path / "codev/tests/dec_tilt_test.seq")
#            self.open_file(path / "codev/tests/landscape_lens.seq")
#            self.open_file(path / "codev/tests/mangin.seq")
#            self.open_file(path / "optical/tests/cell_phone_camera.roa")
#            self.open_file(path / "optical/tests/singlet_f3.roa")

#        try:
#            root_pos = pth.parts.index('ray-optics')
#        except ValueError:
#            logging.debug("Can't find ray-optics: path is %s", pth)
#        else:
#            path = Path(*pth.parts[:root_pos+1])
#            self.open_file(path / "models/TwoMirror.roa")
#            self.open_file(path / "models/TwoSphericalMirror.roa")
#            self.open_file(path / "models/Sasian Triplet.roa")
#            self.open_file(path / "models/singlet_f5.roa")
#            self.open_file(path / "models/thinlens.roa")
#            self.open_file(path / "models/thin_triplet.roa")
#            self.open_file(path / "models/Cassegrain.roa")
#            self.open_file(path / "models/Ritchey_Chretien.roa")
#        finally:
#            self.add_ipython_subwindow()

    def add_subwindow(self, widget, model_info):
        sub_wind = self.mdi.addSubWindow(widget)
        self.app_manager.add_view(sub_wind, widget, model_info)
        MainWindow.count += 1
        return sub_wind

    def delete_subwindow(self, sub_wind):
        self.app_manager.delete_view(sub_wind)
        self.mdi.removeSubWindow(sub_wind)
        MainWindow.count -= 1

    def add_ipython_subwindow(self):
        try:
            create_ipython_console(self, 'iPython console', 600, 400)
        except MultipleInstanceError:
            logging.debug("Unable to open iPython console. "
                          "MultipleInstanceError")
        except Exception as inst:
            print(type(inst))  # the exception instance
            print(inst.args)  # arguments stored in .args
            print(inst)  # __str__ allows args to be printed directly,
            pass  # but may be overridden in exception subclasses

    def initial_window_offset(self):
        offset_x = 50
        offset_y = 25
        orig_x = (MainWindow.count - 1) * offset_x
        orig_y = (MainWindow.count - 1) * offset_y
        return orig_x, orig_y

    def file_action(self, q):
        if q.text() == "New":
            self.new_model()

        if q.text() == "Open...":
            options = QFileDialog.Options()
            # options |= QFileDialog.DontUseNativeDialog
            fileName, _ = QFileDialog.getOpenFileName(
                self,
                "QFileDialog.getOpenFileName()",
                "",
                "CODE V Files (*.seq);;Ray-Optics Files (*.roa)",
                options=options)
            if fileName:
                logging.debug("open file: %s", fileName)
                self.open_file(fileName)

        if q.text() == "Save As...":
            options = QFileDialog.Options()
            # options |= QFileDialog.DontUseNativeDialog
            fileName, _ = QFileDialog.getSaveFileName(
                self,
                "QFileDialog.getSaveFileName()",
                "",
                "Ray-Optics Files (*.roa);;All Files (*)",
                options=options)
            if fileName:
                logging.debug("save file: %s", fileName)
                self.save_file(fileName)

        if q.text() == "Close":
            self.close_model()

    def new_model(self):
        iid = cmds.create_new_ideal_imager(gui_parent=self,
                                           conjugate_type='infinite')

        self.refresh_app_ui()

    def open_file(self, file_name):
        self.cur_filename = file_name
        self.app_manager.set_model(open_model(file_name))
        self.is_changed = True
        self.create_lens_table()
        cmds.create_live_layout_view(self.app_manager.model, gui_parent=self)
        #        cmds.create_lens_layout_view(self.app_manager.model, gui_parent=self)
        #        self.create_2D_lens_view()
        self.refresh_app_ui()

    def save_file(self, file_name):
        self.app_manager.model.save_model(file_name)
        self.cur_filename = file_name
        self.is_changed = False

    def close_model(self):
        """ NOTE: this does not check to save a modified model """
        self.app_manager.close_model(self.delete_subwindow)

    def view_action(self, q):
        opt_model = self.app_manager.model

        if q.text() == "Spec Sheet":
            cmds.create_new_ideal_imager(opt_model=opt_model, gui_parent=self)

        if q.text() == "Optical Layout":
            cmds.create_live_layout_view(opt_model, gui_parent=self)

        if q.text() == "Lens View":
            cmds.create_lens_layout_view(opt_model, gui_parent=self)


#            self.create_2D_lens_view()

        if q.text() == "Lens Table":
            self.create_lens_table()

        if q.text() == "Element Table":
            model = cmds.create_element_table_model(opt_model)
            self.create_table_view(model, "Element Table")

        if q.text() == "Ray Fans":
            cmds.create_ray_fan_view(opt_model, "Ray", gui_parent=self)

        if q.text() == "OPD Fans":
            cmds.create_ray_fan_view(opt_model, "OPD", gui_parent=self)

        if q.text() == "Spot Diagram":
            cmds.create_ray_grid_view(opt_model, gui_parent=self)

        if q.text() == "Wavefront Map":
            cmds.create_wavefront_view(opt_model, gui_parent=self)

        if q.text() == "Astigmatism Curves":
            cmds.create_field_curves(opt_model, gui_parent=self)

        if q.text() == "3rd Order Aberrations":
            cmds.create_3rd_order_bar_chart(opt_model, gui_parent=self)

        if q.text() == "Paraxial Height View":
            cmds.create_paraxial_design_view(opt_model, 'ht', gui_parent=self)

        if q.text() == "Paraxial Height View V2":
            cmds.create_paraxial_design_view_v2(opt_model,
                                                'ht',
                                                gui_parent=self)

        if q.text() == "Paraxial Slope View":
            cmds.create_paraxial_design_view(opt_model, 'slp', gui_parent=self)

        if q.text() == "Paraxial Ray Table":
            model = cmds.create_parax_table_model(opt_model)
            self.create_table_view(model, "Paraxial Ray Table")

        if q.text() == "Paraxial Model":
            model = cmds.create_parax_model_table(opt_model)
            self.create_table_view(model, "Paraxial Model")

        if q.text() == "Ray Table":
            self.create_ray_table(opt_model)

    def window_action(self, q):
        if q.text() == "Cascade":
            self.mdi.cascadeSubWindows()

        if q.text() == "Tiled":
            self.mdi.tileSubWindows()

    def create_lens_table(self):
        seq_model = self.app_manager.model.seq_model
        model = cmds.create_lens_table_model(seq_model)
        self.create_table_view(model, "Surface Data Table")

    def create_ray_table(self, opt_model):
        osp = opt_model.optical_spec
        pupil = [0., 1.]
        fi = 0
        wl = osp.spectral_region.reference_wvl
        fld, wvl, foc = osp.lookup_fld_wvl_focus(fi, wl)
        ray, ray_op, wvl = trace.trace_base(opt_model, pupil, fld, wvl)
        #        ray, ray_op, wvl, opd = trace.trace_with_opd(opt_model, pupil,
        #                                                     fld, wvl, foc)

        #        cr = trace.RayPkg(ray, ray_op, wvl)
        #        s, t = trace.trace_coddington_fan(opt_model, cr, foc)

        ray = [RaySeg(*rs) for rs in ray]
        model = cmds.create_ray_table_model(opt_model, ray)
        self.create_table_view(model, "Ray Table")

    def create_2D_lens_view(self):
        scene2d = QGraphicsScene()
        self.create_element_model(scene2d)
        self.create_ray_model(scene2d)
        scene2d.setBackgroundBrush(QColor(237, 243, 254))  # light blue
        sceneRect2d = scene2d.sceneRect()

        # construct the top level widget
        widget = QWidget()
        # construct the top level layout
        layout = QVBoxLayout(widget)

        # set the layout on the widget
        widget.setLayout(layout)

        sub = self.add_subwindow(
            widget,
            ModelInfo(self.app_manager.model, MainWindow.update_2D_lens_view,
                      (scene2d, )))
        sub.setWindowTitle("2D Lens View")
        view_width = 660
        view_ht = 440
        view_ratio = view_width / view_ht
        orig_x, orig_y = self.initial_window_offset()
        sub.setGeometry(orig_x, orig_y, view_width, view_ht)

        self.gview2d = QGraphicsView(scene2d)
        scene_ratio = sceneRect2d.width() / sceneRect2d.height()
        oversize_fraction = 1.2
        if scene_ratio > view_ratio:
            view_scale = view_width / (oversize_fraction * sceneRect2d.width())
        else:
            view_scale = view_ht / (oversize_fraction * sceneRect2d.height())

        self.gview2d.scale(view_scale, view_scale)
        layout.addWidget(self.gview2d)

        sub.show()

        return sub

    def update_2D_lens_view(scene2d):
        for gi in scene2d.items():
            gi.prepareGeometryChange()
            gi.update_shape()

    def create_element_model(self, gscene):
        ele_model = self.app_manager.model.ele_model
        ele_model.elements_from_sequence(self.app_manager.model.seq_model)
        for e in ele_model.elements:
            ge = OpticalElement(e)
            gscene.addItem(ge)

    def create_ray_model(self, gscene, start_surf=1):
        opt_model = self.app_manager.model

        img_dist = abs(opt_model.optical_spec.parax_data[2].img_dist)
        start_offset = 0.05 * (gscene.sceneRect().width() + img_dist)

        fov = opt_model.optical_spec.field_of_view
        for fi, f in enumerate(fov.fields):
            rb = RayBundle(opt_model, fi, start_offset)
            gscene.addItem(rb)

    def create_table_view(self, table_model, table_title, close_callback=None):
        # construct the top level widget
        widget = QWidget()
        # construct the top level layout
        layout = QVBoxLayout(widget)

        tableView = QTableView()
        tableView.setAlternatingRowColors(True)

        # Add table to box layout
        layout.addWidget(tableView)

        # set the layout on the widget
        widget.setLayout(layout)

        sub = self.add_subwindow(
            widget,
            ModelInfo(self.app_manager.model, cmds.update_table_view,
                      (tableView, )))
        sub.setWindowTitle(table_title)

        sub.installEventFilter(self)

        tableView.setModel(table_model)

        tableView.setMinimumWidth(tableView.horizontalHeader().length() +
                                  tableView.horizontalHeader().height())
        #                                  The following line should work but returns 0
        #                                  tableView.verticalHeader().width())

        view_width = tableView.width()
        view_ht = tableView.height()
        orig_x, orig_y = self.initial_window_offset()
        sub.setGeometry(orig_x, orig_y, view_width, view_ht)

        # table data updated successfully
        table_model.update.connect(self.on_data_changed)

        sub.show()

        return sub

    def eventFilter(self, obj, event):
        if (event.type() == QEvent.Close):
            print('close event received:', obj)
        return False

    def refresh_gui(self):
        self.app_manager.refresh_gui()

    def refresh_app_ui(self):
        dock.update_dock_windows(self)

    def handle_ideal_imager_command(self, iid, command, specsheet):
        if command == 'Apply':
            opt_model = self.app_manager.model
            opt_model.set_from_specsheet(specsheet)
            self.refresh_gui()
        elif command == 'Close':
            for view, info in self.app_manager.view_dict.items():
                if iid == info[0]:
                    view.close()
        elif command == 'Update':
            opt_model = self.app_manager.model
            specsheet = opt_model.specsheet
            firstorder.specsheet_from_parax_data(opt_model, specsheet)
            iid.specsheet_dict[specsheet.conjugate_type] = specsheet
            iid.update_values()
        elif command == 'New':
            opt_model = cmds.create_new_optical_model_from_specsheet(specsheet)
            self.app_manager.set_model(opt_model)
            for view, info in self.app_manager.view_dict.items():
                if iid == info[0]:
                    w = iid
                    mi = info[1]
                    args = (iid, opt_model)
                    new_mi = ModelInfo(model=opt_model,
                                       fct=mi.fct,
                                       args=args,
                                       kwargs=mi.kwargs)
                    self.app_manager.view_dict[view] = w, new_mi
            self.refresh_gui()
            self.create_lens_table()
            cmds.create_live_layout_view(opt_model, gui_parent=self)
            cmds.create_paraxial_design_view(opt_model, 'ht', gui_parent=self)
            self.refresh_gui()

    @pyqtSlot(object, int)
    def on_data_changed(self, rootObj, index):
        self.refresh_gui()
Пример #6
0
class GUIWindow(QMainWindow):
    def __init__(self, app, pipeline=Pipeline()):
        super().__init__()
        self._app = app
        self._logger = logging.getLogger(self.__class__.__name__)
        self._is_initialized = False
        self.init_basic(pipeline)
        self.init_ui()
        self.init_controls()
        self.setWindowTitle("Cognigraph")
        self.setWindowIcon(QIcon(':/cognigraph_icon.png'))

    def init_basic(self, pipeline):
        self._pipeline = pipeline  # type: Pipeline
        self._updater = AsyncUpdater(self._app, pipeline)
        self._pipeline._signal_sender.long_operation_started.connect(
            self._show_progress_dialog)
        self._pipeline._signal_sender.long_operation_finished.connect(
            self._hide_progress_dialog)
        self._pipeline._signal_sender.request_message.connect(
            self._show_message)
        self._pipeline._signal_sender.node_widget_added.connect(
            self._on_node_widget_added)
        self._controls = Controls(pipeline=self._pipeline)
        self._controls.setSizePolicy(QSizePolicy.Preferred,
                                     QSizePolicy.Expanding)

        self._controls.tree_widget.node_removed.connect(self._on_node_removed)
        if hasattr(self, "central_widget"):
            for w in self.central_widget.subWindowList():
                self.central_widget.removeSubWindow(w)

    def init_controls(self):
        self.controls_dock.setWidget(self._controls)
        self.run_toggle_action.triggered.disconnect()
        self.run_toggle_action.triggered.connect(self._updater.toggle)
        self._updater._sender.run_toggled.connect(self._on_run_button_toggled)
        self._updater._sender.errored.connect(self._show_message)
        self.is_initialized = False

    def init_ui(self):
        self.central_widget = QMdiArea()
        self.setCentralWidget(self.central_widget)

        # -------- controls widget -------- #
        self.controls_dock = QDockWidget("Processing pipeline setup", self)
        self.controls_dock.setObjectName("Controls")
        self.controls_dock.setAllowedAreas(Qt.LeftDockWidgetArea
                                           | Qt.RightDockWidgetArea)
        self.controls_dock.visibilityChanged.connect(
            self._update_pipeline_tree_widget_action_text)

        self.addDockWidget(Qt.LeftDockWidgetArea, self.controls_dock)

        # self._controls.setMinimumWidth(800)
        # --------------------------------- #

        file_menu = self.menuBar().addMenu("&File")  # file menu
        load_pipeline_action = self._createAction("&Load pipeline",
                                                  self._load_pipeline)
        save_pipeline_action = self._createAction("&Save pipeline",
                                                  self._save_pipeline)
        file_menu.addAction(load_pipeline_action)
        file_menu.addAction(save_pipeline_action)

        # -------- view menu & toolbar -------- #
        tile_windows_action = self._createAction(
            "&Tile windows", self.central_widget.tileSubWindows)
        view_menu = self.menuBar().addMenu("&View")
        view_menu.addAction(tile_windows_action)
        view_toolbar = self.addToolBar("View")
        view_toolbar.addAction(tile_windows_action)
        # ------------------------------------- #

        edit_menu = self.menuBar().addMenu("&Edit")
        self._toggle_pipeline_tree_widget_action = self._createAction(
            "&Hide pipeline settings", self._toggle_pipeline_tree_widget)
        edit_menu.addAction(self._toggle_pipeline_tree_widget_action)
        edit_toolbar = self.addToolBar("Edit")
        edit_toolbar.setObjectName("edit_toolbar")
        edit_toolbar.addAction(self._toggle_pipeline_tree_widget_action)

        # -------- run menu & toolbar -------- #
        self.run_toggle_action = self._createAction(
            "&Start", self._on_run_button_toggled)
        run_menu = self.menuBar().addMenu("&Run")
        self.initialize_pipeline = self._createAction("&Initialize pipeline",
                                                      self.initialize)
        run_menu.addAction(self.run_toggle_action)
        run_menu.addAction(self.initialize_pipeline)
        run_toolbar = self.addToolBar("Run")
        run_toolbar.setObjectName("run_toolbar")
        run_toolbar.addAction(self.run_toggle_action)
        run_toolbar.addAction(self.initialize_pipeline)
        # ------------------------------------ #

    def _toggle_pipeline_tree_widget(self):
        if self.controls_dock.isHidden():
            self.controls_dock.show()
        else:
            self.controls_dock.hide()

    def _update_pipeline_tree_widget_action_text(self, is_visible):
        if is_visible:
            self._toggle_pipeline_tree_widget_action.setText(
                "&Hide pipelne settings")
        else:
            self._toggle_pipeline_tree_widget_action.setText(
                "&Show pipelne settings")

    def _load_pipeline(self):
        file_dialog = QFileDialog(caption="Select pipeline file",
                                  directory=PIPELINES_DIR)
        ext_filter = "JSON file (*.json);; All files (*.*)"
        pipeline_path = file_dialog.getOpenFileName(filter=ext_filter)[0]
        if pipeline_path:
            self._logger.info("Loading pipeline configuration from %s" %
                              pipeline_path)
            if not self._updater.is_paused:
                self.run_toggle_action.trigger()
            with open(pipeline_path, "r") as db:
                try:
                    params_dict = json.load(db)
                except json.decoder.JSONDecodeError as e:
                    self._show_message("Bad pipeline configuration file",
                                       detailed_text=str(e))

                pipeline = self.assemble_pipeline(params_dict, "Pipeline")
                self.init_basic(pipeline)
                self.init_controls()
                # self.resize(self.sizeHint())
        else:
            return

    def _save_pipeline(self):
        self._logger.info("Saving pipeline")
        file_dialog = QFileDialog(caption="Select pipeline file",
                                  directory=PIPELINES_DIR)
        ext_filter = "JSON file (*.json);; All files (*.*)"
        pipeline_path = file_dialog.getSaveFileName(filter=ext_filter)[0]
        if pipeline_path:
            self._logger.info("Saving pipeline configuration to %s" %
                              pipeline_path)
            try:
                self._pipeline.save_pipeline(pipeline_path)
            except Exception as exc:
                self._show_message(
                    "Cant`t save pipeline configuration to %s" % pipeline_path,
                    detailed_text=str(exc),
                )
                self._logger.exception(exc)

    def assemble_pipeline(self, d, class_name):
        node_class = getattr(nodes, class_name)
        node = node_class(**d["init_args"])
        for child_class_name in d["children"]:
            child = self.assemble_pipeline(d["children"][child_class_name],
                                           child_class_name)
            node.add_child(child)
        return node

    def initialize(self):
        is_paused = self._updater.is_paused
        if not is_paused:
            self._updater.stop()
        self._logger.info("Initializing all nodes")
        async_initer = AsyncPipelineInitializer(pipeline=self._pipeline,
                                                parent=self)
        async_initer.no_blocking_execution()
        for node in self._pipeline.all_nodes:
            if hasattr(node, "widget"):
                if not node.widget.parent():  # widget not added to QMdiArea
                    self._add_subwindow(node.widget, repr(node))
        self.central_widget.tileSubWindows()
        self.run_toggle_action.setDisabled(False)
        if not is_paused:
            self._updater.start()

    def _finish_initialization(self):
        self.progress_dialog.hide()
        self.progress_dialog.deleteLater()
        for node in self._pipeline.all_nodes:
            if hasattr(node, "widget"):
                self._add_subwindow(node.widget, repr(node))
        self.central_widget.tileSubWindows()

    def _add_subwindow(self, widget, title):
        sw = _HookedSubWindow(self.central_widget)
        sw.setWidget(widget)
        sw.setWindowTitle(title)
        widget.show()

    def _show_progress_dialog(self, text):
        # -------- setup progress dialog -------- #
        self.progress_dialog = QProgressDialog(self)
        self.progress_dialog.setLabelText(text)
        self.progress_dialog.setCancelButtonText(None)
        self.progress_dialog.setRange(0, 0)
        self.progress_dialog.show()

    def _hide_progress_dialog(self):
        self.progress_dialog.hide()
        self.progress_dialog.deleteLater()

    def _on_subwindow_close(self, close_event):
        pass

    def _on_node_widget_added(self, widget, widget_name):
        self._add_subwindow(widget, widget_name)
        self.central_widget.tileSubWindows()

    def _on_node_removed(self, tree_item):
        if hasattr(tree_item.node, "widget"):
            try:
                self.central_widget.removeSubWindow(
                    tree_item.node.widget.parent())
            except AttributeError:
                pass
            except Exception as exc:
                self._show_message(
                    "Can`t remove widget for %s" % tree_item.node,
                    detailed_text=str(exc),
                )
                self._logger.exception(exc)

    def _show_message(self, text, detailed_text=None, level="error"):
        if level == "error":
            icon = QMessageBox.Critical
        elif level == "warning":
            icon = QMessageBox.Warning
        elif level == "info":
            icon = QMessageBox.Information
        msg = QMessageBox(self)
        msg.setIcon(icon)
        msg.setText(text)
        msg.setDetailedText(detailed_text)
        msg.show()

    def _createAction(
        self,
        text,
        slot=None,
        shortcut=None,
        icon=None,
        tip=None,
        checkable=False,
    ):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            action.triggered.connect(slot)
        if checkable:
            action.setCheckable(True)
        return action

    def moveEvent(self, event):
        return super(GUIWindow, self).moveEvent(event)

    def _on_run_button_toggled(self, is_paused=True):
        if is_paused:
            self.run_toggle_action.setText("Start")
        else:
            self.run_toggle_action.setText("Pause")

    @property
    def is_initialized(self):
        return self._is_initialized

    @is_initialized.setter
    def is_initialized(self, value):
        if value:
            self.run_toggle_action.setDisabled(False)
        else:
            self.run_toggle_action.setDisabled(True)
        self._is_initialized = value

    @property
    def _node_widgets(self) -> List[QWidget]:
        node_widgets = list()
        for node in self._pipeline.all_nodes:
            try:
                node_widgets.append(node.widget)
            except AttributeError:
                pass
        return node_widgets