Ejemplo n.º 1
0
class OpenposeGUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("OpencvGUI")
        self.setWindowIcon(QIcon("icon/logo.png"))

        # QAction
        self.action_autosave = QAction(QIcon("icon/autosave.png"), "自动保存",
                                       self)
        self.action_save = QAction(QIcon("icon/save.png"), "保存", self)
        self.action_image = QAction(QIcon("icon/image.png"), "打开图片", self)
        self.action_video = QAction(QIcon("icon/video.png"), "打开视频", self)
        self.action_camera = QAction(QIcon("icon/camera.png"), "打开摄像头", self)
        self.action_folder = QAction(QIcon("icon/folder.png"), "打开文件夹", self)
        self.action_setting = QAction(QIcon("icon/setting.png"), "设置", self)
        self.action_filetree = QAction(QIcon("icon/filetree.png"), "目录树", self)
        self.action_camera.setCheckable(True)
        self.action_video.setCheckable(True)
        self.action_setting.setCheckable(True)
        self.action_filetree.setCheckable(True)
        self.action_autosave.setCheckable(True)

        # 菜单栏
        self.menu_bar = QMenuBar()
        self.menu_open = self.menu_bar.addMenu("Open")
        self.menu_open.addAction(self.action_image)
        self.menu_open.addAction(self.action_video)
        self.menu_open.addAction(self.action_camera)

        self.menu_view = self.menu_bar.addMenu("View")
        self.menu_view.addAction(self.action_setting)
        self.menu_view.addAction(self.action_filetree)

        self.menu_function = self.menu_bar.addMenu("Function")
        self.action_background = self.menu_function.addAction(
            "Black/RGB Blackground")
        self.action_geture = self.menu_function.addAction(
            "Gesture Recognition")

        self.setMenuBar(self.menu_bar)

        # 工具栏
        self.tool_bar = QToolBar()
        self.tool_bar.addAction(self.action_save)
        self.tool_bar.addAction(self.action_autosave)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.action_folder)
        self.tool_bar.addAction(self.action_camera)
        self.tool_bar.addAction(self.action_image)
        self.tool_bar.addAction(self.action_video)
        self.tool_bar.addSeparator()
        self.tool_bar.addAction(self.action_setting)
        self.tool_bar.addAction(self.action_filetree)
        self.addToolBar(self.tool_bar)

        # 状态栏
        self.status_bar = QStatusBar()
        self.status_fps = QLabel("FPS:00.0")
        self.status_bar.addPermanentWidget(self.status_fps)
        self.setStatusBar(self.status_bar)

        # 组件
        self.timer = QTimer()
        self.camera = Camera(self)
        self.setting_widget = SettingWidget(self)
        self.label_frame = LabelFrame(self)
        self.file_system_tree = FileSystemTreeView(self)
        self.openpose_model = OpenposeModel(self)
        self.media_control = MediaControl(self)
        self.save_widget = SaveWidget()
        self.gesture_model = GestureModel()

        # 设置dock
        self.dock_filetree = QDockWidget(self)
        self.dock_filetree.setWidget(self.file_system_tree)
        self.dock_file_label = QLabel("Current Directory")
        self.dock_file_label.setMinimumHeight(25)
        self.dock_file_label.setAlignment(Qt.AlignCenter | Qt.AlignHCenter)
        self.dock_filetree.setTitleBarWidget(self.dock_file_label)
        self.dock_filetree.setFeatures(QDockWidget.AllDockWidgetFeatures)
        self.dock_filetree.hide()

        self.dock_setting = QDockWidget(self)
        self.dock_setting.setWidget(self.setting_widget)
        self.dock_setting_label = QLabel("Openpose Settings")
        self.dock_setting_label.setMinimumHeight(25)
        self.dock_setting_label.setAlignment(Qt.AlignCenter | Qt.AlignHCenter)
        self.dock_setting.setTitleBarWidget(self.dock_setting_label)
        self.dock_setting.setFeatures(QDockWidget.AllDockWidgetFeatures)
        self.dock_setting.hide()

        self.dock_media = QDockWidget(self)
        self.dock_media.setWidget(self.media_control)
        self.dock_media.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.dock_media.hide()

        self.setCentralWidget(self.label_frame)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_setting)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_filetree)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.dock_media)

        # 信号与槽

        self.setting_widget.horizontalSlider_Body.sliderReleased.connect(
            self.change_body_threshold)
        self.setting_widget.horizontalSlider_Face.sliderReleased.connect(
            self.change_face_threshold)
        self.setting_widget.horizontalSlider_Hand.sliderReleased.connect(
            self.change_hand_threshold)
        self.setting_widget.checkBox_body.stateChanged.connect(
            self.check_body)  # 状态改变触发check_box_changed函数
        self.setting_widget.checkBox_hand.stateChanged.connect(
            self.check_hand)  # 状态改变触发check_box_changed函数
        self.setting_widget.checkBox_face.stateChanged.connect(
            self.check_face)  # 状态改变触发check_box_changed函数
        self.setting_widget.radioButton_black.toggled.connect(
            self.change_background)

        self.setting_widget.comboBox_resolution.currentIndexChanged.connect(
            self.change_resolution)

        self.media_control.play_button.toggled.connect(self.play_media)

        self.action_image.triggered.connect(self.open_image)
        self.action_video.triggered.connect(self.open_video)
        self.action_folder.triggered.connect(self.open_folder)
        self.action_camera.triggered.connect(self.open_camera)
        self.action_filetree.triggered.connect(self.show_filetree)
        self.action_setting.triggered.connect(self.show_setting)
        self.action_autosave.triggered.connect(self.auto_save)

        self.action_save.triggered.connect(self.save_current)
        self.action_setting.triggered.connect(self.setting_widget.show)

        self.file_system_tree.doubleClicked.connect(self.open_image)
        self.camera.timer.timeout.connect(self.update)
        self.timer.timeout.connect(self.save_current)

    def update(self):
        start_time = time.time()
        frame = self.camera.frame()
        self.media_control.update(self.camera.frame_pos,
                                  self.camera.frame_count)
        if frame is None:
            return None
        result, keypoints = self.openpose_model(frame)
        result, keypoints = self.gesture_recognition(result, keypoints)

        message = self.generate_message(keypoints)
        self.label_frame.update_frame(result)
        fps = 1 / (time.time() - start_time)
        self.status_fps.setText("FPS:{:.1f}".format(fps))
        self.status_bar.showMessage(message, 2000)
        return result, keypoints

    def gesture_recognition(self, result, keypoints):
        """实现手势识别"""
        if self.setting_widget.gesture_on():
            hands = keypoints[1]
            person_num = hands[0].shape[0]
            for i in range(person_num):
                for hand in hands:
                    rect = self.gesture_model.hand_bbox(hand[i])
                    gesture = self.gesture_model(hand[i])
                    if rect:
                        print(rect)
                        x, y, w, h = rect
                        cv2.rectangle(result, (x, y), (x + w, y + h),
                                      (255, 255, 255))
                        cv2.putText(result, gesture, (x, y),
                                    cv2.FONT_HERSHEY_SIMPLEX, 1,
                                    (255, 255, 255))
        return result, keypoints

    def generate_message(self, keypoints):
        """获取识别结果信息"""
        if keypoints[0].size != 1:
            person_num = keypoints[0].shape[0]
            message = "person: {}".format(person_num)
            for i in range(person_num):
                message += " | person{}(".format(i + 1)
                if self.setting_widget.body_on():
                    pose_keypoints = keypoints[0][i, :, 2]
                    pose_detected = pose_keypoints[
                        pose_keypoints > self.setting_widget.body_threshold()]
                    message += "pose: {:>2d}/{}".format(pose_detected.size, 25)

                if self.setting_widget.hand_on():
                    right_hand_keypoinys = keypoints[1][0][i, :, 2]
                    left_hand_keypoinys = keypoints[1][1][i, :, 2]

                    right_hand_detected = right_hand_keypoinys[
                        right_hand_keypoinys >
                        self.setting_widget.hand_threshold()]
                    left_hand_detected = left_hand_keypoinys[
                        left_hand_keypoinys >
                        self.setting_widget.hand_threshold()]
                    message += " | right hand: {:>2d}/{}".format(
                        right_hand_detected.size, 21)
                    message += " | left hand: {:>2d}/{}".format(
                        left_hand_detected.size, 21)
                message += ")"
        else:
            message = "person: {}".format(0)
        return message

    def save_current(self):
        if not self.label_frame.pixmap():
            QMessageBox.warning(self, "Note", "No data in frame",
                                QMessageBox.Yes)
            return

        pixmap = self.label_frame.cvimg2pixmap(
            self.openpose_model.get_rendered_image())
        body_keypoints, hand_keypoints, face_keypoints = self.openpose_model.get_keypoints(
        )
        body_keypoints = copy.deepcopy(
            body_keypoints) if self.setting_widget.body_on() else None
        hand_keypoints = copy.deepcopy(
            hand_keypoints) if self.setting_widget.hand_on() else None
        face_keypoints = copy.deepcopy(
            face_keypoints) if self.setting_widget.face_on() else None
        keypoints = (body_keypoints, hand_keypoints, face_keypoints)

        if self.timer.isActive():
            self.save_widget.save(pixmap.copy(), *keypoints)
        else:
            message = self.generate_message(keypoints)
            self.save_widget.setFrame(pixmap.copy(), *keypoints, message)
            self.save_widget.show()

    def auto_save(self):
        if not self.camera.is_open():
            self.action_autosave.setChecked(False)
        if self.action_autosave.isChecked():
            self.timer.start(self.setting_widget.save_interval() * 1000)
        else:
            self.timer.stop()

    def update_setting(self):
        pass

    def update_openpose(self, key, value):
        pass

    # 槽函数

    # 参数
    def check_body(self, status):
        # 姿态估计
        flag = True if status == Qt.Checked else False
        render_pose = 1 if status == Qt.Checked else 0
        self.setting_widget.horizontalSlider_Body.setEnabled(flag)
        self.setting_widget.radioButton_black.setEnabled(flag)
        self.setting_widget.radioButton_rgb.setEnabled(flag)
        self.openpose_model.update_wrapper("render_pose", render_pose)

    def check_hand(self, status):
        # 手部估计
        flag = True if status == Qt.Checked else False
        self.setting_widget.horizontalSlider_Hand.setEnabled(flag)
        self.setting_widget.checkBox_gesture.setEnabled(flag)
        self.openpose_model.update_wrapper("hand", flag)

    def check_face(self, status):
        # 脸部估计
        flag = True if status == Qt.Checked else False
        self.setting_widget.horizontalSlider_Face.setEnabled(flag)
        self.setting_widget.checkBox_emotion.setEnabled(flag)
        self.openpose_model.update_wrapper("face", flag)

    def change_body_threshold(self):
        # 姿态估计阈值
        value = self.setting_widget.body_threshold()
        self.setting_widget.label_threshold_body.setText(str(value * 100))
        self.openpose_model.update_wrapper("render_threshold", value)

    def change_hand_threshold(self):
        # 手部估计阈值
        value = self.setting_widget.hand_threshold()
        self.setting_widget.label_threshold_hand.setText(str(value * 100))
        self.openpose_model.update_wrapper("hand_render_threshold", value)

    def change_face_threshold(self):
        # 脸部估计阈值
        value = self.setting_widget.face_threshold()
        self.setting_widget.label_threshold_face.setText(str(value * 100))
        self.openpose_model.update_wrapper("face_render_threshold", value)

    def change_resolution(self):
        resolution = self.setting_widget.net_resolution()
        self.openpose_model.update_wrapper("net_resolution", resolution)

    def change_background(self):
        # 背景
        self.openpose_model.update_wrapper(
            "disable_blending", self.setting_widget.black_background())

    # 功能
    def show_setting(self):
        if self.dock_setting.isHidden():
            self.dock_setting.show()
        else:
            self.dock_setting.hide()

    def show_filetree(self):
        if self.dock_filetree.isHidden():
            self.dock_filetree.show()
        else:
            self.dock_filetree.hide()

    def open_image(self, file_index=None):
        if file_index:
            file = self.file_system_tree.fileSystemModel.filePath(file_index)
        else:
            file, _ = QFileDialog.getOpenFileName(
                self, '请选择图片', './',
                'Image files(*.jpg *.png *.JPG *.PNG)')  # 可设置默认路径
        if not file or not file.endswith((".jpg", ".png", ".JPG", ".PNG")):
            return False
        image = cv2.imread(file)
        result, keypoints = self.openpose_model(image)
        message = self.generate_message(keypoints)
        # self.label_frame.resize(*image.shape[:2])
        self.label_frame.update_frame(result)
        self.status_bar.showMessage(message)

    def open_video(self):
        if self.action_video.isChecked():
            file, _ = QFileDialog.getOpenFileName(
                self, '请选择视频', './', 'Video files(*.mp4 *.avi)')  # 可设置默认路径
            if not file:
                self.action_video.setChecked(False)
                return
            self.camera.start(file)
            self.label_frame.resize(*self.camera.resolution)
            self.action_video.setIcon(QIcon('icon/stop.png'))
            self.update()  # 初始化画面
            self.camera.is_pause = True
            self.media_control.pause()
            self.dock_media.show()
        else:
            self.label_frame.clear()
            self.camera.stop()
            self.status_fps.setText("FPS:00.0")
            self.action_video.setIcon(QIcon("icon/video.png"))
            self.media_control.play()
            self.dock_media.hide()

    def open_folder(self):
        new_foler = QFileDialog.getExistingDirectory(self, '请选择目录',
                                                     './')  # 可设置默认路径
        if not new_foler:
            return
        self.file_system_tree.alter_dir(new_foler)
        self.dock_filetree.show()
        self.status_bar.showMessage("current folder: {}".format(new_foler),
                                    3000)

    def open_camera(self):
        if self.action_camera.isChecked():
            self.camera.start()
            self.action_camera.setIcon(QIcon('icon/stop.png'))
        else:
            self.label_frame.clear()
            self.camera.stop()
            self.status_fps.setText("FPS:00.0")
            self.action_camera.setIcon(QIcon("icon/camera.png"))

    def play_media(self):
        if not self.media_control.is_play():
            self.media_control.pause()
            self.camera.is_pause = True
        else:
            self.media_control.play()
            self.camera.is_pause = False
Ejemplo n.º 2
0
class pyviViewerWindow(QtGui.QMainWindow):
    def __init__(self, flowchart):
        QtGui.QMainWindow.__init__(self)
        self.resize(1000, 800)
        self.setWindowTitle('Viewer')

        self.fc = flowchart
        self.fc.sigChartChanged.connect(self.nodeEvent)
        self.fc.sigChartLoaded.connect(self.loadChartEvent)
        self.fc.widget().resize(1000, 800)

        self.pyviwin = SimpleWindow()
        ## http://blog.qt.io/blog/2013/02/19/introducing-qwidgetcreatewindowcontainer/
        pyvi_widget = QWidget.createWindowContainer(self.pyviwin)
        pyvi_widget.setMinimumSize(200, 200)
        pyvi_widget.resize(800, 800)
        self.setCentralWidget(pyvi_widget)

        self.layerDockWidget = QDockWidget('Layers')
        self.layerDockWidget.setFeatures(QDockWidget.DockWidgetFloatable
                                         | QDockWidget.DockWidgetMovable)
        self.layerDockWidget.setWidget(self.pyviwin.layerWidget)
        self.pyviwin.layerWidget.resize(200, 500)
        self.addDockWidget(Qt.RightDockWidgetArea, self.layerDockWidget)

        self.plotDockWidget = QDockWidget('Plot')
        self.plotDockWidget.setFeatures(QDockWidget.DockWidgetFloatable
                                        | QDockWidget.DockWidgetMovable
                                        | QDockWidget.DockWidgetClosable)
        self.plotWidget = PlotWidget()
        self.plotDockWidget.setWidget(self.plotWidget)
        self.plotWidget.resize(100, 100)
        # self.plotDockWidget.show()
        # self.addDockWidget(Qt.RightDockWidgetArea, self.plotDockWidget)

        self.plotList = {'plot 1': self.plotWidget}

        self.plot3DDockWidget = QDockWidget('Plot3D')
        self.plot3DDockWidget.setFeatures(QDockWidget.DockWidgetFloatable
                                          | QDockWidget.DockWidgetMovable
                                          | QDockWidget.DockWidgetClosable)
        self.pyviwin_plot = SimpleWindow()
        pyviwin_plot_widget = QWidget.createWindowContainer(self.pyviwin_plot)
        pyviwin_plot_widget.setMinimumSize(100, 100)
        pyviwin_plot_widget.resize(200, 200)
        self.plot3DDockWidget.setWidget(pyviwin_plot_widget)
        self.plot3DDockWidget.show()
        # self.plot3D.resize(100,100)

        self.plotLayerDockWidget = QDockWidget('Plot Layers')
        self.plotLayerDockWidget.setFeatures(QDockWidget.DockWidgetFloatable
                                             | QDockWidget.DockWidgetMovable)
        self.plotLayerDockWidget.setWidget(self.pyviwin_plot.layerWidget)
        self.pyviwin_plot.layerWidget.resize(200, 500)
        self.addDockWidget(Qt.RightDockWidgetArea, self.plotLayerDockWidget)

        # self.dockWidgetDict = {}

        self.createActions()
        self.createMenus()

        self.show()
        self.fc.widget().show()

    def save(self):
        try:
            self.fc.saveFile()
        except:
            raise

    def open(self):
        self.fc.loadFile()

    def createActions(self):
        self.saveAct = QAction("&Save...",
                               self,
                               shortcut=QtGui.QKeySequence.Save,
                               statusTip="Save the current form letter",
                               triggered=self.save)

        self.openAct = QAction("&Open...",
                               self,
                               shortcut=QtGui.QKeySequence.Open,
                               statusTip="Save the current form letter",
                               triggered=self.open)

        self.reloadAct = QAction("&Reload nodelibs...",
                                 self,
                                 shortcut=QtGui.QKeySequence.Refresh,
                                 statusTip="Reload nodelibraries",
                                 triggered=self.fc.widget().reloadLibrary)

        self.quitAct = QAction("&Quit",
                               self,
                               shortcut=QtGui.QKeySequence.Quit,
                               statusTip="Quit the application",
                               triggered=self.close)

        self.toggleViewerAct = QAction("&Toggle Viewer",
                                       self,
                                       statusTip="Toggle viewer window",
                                       triggered=self.toggleViewer)

        self.toggleFlowchartAct = QAction("&Toggle Flowchart",
                                          self,
                                          statusTip="Toggle flowchart window",
                                          triggered=self.toggleFlowchart)

        self.togglePlotterAct = QAction("&Toggle Plotter",
                                        self,
                                        statusTip="Toggle plot window",
                                        triggered=self.togglePlotter)

    def toggleViewer(self):
        if self.isHidden():
            self.show()
        else:
            self.hide()

    def togglePlotter(self):
        if self.plotDockWidget.isHidden():
            self.plotDockWidget.show()
        else:
            self.plotDockWidget.hide()

    def toggleFlowchart(self):
        if self.fc.widget().isHidden():
            self.fc.widget().show()
        else:
            self.fc.widget().hide()

    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.saveAct)
        self.fileMenu.addAction(self.reloadAct)
        # self.fileMenu.addSeparator()
        # self.fileMenu.addAction(self.quitAct)

        # self.editMenu = self.menuBar().addMenu("&Edit")
        # self.editMenu.addAction(self.undoAct)

        self.viewMenu = self.menuBar().addMenu("&View")
        self.viewMenu.addAction(self.toggleViewerAct)
        self.viewMenu.addAction(self.toggleFlowchartAct)
        self.viewMenu.addAction(self.togglePlotterAct)

    def loadChartEvent(self):
        # self.clearDock()
        for node in self.fc.nodes().values():
            self.addNode(node)

    def nodeEvent(self, flowchart, action, node):
        if action == 'add':
            self.addNode(node)
        elif action == 'remove':
            self.removeNode(node)
        elif action == 'rename':
            pass
            # try:
            #     self.dockWidgetDict[node].setWindowTitle(node.name())
            # except KeyError:
            #     pass

    def addNode(self, node):
        # ctrlWidget = node.ctrlWidget()
        # if ctrlWidget:
        #     dock = QDockWidget(node.name(), self)
        #     dock.setWidget(node.ctrlWidget())
        #     dock.hide()
        #     # nodeDock.hideTitleBar()
        #     # self.da.addDock(nodeDock, 'right', )
        #     self.addDockWidget(Qt.RightDockWidgetArea, dock)
        #     self.viewMenu.addAction(dock.toggleViewAction())
        #     self.dockWidgetDict[node] = dock
        if type(node) is pvWindowNode:
            node.setPyViWindow(self.pyviwin, self.pyviwin_plot)
        elif type(node) is PlotWidgetNode:
            node.setPlotList(self.plotList)
            node.setPlot(self.plotWidget)

    def removeNode(self, node):
        print("removing...", node)
Ejemplo n.º 3
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