示例#1
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.grid_size = GRID_SIZE

        #adjusting main window
        self.resize(TILES_WINDOW_WIDTH, TILES_WINDOW_HEIGHT)
        self.move(0, 0)
        self.setWindowTitle("Trainer")

        #initializing tiles

        self.addTiles(TILE_WIDTH, TILE_HEIGHT)

        #initializing menubar
        self.menubar = QMenuBar(self)

        #adding menu options
        self.grid_menu = QMenu(self.menubar)
        self.grid_menu.setTitle("Grid")

        self.model_menu = QMenu(self.menubar)
        self.model_menu.setTitle("Model")

        self.file_menu = QMenu(self.menubar)
        self.file_menu.setTitle("File")

        #adding file submenu options
        exit = QAction(self)
        exit.setText("Exit")
        exit.setShortcut('Alt+F4')
        exit.triggered.connect(self.exit)
        self.file_menu.addAction(exit)

        #adding grid submenu options
        g3 = QAction(self)
        g3.setText("3 X 3")
        g3.triggered.connect(self.toGrid3)
        g3.setShortcut("3")
        self.grid_menu.addAction(g3)

        g5 = QAction(self)
        g5.setText("5 X 5")
        g5.triggered.connect(self.toGrid5)
        g5.setShortcut("5")
        self.grid_menu.addAction(g5)

        g7 = QAction(self)
        g7.setText("7 X 7")
        g7.triggered.connect(self.toGrid7)
        g7.setShortcut("7")
        self.grid_menu.addAction(g7)

        g9 = QAction(self)
        g9.setText("9 X 9")
        g9.setShortcut("9")
        g9.triggered.connect(self.toGrid9)
        self.grid_menu.addAction(g9)

        #add model submenus
        re_train = QAction(self)
        re_train.setText("Retrain")
        re_train.setShortcut('Ctrl+R')
        re_train.triggered.connect(self.retrain)
        self.model_menu.addAction(re_train)

        #registering menus to menubar
        self.menubar.addAction(self.file_menu.menuAction())
        self.menubar.addAction(self.model_menu.menuAction())
        self.menubar.addAction(self.grid_menu.menuAction())

        #registering menubar
        self.menubar.move(0, 0)
        self.menubar.setDefaultUp(False)
        self.setMenuBar(self.menubar)

        #initializing and registering statusbar
        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)

    def retrain(self):
        print("retrain")
        # to be implemented

    def exit(self):
        QCoreApplication.instance().quit()

    def toGrid3(self):
        self.changeGridSize(3)

    def toGrid5(self):
        self.changeGridSize(5)

    def toGrid7(self):
        self.changeGridSize(7)

    def toGrid9(self):
        self.changeGridSize(9)

    def changeGridSize(self, size):
        #change grid size to new size
        self.grid_size = size

        #redraw frames to get new look
        self.drawFrames()

    def addTiles(self, tile_width, tile_height):

        # initializing and registering centeral widget
        self.centeral_widget = QWidget(self)
        self.setCentralWidget(self.centeral_widget)

        #initialize tiles
        self.tiles = []

        #add tiles according to grid size
        for i in range(0, self.grid_size):
            self.tiles.append([])

            for j in range(0, self.grid_size):
                tile = Tile(self.centeral_widget)
                tile.move(TILES_MARGIN_LEFT * (j + 1) + tile_width * j,
                          TILES_MARGIN_TOP * (i + 1) + tile_height * i)
                tile.resize(tile_width, tile_height)
                self.tiles[i].append(tile)

    def drawFrames(self):

        #draw images in frames

        #initial width and height
        width = TILE_WIDTH
        height = TILE_HEIGHT

        if DYNAMIC_WINDOW_SCALING:
            #if dynamic scalling is available resize tiles
            # according to current window size
            window_width = self.centeral_widget.width()
            window_height = self.centeral_widget.height()
            (width, height) = reconfig_tile_geometery(window_width,
                                                      window_height,
                                                      self.grid_size)

        #add tile views for images
        self.addTiles(width, height)

        #read test image
        img = cv2.imread("fish.jpg")

        #resize test image
        img = cv2.resize(img, (width, height))

        #set test image in all tiles
        for i in range(0, self.grid_size):
            for j in range(0, self.grid_size):

                if random.random() < 0.5:
                    self.tiles[i][j].lable = 1
                else:
                    self.tiles[i][j].lable = 0
                self.tiles[i][j].image = img
                self.tiles[i][j].drawImage()

    def resizeEvent(self, event):
        self.drawFrames()
示例#2
0
class DBogUI(object):
    def __init__(self):
        self.db_handler = MyDBHandler()
        self.sqlTranslator = SqlTranslator()
        self.page_size = 10
        self.former_table_data = []
        self.current_table_data = []
        self.changedTableData = []
        self.cell_editable_flg = 0
        self.mainWidget = None
        self.centerLayout = None
        self.stackedWidget = None
        self.statusbar = None
        self.data_page = None
        self.data_page_layout = None
        self.menubar = None
        self.dataPanel = None
        self.data_panel_layout = None
        self.sqlTextEdit = None
        self.table_widget = None
        self.btn_panel = None
        self.btn_panel_layout = None
        self.addLnBtn = None
        self.rmvLnBtn = None
        self.queryBtn = None
        self.editBtn = None
        self.saveBtn = None
        self.treePanel = None
        self.treePanelLayout = None
        self.tableTree = None
        self.translate_page = None
        self.translate_page_layout = None
        self.translate_up_panel = None
        self.translate_up_panel_layout = None
        self.src_sql_text_edit = None
        self.dest_sql_text_edit = None
        self.translate_down_panel = None
        self.translate_down_panel_layout = None
        self.from_sql_type_combobox = None
        self.to_sql_type_combobox = None
        self.translate_btn = None
        self.swap_btn = None
        self.clear_btn = None
        self.login_page = None
        self.usmLabel = None
        self.unmTextEdit = None
        self.pwdLabel = None
        self.pwdTextEdit = None
        self.hostLabel = None
        self.hostTextEdit = None
        self.dbNameLabel = None
        self.dbNameTextEdit = None
        self.cnntDbBtn = None
        self.load_cfg_btn = None
        self.error_widgets = []
        self.plain_color = None

    # setup UI
    def setup_ui(self, MainWindow):
        # UI MainWindow
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(900, 600)
        # center widgets
        self.mainWidget = QWidget(MainWindow)
        # 设置字体
        self.mainWidget.setFont(QFont("Roman times", 11))
        self.mainWidget.setObjectName("mainWidget")
        MainWindow.setCentralWidget(self.mainWidget)
        self.centerLayout = QVBoxLayout(self.mainWidget)
        # 设置stacked widget
        self.stackedWidget = QStackedWidget(self.mainWidget)
        self.centerLayout.addWidget(self.stackedWidget)
        # 设置data page
        self.build_data_page()
        # layout不同的比例区分大小
        self.data_page_layout.setStretchFactor(self.dataPanel, 4)
        self.data_page_layout.setStretchFactor(self.treePanel, 1)
        # 设置login面板
        self.build_login_page()
        # 设置translate面板
        self.build_translate_page()
        # 将三个面板,加入stackedWidget
        self.stackedWidget.addWidget(self.data_page)
        self.stackedWidget.addWidget(self.login_page)
        self.stackedWidget.addWidget(self.translate_page)
        # menu
        self.build_menu_bar(MainWindow)
        # status bar
        self.statusbar = QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        # 刷新
        self.re_translate_ui(MainWindow)
        QMetaObject.connectSlotsByName(MainWindow)
        # refresh tree list
        self.db_handler.config(None, None, None, None)
        self.paint_tree()

    # data page
    def build_data_page(self):
        self.data_page = QWidget(self.mainWidget)
        self.centerLayout.addWidget(self.data_page)
        self.data_page_layout = QHBoxLayout(self.data_page)
        # 设置data面板
        self.build_tree_panel()
        # 设置data面板
        self.build_data_panel()
        # set event
        self.set_data_page_event()

    # menu
    def build_menu_bar(self, MainWindow):
        self.menubar = QMenuBar(MainWindow)
        self.menubar.setGeometry(QRect(0, 0, 400, 50))
        self.menubar.move(100, 100)
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        # 创建一个事件和一个特定图标
        show_data_action = QAction(QIcon('../resources/icon/database.png'), 'Operate', self)
        # show_data_action.setShortcut('Ctrl+Q')  # 设置事件的快捷方式
        show_data_action.setStatusTip('show data panel')  # 设置事件的状态提示
        show_data_action.triggered.connect(lambda: self.switch_page(0))  # 事件的触发
        login_menu = self.menubar.addMenu('&Data')  # 添加菜单file
        login_menu.addAction(show_data_action)  # 菜单添加事件
        # 创建一个事件和一个特定图标
        show_login_action = QAction(QIcon('../resources/icon/configure.png'), 'Config', self)
        # show_login_action.setShortcut('Ctrl+P')  # 设置事件的快捷方式
        show_login_action.setStatusTip('show login panel')  # 设置事件的状态提示
        show_login_action.triggered.connect(lambda: self.switch_page(1))  # 事件的触发
        login_menu = self.menubar.addMenu('&Login')  # 添加菜单file
        login_menu.addAction(show_login_action)  # 菜单添加事件
        # 创建一个事件和一个特定图标
        show_translate_action = QAction(QIcon('../resources/icon/translate.png'), 'Translate', self)
        # show_login_action.setShortcut('Ctrl+P')  # 设置事件的快捷方式
        show_translate_action.setStatusTip('show translate page')  # 设置事件的状态提示
        show_translate_action.triggered.connect(lambda: self.switch_page(2))  # 事件的触发
        translate_menu = self.menubar.addMenu('&Translate')  # 添加菜单file
        translate_menu.addAction(show_translate_action)  # 菜单添加事件

    # 设置data面板
    def build_data_panel(self):
        # data panel
        self.dataPanel = QWidget(self.data_page)
        self.data_page_layout.addWidget(self.dataPanel)
        self.data_panel_layout = QVBoxLayout(self.dataPanel)
        # SQL输入框
        self.sqlTextEdit = QTextEdit(self.dataPanel)
        self.sqlTextEdit.setObjectName("textEdit")
        # self.sqlTextEdit.setText("Enter your SQL here...")
        self.data_panel_layout.addWidget(self.sqlTextEdit)
        # 数据表格
        self.table_widget = TableWidget(self.dataPanel)
        self.table_widget.init_ui(0, 0, self.dataPanel)
        self.table_widget.set_page_controller(0)  # 表格设置页码控制
        self.table_widget.control_signal.connect(self.control_page)
        self.table_widget.setFixedSize(700, 400)
        self.data_panel_layout.addWidget(self.table_widget)
        # build inside panel
        self.build_btn_panel()
        self.data_panel_layout.setStretchFactor(self.sqlTextEdit, 2)
        self.data_panel_layout.setStretchFactor(self.table_widget, 6)
        self.data_panel_layout.setStretchFactor(self.btn_panel, 2)

    # button panel
    def build_btn_panel(self):
        # button panel
        self.btn_panel = QWidget(self.data_page)
        self.btn_panel.setFont(QFont("YaHei", 10))
        self.data_panel_layout.addWidget(self.btn_panel)
        self.btn_panel_layout = QHBoxLayout(self.btn_panel)
        # 表格操作:增加数据行按钮
        self.addLnBtn = QPushButton(self.btn_panel)
        self.addLnBtn.setMaximumSize(100, 80)
        self.addLnBtn.setObjectName("addLineButton")
        self.btn_panel_layout.addWidget(self.addLnBtn)
        # 表格操作:增加数据行按钮
        self.rmvLnBtn = QPushButton(self.btn_panel)
        self.rmvLnBtn.setObjectName("rmvLineButton")
        self.btn_panel_layout.addWidget(self.rmvLnBtn)
        # 查询按钮
        self.queryBtn = QPushButton(self.btn_panel)
        # self.queryBtn.setGeometry(QRect(150, 500, 100, 50))
        self.queryBtn.setObjectName("QueryButton")
        self.btn_panel_layout.addWidget(self.queryBtn)
        # 更新按钮
        self.editBtn = QPushButton(self.btn_panel)
        # self.editBtn.setGeometry(QRect(150, 500, 100, 50))
        self.editBtn.setObjectName("EditButton")
        self.btn_panel_layout.addWidget(self.editBtn)
        # 保存按钮
        self.saveBtn = QPushButton(self.btn_panel)
        # self.saveBtn.setGeometry(QRect(225, 500, 100, 50))
        self.saveBtn.setObjectName("SaveButton")
        self.btn_panel_layout.addWidget(self.saveBtn)
        # set partition / relative size
        self.btn_panel_layout.setStretchFactor(self.addLnBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.rmvLnBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.queryBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.editBtn, 1)
        self.btn_panel_layout.setStretchFactor(self.saveBtn, 1)

    # left catalog tree panel
    def build_tree_panel(self):
        self.treePanel = QWidget(self.data_page)
        self.treePanel.setFont(QFont("SimHei", 11))
        self.treePanelLayout = QVBoxLayout(self.treePanel)
        # self.treePanel.setGeometry(QRect(0, 0, 100, 600))
        self.data_page_layout.addWidget(self.treePanel)
        # tableTree list
        self.tableTree = QTreeWidget(self.treePanel)
        self.treePanelLayout.addWidget(self.tableTree)
        # 设置列数
        self.tableTree.setColumnCount(1)
        self.tableTree.setColumnWidth(0, 100)
        # 设置头的标题
        self.tableTree.setHeaderLabel('Table List')
        # 绑定点击事件
        self.tableTree.clicked.connect(self.on_tree_clicked)

    # 加载树形菜单
    def paint_tree(self):
        if not CTools.isEmpty(self.db_handler.db_name):
            self.tableTree.clear()
            self.sqlTextEdit.setText("use %s;show tables;" % self.db_handler.db_name)
            table_lst = self.show_tables()
            # repaint tree nodes
            root = QTreeWidgetItem(self.tableTree)
            root.setText(0, self.db_handler.db_name)
            for i in range(len(table_lst)):
                table_name = table_lst[i]
                table_node = QTreeWidgetItem(root)
                table_node.setText(0, table_name)
            self.tableTree.addTopLevelItem(root)

    # data page
    def build_translate_page(self):
        self.translate_page = QWidget(self.mainWidget)
        self.centerLayout.addWidget(self.translate_page)
        self.translate_page_layout = QVBoxLayout(self.translate_page)
        self.build_translate_panel()

    # 设置data面板
    def build_translate_panel(self):
        # translate panel
        self.translate_up_panel = QWidget(self.translate_page)
        self.translate_page_layout.addWidget(self.translate_up_panel)
        # append widget
        self.translate_up_panel_layout = QHBoxLayout(self.translate_up_panel)
        # SQL输入框
        self.src_sql_text_edit = QTextEdit(self.translate_up_panel)
        self.src_sql_text_edit.setObjectName("src_sql_text_edit")
        self.src_sql_text_edit.setText("Enter source SQL to translate here...")
        self.translate_up_panel_layout.addWidget(self.src_sql_text_edit)
        # SQL输入框
        self.dest_sql_text_edit = QTextEdit(self.translate_up_panel)
        self.dest_sql_text_edit.setObjectName("dest_sql_text_edit")
        self.dest_sql_text_edit.setText("SQL translate result will be placed here...")
        self.dest_sql_text_edit.setReadOnly(True)
        self.translate_up_panel_layout.addWidget(self.dest_sql_text_edit)
        # translate panel
        self.translate_down_panel = QWidget(self.translate_page)
        self.translate_page_layout.addWidget(self.translate_down_panel)
        # append widget
        self.translate_down_panel_layout = QHBoxLayout(self.translate_down_panel)
        # select from sql type
        self.from_sql_type_combobox = QComboBox(self.translate_down_panel)
        self.from_sql_type_combobox.setMaximumSize(150, 120)
        self.from_sql_type_combobox.setObjectName("from_sql_type_combobox")
        self.from_sql_type_combobox.addItems(self.sqlTranslator.valid_from_dialect)
        self.translate_down_panel_layout.addWidget(self.from_sql_type_combobox)
        self.plain_color = self.from_sql_type_combobox.palette().color(QPalette.Background).toRgb()
        # select to sql type
        self.to_sql_type_combobox = QComboBox(self.translate_down_panel)
        self.to_sql_type_combobox.setMaximumSize(150, 120)
        self.to_sql_type_combobox.setObjectName("to_sql_type_combobox")
        # self.to_sql_type_combobox.addItems(['', 'mssql', 'sqlserver', 'mssqlserver', 'mysql', 'oracle', 'db2', 'db2udb'])
        self.to_sql_type_combobox.addItems(self.sqlTranslator.valid_to_dialect)
        self.translate_down_panel_layout.addWidget(self.to_sql_type_combobox)
        # translate按钮
        self.translate_btn = QPushButton(self.translate_down_panel)
        self.translate_btn.setMaximumSize(150, 120)
        self.translate_btn.setObjectName("Translate")
        self.translate_btn.setText("Translate")
        self.translate_down_panel_layout.addWidget(self.translate_btn)
        self.translate_btn.clicked.connect(self.on_translate)
        # 交换按钮
        self.swap_btn = QPushButton(self.translate_down_panel)
        self.swap_btn.setMaximumSize(150, 120)
        self.swap_btn.setObjectName("Swap")
        self.swap_btn.setText("Swap")
        self.translate_down_panel_layout.addWidget(self.swap_btn)
        self.swap_btn.clicked.connect(self.on_swap)
        # 清空按钮
        self.clear_btn = QPushButton(self.translate_down_panel)
        self.clear_btn.setMaximumSize(150, 120)
        self.clear_btn.setObjectName("Clear")
        self.clear_btn.setText("Clear")
        self.translate_down_panel_layout.addWidget(self.clear_btn)
        self.clear_btn.clicked.connect(self.on_clear)

    def on_translate(self):
        src_sql_str = self.src_sql_text_edit.toPlainText()
        from_type = self.from_sql_type_combobox.currentText()
        to_type = self.to_sql_type_combobox.currentText()
        error_msg = ""
        if not src_sql_str or src_sql_str == "Enter source SQL to translate here...":
            error_msg += "source SQL should not be empty\n"
            self.src_sql_text_edit.setTextBackgroundColor(QColor(255, 0, 0, 255))
            self.error_widgets.append(self.src_sql_text_edit)
        else:
            if self.src_sql_text_edit in self.error_widgets:
                self.error_widgets.remove(self.src_sql_text_edit)
        if not from_type:
            error_msg += "from SQL Type should not be empty\n"
            self.from_sql_type_combobox.setStyleSheet("QComboBox{color: rgb(255,0,0,255)}")
            self.error_widgets.append(self.from_sql_type_combobox)
        else:
            if self.from_sql_type_combobox in self.error_widgets:
                self.error_widgets.remove(self.from_sql_type_combobox)
        if not to_type:
            error_msg += "to SQL Type should not be empty\n"
            self.to_sql_type_combobox.setStyleSheet("QComboBox{color: rgb(255,0,0,255)}")
            self.error_widgets.append(self.to_sql_type_combobox)
        else:
            if self.to_sql_type_combobox in self.error_widgets:
                self.error_widgets.remove(self.to_sql_type_combobox)
        if self.error_widgets:
            QMessageBox.warning(None, "Warning", error_msg)
            return
        if self.src_sql_text_edit in self.error_widgets:
            self.src_sql_text_edit.setTextBackgroundColor(QColor(255, 255, 255, 255))
        if self.from_sql_type_combobox in self.error_widgets:
            self.from_sql_type_combobox.setStyleSheet("QComboBox{color: rgb%s}" % str(self.plain_color.getRgb()))
        if self.to_sql_type_combobox in self.error_widgets:
            self.to_sql_type_combobox.setStyleSheet("QComboBox{color: rgb%s}" % str(self.plain_color.getRgb()))
        dest_sql_str = self.sqlTranslator.web_translate(src_sql_str, from_type, to_type)
        self.dest_sql_text_edit.setText(dest_sql_str)

    def on_swap(self):
        # origin
        # from_sql_type_index = self.from_sql_type_combobox.currentIndex()
        # to_sql_type_index = self.to_sql_type_combobox.currentIndex()
        from_sql_type_text = self.from_sql_type_combobox.currentText()
        to_sql_type_text = self.to_sql_type_combobox.currentText()
        src_sql_str = self.src_sql_text_edit.toPlainText()
        dest_sql_str = self.dest_sql_text_edit.toPlainText()
        # change content
        self.from_sql_type_combobox.setCurrentIndex(
            self.sqlTranslator.get_opposite_sql_type(from_sql_type_text, 'from'))
        self.to_sql_type_combobox.setCurrentIndex(self.sqlTranslator.get_opposite_sql_type(to_sql_type_text, 'to'))
        self.dest_sql_text_edit.setText(src_sql_str)
        self.src_sql_text_edit.setText(dest_sql_str)

    def on_clear(self):
        self.from_sql_type_combobox.setCurrentIndex(0)
        self.to_sql_type_combobox.setCurrentIndex(0)
        self.dest_sql_text_edit.setText("")
        self.src_sql_text_edit.setText("")

    # show data after clicking the menu item in tree
    def on_tree_clicked(self):
        item = self.tableTree.currentItem()
        table_name = item.text(0)
        # no response when click database label
        if table_name != self.db_handler.db_name:
            cTool.logger.info("switch to data of table, name: %s" % table_name)
            self.sqlTextEdit.setText("select * from %s limit 0, %d;" % (table_name, self.page_size))
            self.do_query()

    # when press append record button
    def on_append_record(self, table_name, page_size):
        if self.is_cell_editable():
            self.sqlTextEdit.setText("select * from %s limit 0, %d;" % (table_name, page_size))
            self.do_query()
        else:
            self.warn_action("Table is not allowed to append record now")

    # when action is fobidden
    def warn_action(self, err_msg="illegal action"):
        cTool.logger.info("%s" % err_msg)
        QMessageBox.warning(None, "Warning", err_msg)

    # when press remove record button
    def on_remove_record(self):
        if self.is_cell_editable():
            cell_item_list = self.table_widget.table.selectedItems()
            count = len(cell_item_list)
            row_num_set = set()
            for x in range(0, count):
                table_item = cell_item_list[x]
                row_num_set.add(self.table_widget.table.row(table_item))
            selected_pk_set = set(
                self.former_table_data[row_num][self.db_handler.def_pk_index] for row_num in row_num_set)
            self.db_handler.delete_sql = self.db_handler.buildDeleteSQL(self.db_handler.def_table_name,
                                                                        self.db_handler.def_pk_name,
                                                                        selected_pk_set)
            # rmv_cnt = self.db_handler.modifyRecords(delete_sql)
            # print("updated count is %d for SQL %s" % (rmv_cnt, delete_sql))
            # self.do_query()
            self.former_table_data = [former_row_data for former_row_data in
                                      self.former_table_data if
                                      former_row_data[self.db_handler.def_pk_index] not in selected_pk_set]
            self.update_table_data(self.former_table_data, self.db_handler.def_field_name_lst, False)
        else:
            self.warn_action("Table is not allowed to delete record now")

    # 设置login面板
    def build_login_page(self):
        # login panel
        self.login_page = QWidget(self.stackedWidget)
        self.login_page.setGeometry(QRect(100, 100, 600, 600))
        # self.login_page_layout = QVBoxLayout(self.login_page)
        # username label
        self.usmLabel = QLabel(self.login_page)
        self.usmLabel.setGeometry(QRect(100, 100, 150, 50))
        self.usmLabel.setText("username ")
        # self.login_page_layout.addWidget(self.usmLabel)
        # username input
        self.unmTextEdit = QLineEdit(self.login_page)
        self.unmTextEdit.setText("")
        self.unmTextEdit.setGeometry(QRect(300, 100, 450, 50))
        # self.login_page_layout.addWidget(self.unmTextEdit)
        # password label
        self.pwdLabel = QLabel(self.login_page)
        self.pwdLabel.setGeometry(QRect(100, 150, 150, 50))
        self.pwdLabel.setText("password ")
        # self.login_page_layout.addWidget(self.pwdLabel)
        # password input
        self.pwdTextEdit = QLineEdit(self.login_page)
        self.pwdTextEdit.setText("")
        self.pwdTextEdit.setGeometry(QRect(300, 150, 450, 50))
        # self.login_page_layout.addWidget(self.pwdTextEdit)
        # host label
        self.hostLabel = QLabel(self.login_page)
        self.hostLabel.setGeometry(QRect(100, 200, 120, 50))
        self.hostLabel.setText("host address ")
        # self.login_page_layout.addWidget(self.hostLabel)
        # host input
        self.hostTextEdit = QLineEdit(self.login_page)
        self.hostTextEdit.setText("")
        self.hostTextEdit.setGeometry(QRect(300, 200, 450, 50))
        # self.login_page_layout.addWidget(self.hostTextEdit)
        # database name label
        self.dbNameLabel = QLabel(self.login_page)
        self.dbNameLabel.setGeometry(QRect(100, 250, 150, 50))
        self.dbNameLabel.setText("database name ")
        # self.login_page_layout.addWidget(self.dbNameLabel)
        # database name input
        self.dbNameTextEdit = QLineEdit(self.login_page)
        self.dbNameTextEdit.setText("")
        self.dbNameTextEdit.setGeometry(QRect(300, 250, 450, 50))
        # self.login_page_layout.addWidget(self.dbNameTextEdit)
        # confirm to connect database
        self.cnntDbBtn = QPushButton(self.login_page)
        self.cnntDbBtn.setObjectName("DbButton")
        self.cnntDbBtn.setGeometry(QRect(100, 350, 300, 50))
        # self.login_page_layout.addWidget(self.cnntDbBtn)
        # connect database with config file
        self.load_cfg_btn = QPushButton(self.login_page)
        self.load_cfg_btn.setObjectName("LoadConfigButton")
        self.load_cfg_btn.setGeometry(QRect(450, 350, 300, 50))
        # self.login_page_layout.addWidget(self.load_cfg_btn)
        # set event
        self.set_login_panel()

    # build login panel
    def set_login_panel(self):
        self.cnntDbBtn.clicked.connect(self.cnnt_db)
        self.load_cfg_btn.clicked.connect(self.re_config_db)

    # connect to database
    def cnnt_db(self):
        unmStr = self.unmTextEdit.text()
        pwdStr = self.pwdTextEdit.text()
        hostStr = self.hostTextEdit.text()
        dbNameStr = self.dbNameTextEdit.text()
        self.db_handler.config(hostStr, unmStr, pwdStr, dbNameStr)
        self.paint_tree()
        self.switch_page(0)

    # connect to database with configuration file
    def re_config_db(self):
        self.db_handler.config(mVars.host, mVars.username, mVars.password, mVars.database_name)

    # set label text of widget
    def re_translate_ui(self, MainWindow):
        _translate = QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "DBog"))
        self.setWindowIcon(QIcon("../resources/icon/ball.ico"))
        self.queryBtn.setText(_translate("MainWindow", "Query"))
        self.editBtn.setText(_translate("MainWindow", "Edit"))
        self.saveBtn.setText(_translate("MainWindow", "Save"))
        self.cnntDbBtn.setText(_translate("MainWindow", "ConnectByInputConfiguration"))
        self.load_cfg_btn.setText(_translate("MainWindow", "LoadConfigurationFile"))
        self.addLnBtn.setText(_translate("MainWindow", "AppendRecord"))
        self.rmvLnBtn.setText(_translate("MainWindow", "RemoveRecord"))

    # set event of data page
    def set_data_page_event(self):
        self.queryBtn.clicked.connect(self.do_query)
        self.editBtn.clicked.connect(self.do_edit)
        self.saveBtn.clicked.connect(self.do_save)
        self.addLnBtn.clicked.connect(lambda: self.on_append_record(self.db_handler.def_table_name, self.page_size - 1))
        self.rmvLnBtn.clicked.connect(self.on_remove_record)

    # switch to another page
    def switch_page(self, pnIndex):
        self.stackedWidget.setCurrentIndex(int(pnIndex))

    # execute query action
    def do_query(self):
        cmd_str = self.sqlTextEdit.toPlainText().replace(";", "")
        # 获取数据
        result_set = self.db_handler.get_all_result(cmd_str)
        # 获取表结构定义
        # db_filed_lst = self.db_handler.gen_alter(self.db_handler.def_table_name)
        # self.db_handler.field_name_lst = [db_filed["Field"] for db_filed in db_filed_lst]
        self.db_handler.gen_filed_name_list()
        # withdraw former table data / cast value None to empty string
        self.former_table_data = [[str(item) if item else "" for item in row] for row in result_set]
        # change button status to enable or not
        self.change_btn_status()
        # fill in table with empty line
        self.refresh_table(cmd_str)
        cTool.logger.info("result set for %s is %s" % (
            cmd_str, ','.join([str(row_data) for row_data in result_set])))

    # change button status to enable or not
    def change_btn_status(self):
        if self.is_cell_editable():
            self.addLnBtn.setEnabled(True)
            self.rmvLnBtn.setEnabled(True)
            self.saveBtn.setEnabled(True)
        else:
            self.addLnBtn.setEnabled(False)
            self.rmvLnBtn.setEnabled(False)
            self.saveBtn.setEnabled(False)

    # refresh table widget
    def refresh_table(self, cmd_str):
        row_cnt = len(self.former_table_data)
        if row_cnt > 0:
            col_cnt = len(self.former_table_data[0])
            while row_cnt < self.page_size:
                self.former_table_data.append(["" for i in range(col_cnt)])
                row_cnt = row_cnt + 1
        # count record
        page_cnt = self.count_record(cmd_str)
        # refresh table
        self.update_table_data(self.former_table_data, self.db_handler.def_field_name_lst, True)
        # refresh table page
        self.refresh_table_pager(page_cnt)

    # execute count of record
    def count_record(self, cmd_str):
        if "limit" in cmd_str:
            cmd_str = cmd_str.split("limit")[0]
        cnt_sql = "select count(1) from (%s) sub" % cmd_str
        ret_cnt = self.db_handler.get_single_result(cnt_sql)
        page_cnt = 0
        if ret_cnt and len(ret_cnt) > 0:
            ret_cnt = int(ret_cnt[0])
            page_cnt = int(ret_cnt / 10)
            if page_cnt % 10 != 0:
                page_cnt = page_cnt + 1
        return page_cnt

    # make new name of each field
    def rename_fields(self):
        ret_dict = self.count_list(self.db_handler.def_field_name_lst)
        for field_name in self.db_handler.def_field_name_lst:
            if ret_dict[field_name] > 1:
                field_name = "%s_%d" % (field_name, ret_dict[field_name])
                ret_dict[field_name] = ret_dict[field_name] - 1

    # statistic item count in a list
    def count_list(self, target_lst):
        ret_dict = {}
        for i in set(target_lst):
            ret_dict[i] = target_lst.count(i)
        return ret_dict

    # show table of current database
    def show_tables(self):
        tables_sql = "show tables;"
        self.sqlTextEdit.setText(tables_sql)
        return [tbl_name_tp[0] for tbl_name_tp in self.db_handler.get_all_result(tables_sql)]

    # execute update
    def do_edit(self):
        cmd_str = self.sqlTextEdit.toPlainText()
        row_count = self.db_handler.modifyRecords(cmd_str)
        info_msg = "Edit finish, effected row count: %d" % row_count
        cTool.logger.info(info_msg)
        QMessageBox.information(self, "Info", info_msg)

    # @decorator()
    # execute save action
    def do_save(self):
        # edit result set of multi tables is forbidden
        if self.is_cell_editable():
            # clear first
            self.current_table_data.clear()
            row_count = self.table_widget.table.rowCount()
            column_count = self.table_widget.table.columnCount()
            for i in range(0, row_count):
                curRowData = []
                for j in range(0, column_count):
                    cellValStr = self.table_widget.table.item(i, j).text()
                    curRowData.append(cellValStr)
                self.current_table_data.append(curRowData)
            # assemble SQL from data
            deleteSQL = self.db_handler.delete_sql
            if deleteSQL:
                # reset to empty string
                self.db_handler.delete_sql = ""
            else:
                dataLst = self.get_delete_data()
                deleteSQL = self.db_handler.buildDeleteSQL(self.db_handler.def_table_name, self.db_handler.def_pk_name,
                                                           dataLst) if dataLst else ""
            cTool.logger.info(deleteSQL)
            dataLst = self.get_insert_data()
            insertSQL = self.db_handler.buildInsertSQL(self.db_handler.def_table_name, dataLst) if dataLst else ""
            cTool.logger.info(insertSQL)
            rmCnt = self.db_handler.modifyRecords(deleteSQL) if deleteSQL else 0
            inCnt = self.db_handler.modifyRecords(insertSQL) if insertSQL else 0
            info_msg = "Save finish, effect record count removed: %d,inserted: %d" % (rmCnt, inCnt)
            cTool.logger.info(info_msg)
            QMessageBox.information(self, "Info", info_msg)
        else:
            self.warn_action("Table is not allowed to update now")

    # is table cell item editable
    def is_cell_editable(self):
        self.cell_editable_flg = QtCore.Qt.ItemIsEnabled if self.db_handler.is_combine_sql else QtCore.Qt.ItemIsEditable
        return self.cell_editable_flg == 2

    # withdraw delete data
    def get_delete_data(self):
        fmrSet = set(map(lambda x: ",".join(x), self.former_table_data))
        curSet = set(map(lambda x: ",".join(x), self.current_table_data))
        diffSet = fmrSet.difference(curSet)
        return list(map(lambda diffStr: diffStr.split(",")[self.db_handler.def_pk_index], diffSet))

    # withdraw insert data
    def get_insert_data(self):
        curSet = set(map(lambda x: ",".join(x), self.current_table_data))
        fmrSet = set(map(lambda x: ",".join(x), self.former_table_data))
        diffSet = curSet.difference(fmrSet)
        return list(map(lambda diffStr: diffStr.split(","), diffSet))

    # update/refresh table data
    def update_table_data(self, matrix_data, fd_name_list, update_header_flg):
        row_cnt = len(matrix_data)
        col_cnt = len(matrix_data[0]) if len(matrix_data) > 0 and len(matrix_data[0]) > 0 else 0
        # refresh table model struct
        self.clear_table_data()
        # if update_header_flg:
        self.refresh_table_header(fd_name_list, row_cnt, col_cnt)
        # refresh table data
        for row_num in range(0, row_cnt):
            for col_num in range(0, col_cnt):
                cell_data = str(matrix_data[row_num][col_num]) if matrix_data[row_num][col_num] else ""
                # 设置表格内容(行, 列) 文字
                self.table_widget.table.setItem(row_num, col_num, QTableWidgetItem(cell_data))

    # update/refresh table header
    def refresh_table_header(self, fd_name_list, row_cnt, col_cnt):
        self.table_widget.table.setRowCount(row_cnt)
        self.table_widget.table.setColumnCount(col_cnt)
        self.table_widget.table.setHorizontalHeaderLabels(fd_name_list)

    # update/refresh table pager
    def refresh_table_pager(self, page_count):
        self.table_widget.totalPageNum.setText(str(page_count))

    # clear table data
    def clear_table_data(self):
        self.table_widget.table.setRowCount(0)
        self.table_widget.table.setColumnCount(0)

    # config table item
    def touch_table_item(self):
        flag = QtCore.Qt.ItemIsEnabled if self.db_handler.is_combine_sql else QtCore.Qt.ItemIsEditable
        col_cnt = self.table_widget.table.colorCount()
        row_cnt = self.table_widget.table.rowCount()
        for col_num in range(0, col_cnt):
            for row_num in range(0, row_cnt):
                QTableWidgetItem(self.table_widget.table.item(row_cnt, col_cnt)).setFlags(flag)
                # 设置第二列不可编辑
                # self.table_widget.table.setItemDelegateForColumn(col_num, EmptyDelegate(self))
                # self.table_widget.table.setItemDelegateForColumn(col_num, QTableWidget.itemDelegate())

    # control page
    def control_page(self, signal):
        total_page = self.table_widget.show_total_page()
        if "home" == signal[0]:
            self.table_widget.curPage.setText("1")
        elif "pre" == signal[0]:
            if 1 == int(signal[1]):
                QMessageBox.information(self, "提示", "已经是第一页了", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(str(int(signal[1]) - 1))
        elif "next" == signal[0]:
            if total_page == int(signal[1]):
                QMessageBox.information(self, "提示", "已经是最后一页了", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(str(int(signal[1]) + 1))
        elif "final" == signal[0]:
            self.table_widget.curPage.setText(str(total_page))
        elif "confirm" == signal[0]:
            if total_page < int(signal[1]) or int(signal[1]) < 0:
                QMessageBox.information(self, "提示", "跳转页码超出范围", QMessageBox.Yes)
                return
            self.table_widget.curPage.setText(signal[1])
        self.change_table_content()  # 改变表格内容

    # change table pager and content
    def change_table_content(self):
        """根据当前页改变表格的内容"""
        cur_page = int(self.table_widget.curPage.text())
        # 每页默认十条数据
        cTool.logger.info("current page: %s" % cur_page)
        page_limit_sql = "limit %s, %s" % ((cur_page - 1) * 10, 10)
        query_sql = "select * from %s %s;" % (self.db_handler.def_table_name, page_limit_sql)
        self.sqlTextEdit.setText(query_sql)
        self.do_query()
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        #adjusting main window
        self.resize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)
        self.move(0, 0)

        #setting scaling factor = 1
        self.zoom = 1

        #initializing and registering centeral widget
        self.centeral_widget = QWidget(self)
        self.setCentralWidget(self.centeral_widget)

        #initializing frame view
        self.frame_panel = DLabel(self.centeral_widget, self)
        # position frame panel
        self.frame_panel.move(FRAME_MARGIN_LEFT, FRAME_MARGIN_TOP)

        # set initial image
        self.frame_panel.setPixmap(QPixmap("initial.png"))

        #fur testing
        self.frame_panel.content = cv2.imread("initial.png")

        # enabling custom context menu
        self.setContextMenuPolicy(Qt.CustomContextMenu)

        #initializing menubar
        self.menubar = QMenuBar(self)

        #adding menu options
        self.file_menu = QMenu(self.menubar)
        self.zoom_menu = QMenu(self.menubar)
        self.marker_menu = QMenu(self.menubar)
        self.bounding_boxes = QMenu(self.menubar)

        #registering menubar
        self.menubar.move(0, 0)
        self.menubar.setDefaultUp(False)
        self.setMenuBar(self.menubar)

        #initializing and registering statusbar
        self.statusbar = QStatusBar(self)
        self.setStatusBar(self.statusbar)

        #add permanent frame lable to statusbar
        self.statusbar.frame_lable = QLabel(self)
        self.statusbar.frame_lable.setText("")
        self.statusbar.frame_lable.resize(100, 50)
        self.statusbar.frame_lable.move(DEFAULT_WINDOW_WIDTH - 100,
                                        DEFAULT_WINDOW_HEIGHT - 37)

        # set initail status tip
        self.statusbar.setStatusTip("Please choose a video file to begin")

        self.initialize_submenus()

        #assign visual texts to window, menus and submenus
        self.setTexts()

        #initailize video path
        self.vid_path = ""
        self.vid = False

        #adding slider for quick seek
        self.slider = QSlider(self.centeral_widget)

        #setting slider geometery
        self.slider.resize(DEFAULT_WINDOW_WIDTH - SLIDER_MARGINS * 2,
                           SLIDER_HEIGHT)
        self.slider.move(
            SLIDER_MARGINS, DEFAULT_WINDOW_HEIGHT - STATUSBAR_HEIGHT -
            SLIDER_HEIGHT - SLIDER_MARGINS)

        #setting orientation
        self.slider.setOrientation(Qt.Horizontal)

        #connecting mouse event
        self.slider.valueChanged.connect(self.moveSliderToClickedPosition)
        self.slider.setDisabled(True)

        # initialize context menu
        self.initContextMenu()

        # initializing mongo client and db
        self.database_client = MongoClient()
        self.db = self.database_client.fish

        # initializing collection
        self.collection = False

    def initContextMenu(self):

        # initialize custom context menu and submenu
        self.context_menu = QMenu(self)
        self.context_sub_menu = QMenu('Class', self)

        # create a delete action
        self.delete_action = QAction('Delete', self)
        self.delete_action.triggered.connect(self.frame_panel.deleteMarker)

        # create get lable action
        self.ask_lable = QAction('Ask Lable', self)
        self.ask_lable.triggered.connect(self.frame_panel.getLable)

        # adding dictionary for reverse search
        self.classes = {}

        # initializing class group
        self.class_group = QActionGroup(self)

        # adding submenu actions

        for item in CONFIGURED_CLASSES:
            self.classes[item] = QAction(item, self)
            self.classes[item].setCheckable(True)
            self.class_group.addAction(self.classes[item])

        # connecting group to handlers and setting default class
        self.class_group.triggered.connect(self.frame_panel.toggleMarkerClass)
        self.classes[CONFIGURED_CLASSES[0]].setChecked(True)

        # add delete action to menu
        self.context_menu.addAction(self.delete_action)

        # add ask lable action to menu
        self.context_menu.addAction(self.ask_lable)

        # add class actions to submenu
        self.context_sub_menu.addActions(self.class_group.actions())

        # regester submenu to menu
        self.context_menu.addMenu(self.context_sub_menu)

    def initialize_submenus(self):

        #intializing open submenu
        self.open_submenu = QAction(self)
        self.open_submenu.setShortcut('Ctrl+F')
        self.open_submenu.setStatusTip("Open")
        self.open_submenu.triggered.connect(self.selectFile)

        # intializing export submenu
        self.export_submenu = QAction(self)
        self.export_submenu.setShortcut('Ctrl+E')
        self.export_submenu.setStatusTip("Export")
        self.export_submenu.triggered.connect(self.exportImages)
        self.export_submenu.setDisabled(True)

        # intializing export submenu
        self.init_submenu = QAction(self)
        self.init_submenu.setShortcut('Ctrl+I')
        self.init_submenu.setStatusTip("Initialize")
        self.init_submenu.triggered.connect(self.initializeWithAlgorithm)
        self.init_submenu.setDisabled(True)

        #initializing options for zoom menu
        self.p50_submenu = QAction(self)
        self.p50_submenu.setStatusTip("50%")
        self.p50_submenu.setCheckable(True)
        self.p50_submenu.val = 0.5

        self.p100_submenu = QAction(self)
        self.p100_submenu.setStatusTip("100%")
        self.p100_submenu.setCheckable(True)
        self.p100_submenu.val = 1.0

        self.p150_submenu = QAction(self)
        self.p150_submenu.setStatusTip("150%")
        self.p150_submenu.setCheckable(True)
        self.p150_submenu.val = 1.5

        self.p200_submenu = QAction(self)
        self.p200_submenu.setStatusTip("200%")
        self.p200_submenu.setCheckable(True)
        self.p200_submenu.val = 2.0

        self.zoom_group = QActionGroup(self)
        self.zoom_group.addAction(self.p50_submenu)
        self.zoom_group.addAction(self.p100_submenu)
        self.zoom_group.addAction(self.p150_submenu)
        self.zoom_group.addAction(self.p200_submenu)
        self.p100_submenu.setChecked(True)
        self.zoom_group.setDisabled(True)
        self.zoom_group.triggered.connect(self.changeZoom)

        #registering file submenus
        self.file_menu.addAction(self.open_submenu)

        # registering bounding box submenus
        self.bounding_boxes.addAction(self.export_submenu)
        self.bounding_boxes.addAction(self.init_submenu)

        #registering zoom submenus
        self.zoom_menu.addActions(self.zoom_group.actions())

        #registering menus to menubar
        self.menubar.addAction(self.file_menu.menuAction())
        self.menubar.addAction(self.zoom_menu.menuAction())
        self.menubar.addAction(self.bounding_boxes.menuAction())

    def setTexts(self):
        self.setWindowTitle("FIBVID Analyser")

        #file menu
        self.file_menu.setTitle("File")

        self.open_submenu.setText("Open")

        self.bounding_boxes.setTitle("Boxes")

        # bouding boxes menu
        self.export_submenu.setText("Export as Images")
        self.init_submenu.setText("Initialize with Model")

        #zoom menu
        self.zoom_menu.setTitle("Zoom")

        self.p50_submenu.setText("50%")
        self.p100_submenu.setText("100%")
        self.p100_submenu.setText("100%")
        self.p150_submenu.setText("150%")
        self.p200_submenu.setText("200%")

    def selectFile(self):

        # get the file browser pop-up
        path, _ = QFileDialog.getOpenFileName(self.statusbar,
                                              'Select Video File', '/home')
        self.vid_path = path
        self.initVideo()

    def exportImages(self):

        cursor_pos = self.vid.get(cv2.CAP_PROP_POS_FRAMES)

        markers = self.collection.find({})
        markers.batch_size(1000000000)

        markers = [marker for marker in markers]

        height, width = self.current_frame.shape[0:2]

        empty = np.zeros((height, width, 3), np.uint8)

        center = (int(width / 2), int(height / 2))

        i = 0

        last = 0

        for marker in markers:

            progress = int(i * 100 / len(markers))

            img = empty.copy()
            cv2.circle(img, center, 100, (255, 255, 255))
            cv2.circle(img, center, progress, (255, 255, 255), -1)

            print(progress)

            self.setFrame(img)
            cv2.waitKey(1)
            last = progress

            box = [marker["start_pos"], marker["end_pos"]]

            rbox = RBox.fromPointBoundingBox(box)

            self.vid.set(cv2.CAP_PROP_POS_FRAMES, marker["frame_no"] - 1)

            rtt, frame = self.vid.read()

            patch = rbox.extractPatchFromImage(frame, square=True)

            name = str(marker["_id"])
            name = "./exports/" + name + ".png"
            cv2.imwrite(name, patch)
            i += 1

        self.initVideo()

    def showProgress(self, progress, fps):

        height, width = self.current_frame.shape[0:2]

        empty = np.zeros((height, width, 3), np.uint8)

        center = (int(width / 2), int(height / 2))

        cv2.circle(empty, center, progress, (255, 255, 255), -1)
        empty[center[1] - 100:center[1] + 100, center[0] - 100:center[0] +
              100] = cv2.bitwise_and(empty[center[1] - 100:center[1] + 100,
                                           center[0] - 100:center[0] + 100],
                                     empty[center[1] - 100:center[1] + 100,
                                           center[0] - 100:center[0] + 100],
                                     mask=self.coin)

        cv2.circle(empty, center, 98, (255, 255, 255))

        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(empty, "FPS: {}".format(fps), (10, 20), font, 0.5,
                    (255, 255, 255), 1, cv2.LINE_AA)

        self.setFrame(empty)

    def initializeWithAlgorithm(self):

        self.initVideo()
        self.analyser = RuntimeABGSC(vid_path=self.vid_path)

        # read progress image
        coin = cv2.imread("fish_coin_inv.png")
        self.coin = np.uint8(
            cv2.cvtColor(coin, cv2.COLOR_RGB2GRAY) > 150) * 255

        # empty collection
        self.collection.delete_many({})

        # frame cout
        frame_count = self.vid.get(cv2.CAP_PROP_FRAME_COUNT)
        counter = 0

        # disable scroll bar
        self.slider.setDisabled(True)

        # disable video
        self.vid = False

        markers = []

        while (self.analyser.status):

            # while video is not complete
            start = time()
            self.analyser.process(show_output=False)
            end = time()
            # increment counter
            counter += 1

            # calculate fps
            fps = int(1 / (end - start))

            # show progress in display window
            progress = int(counter * 100 / frame_count)
            self.showProgress(progress, fps)

            for box in self.analyser.final_boxes:

                start_pos = (int(box[0][0]), int(box[0][1]))
                end_pos = (int(box[1][0]), int(box[1][1]))

                # add resultant marker to markers of current frame
                marker = Marker(start_pos, end_pos, counter,
                                CONFIGURED_CLASSES[3])
                markers.append(marker.toDictionary())
                #self.frame_panel.markers.append(marker)

        # add markers to database
        self.collection.insert_many(markers)

        self.initVideo()

    def changeZoom(self):

        # get selected option's value
        zoom = self.zoom_group.checkedAction().val

        # reset frame according to new scaling
        self.resetFrame(zoom)

    def resetFrame(self, zoom):

        self.zoom = zoom
        self.frame_panel.zoom = zoom

        #resize frame panel
        self.frame_panel.resize(self.vid_height * zoom, self.vid_width * zoom)

        #resize window if dynamic scaling is enabled
        if DYNAMIC_WINDOW_SCALING:
            self.resize(
                self.vid_height * zoom + FRAME_MARGIN_LEFT +
                FRAME_MARGIN_RIGHT,
                self.vid_width * zoom + FRAME_MARGIN_TOP + FRAME_MARGIN_BOTTOM)

            # reposition slider
            self.slider.move(
                SLIDER_MARGINS,
                self.vid_width * zoom - (SLIDER_HEIGHT + SLIDER_MARGINS))

            # resize slider
            self.slider.resize(self.width() - SLIDER_MARGINS * 2,
                               SLIDER_HEIGHT)

            # reposition frame lable
            self.statusbar.frame_lable.move(self.width() - 100,
                                            self.height() - 37)

        self.currentFrame()

    def initVideo(self):

        # reset zoom
        self.zoom = 1.0

        self.vid = cv2.VideoCapture()
        # load selected video
        rtt = self.vid.open(self.vid_path)

        # clear slider
        self.slider.disconnect()

        # seek slider to start
        self.slider.setValue(0)
        self.slider.valueChanged.connect(self.moveSliderToClickedPosition)

        if rtt == False:
            if self.vid_path == "":
                self.statusbar.setStatusTip("No file selected")
            else:
                self.statusbar.setStatusTip("Error: Format not supported")

            # disalbe zoom options
            self.zoom_group.setDisabled(True)

            # clean video from memory
            self.vid = False

            # disable slider
            self.slider.setDisabled(True)

            return
        else:
            self.statusbar.setStatusTip("Ready")

            # set collection
            self.collection = self.db[self.vid_path]

        # seek to start
        self.vid.set(cv2.CAP_PROP_POS_FRAMES, 0)

        # read first frame
        rtt, frame = self.vid.read()

        # extract height and width of video
        self.vid_width, self.vid_height, self.vid_depth = np.shape(frame)
        self.vid_width *= self.zoom
        self.vid_height *= self.zoom

        self.current_frame = frame.copy()

        # resize frame view according to the video's geometery
        self.frame_panel.resize(self.vid_height, self.vid_width)

        # getting current frame number
        self.frame_no = self.vid.get(cv2.CAP_PROP_POS_FRAMES)

        # adding frame no to status tip
        self.statusbar.frame_lable.setText("Frame: {0}".format(
            "%05.0i" % int(self.frame_no)))

        # reset frame to start
        self.resetFrame(self.zoom)

        #enable zoom options
        self.zoom_group.setDisabled(False)

        #enable slider
        self.slider.setDisabled(False)

        # enable algorithmBasedInitialization
        self.init_submenu.setDisabled(False)
        self.export_submenu.setDisabled(False)

        # plot the current frame on canvas
        self.frame_panel.content = frame.copy()
        self.setImageFrame(frame)

        # seek slider to start
        self.moveSliderToPosition(0)

    def setImageFrame(self, frame):

        # load image with markers
        self.frame_panel.loadMarkers()
        self.frame_panel.reDraw()
        self.frame_panel.dragStatus = "Ended"

    def setFrame(self, frame):
        # convert raw frame to QPixmap and set it on frame_panel
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        self.frame_panel.setPixmap(
            QPixmap.fromImage(
                QImage(frame, self.vid_height * self.zoom,
                       self.vid_width * self.zoom,
                       self.vid_height * self.zoom * self.vid_depth,
                       QImage.Format_RGB888)))

    def nextFrame(self):
        # read next frame of video as Image
        rtt, frame = self.vid.read()

        if rtt:

            self.frame_panel.content = frame.copy()
            self.current_frame = frame.copy()

            # check if there is a next frame and convert cv Image to Pixmap
            frame = cv2.resize(frame,
                               None,
                               fx=self.zoom,
                               fy=self.zoom,
                               interpolation=cv2.INTER_CUBIC)

            # initializing markers on frame_panel
            self.frame_panel.markers = []

            # getting current frame number
            self.frame_no = self.vid.get(cv2.CAP_PROP_POS_FRAMES)

            # clear slider
            self.slider.disconnect()

            # seek slider to start
            self.slider.setValue(self.frame_no * 100 /
                                 self.vid.get(cv2.CAP_PROP_FRAME_COUNT))
            self.slider.valueChanged.connect(self.moveSliderToClickedPosition)

            self.setImageFrame(frame)

            #adding frame no to status tip
            self.statusbar.frame_lable.setText("Frame: {0}".format(
                "%05.0i" % int(self.frame_no)))

    def previousFrame(self):
        # set current frame number to 2 frames backward
        self.vid.set(cv2.CAP_PROP_POS_FRAMES,
                     self.vid.get(cv2.CAP_PROP_POS_FRAMES) - 2)
        # get next frame
        self.nextFrame()

    def currentFrame(self):

        self.frame_panel.content = self.current_frame.copy()

        # check if there is a next frame and convert cv Image to Pixmap
        frame = cv2.resize(self.current_frame.copy(),
                           None,
                           fx=self.zoom,
                           fy=self.zoom,
                           interpolation=cv2.INTER_CUBIC)
        self.setImageFrame(frame)

    def keyPressEvent(self, event):

        # don't do anything if no video is selected
        if self.vid == False:
            return

        # Key press event on window
        if type(event) == QKeyEvent and event.key() == Qt.Key_D:

            # navigate forward if D pressed
            self.nextFrame()
        if type(event) == QKeyEvent and event.key() == Qt.Key_A:

            # navigate backward if A pressed
            self.previousFrame()

        if type(event) == QKeyEvent and event.key() == Qt.Key_Plus:

            # zoom in
            self.resetFrame(self.zoom + 1)

        if type(event) == QKeyEvent and event.key() == Qt.Key_Minus:

            # zoom out if zoom factor is greater than 1
            if (self.zoom > 1.0):
                self.resetFrame(self.zoom - 1)

    def moveSliderToClickedPosition(self):

        # enable slider to move with mouse click

        # get click position relative to slider
        click_position = self.slider.mapFromGlobal(QCursor.pos()).x()
        self.moveSliderToPosition(click_position)

    def moveSliderToPosition(self, position):

        # set position of slider to position by setting its value
        slider_completion_ratio = position / self.slider.width()
        self.slider.setValue(slider_completion_ratio * 100)

        # seek video accordingly
        if self.vid:
            self.vid.set(
                cv2.CAP_PROP_POS_FRAMES,
                self.vid.get(cv2.CAP_PROP_FRAME_COUNT) *
                slider_completion_ratio)

            # refresh frame
            self.nextFrame()
class UiMainClientWindow(object):
    """
    Класс, создающий интерфейс главного окна.
    """
    def __init__(self, main_client_window):
        main_client_window.setObjectName("MainClientWindow")
        main_client_window.resize(756, 534)
        main_client_window.setMinimumSize(QSize(756, 534))
        self.centralwidget = QWidget(main_client_window)
        self.centralwidget.setObjectName("centralwidget")
        self.label_contacts = QLabel(self.centralwidget)
        self.label_contacts.setGeometry(QRect(10, 0, 101, 16))
        self.label_contacts.setObjectName("label_contacts")
        self.btn_add_contact = QPushButton(self.centralwidget)
        self.btn_add_contact.setGeometry(QRect(10, 450, 121, 31))
        self.btn_add_contact.setObjectName("btn_add_contact")
        self.btn_remove_contact = QPushButton(self.centralwidget)
        self.btn_remove_contact.setGeometry(QRect(140, 450, 121, 31))
        self.btn_remove_contact.setObjectName("btn_remove_contact")
        self.label_history = QLabel(self.centralwidget)
        self.label_history.setGeometry(QRect(300, 0, 391, 21))
        self.label_history.setObjectName("label_history")
        self.text_message = QTextEdit(self.centralwidget)
        self.text_message.setGeometry(QRect(300, 360, 441, 71))
        self.text_message.setObjectName("text_message")
        self.label_new_message = QLabel(self.centralwidget)
        self.label_new_message.setGeometry(QRect(300, 270, 450, 76))
        self.label_new_message.setObjectName("label_new_message")
        self.text_menu = QMenuBar(self.label_new_message)
        self.text_menu.move(0, 51)
        self.action_bold = QAction(QIcon(os.path.join(STATIC, 'img/b.jpg')),
                                   'Bold', self.text_message)
        self.action_italic = QAction(QIcon(os.path.join(STATIC, 'img/i.jpg')),
                                     'Italic', self.text_message)
        self.action_underlined = QAction(
            QIcon(os.path.join(STATIC, 'img/u.jpg')), 'Underlined',
            self.text_message)
        self.action_smile = QAction(
            QIcon(os.path.join(STATIC, 'img/smile.gif')), 'smile',
            self.text_message)
        self.list_contacts = QListView(self.centralwidget)
        self.list_contacts.setGeometry(QRect(10, 20, 251, 411))
        self.list_contacts.setObjectName("list_contacts")
        self.list_messages = QListView(self.centralwidget)
        self.list_messages.setGeometry(QRect(300, 20, 441, 271))
        self.list_messages.setObjectName("list_messages")
        self.btn_send = QPushButton(self.centralwidget)
        self.btn_send.setGeometry(QRect(610, 450, 131, 31))
        self.btn_send.setObjectName("btn_send")
        self.btn_clear = QPushButton(self.centralwidget)
        self.btn_clear.setGeometry(QRect(460, 450, 131, 31))
        self.btn_clear.setObjectName("btn_clear")
        main_client_window.setCentralWidget(self.centralwidget)
        self.menubar = QMenuBar(main_client_window)
        self.menubar.setGeometry(QRect(0, 0, 756, 21))
        self.menubar.setObjectName("menubar")
        self.menu_file = QMenu(self.menubar)
        self.menu_file.setObjectName("file")
        self.menu_contacts = QMenu(self.menubar)
        self.menu_contacts.setObjectName("contacts")
        self.menu_profile = QMenu(self.menubar)
        self.menu_profile.setObjectName("profile")
        main_client_window.setMenuBar(self.menubar)
        self.statusBar = QStatusBar(main_client_window)
        self.statusBar.setObjectName("statusBar")
        main_client_window.setStatusBar(self.statusBar)
        self.menu_exit = QAction(main_client_window)
        self.menu_exit.setObjectName("menu_exit")
        self.menu_add_contact = QAction(main_client_window)
        self.menu_add_contact.setObjectName("menu_add_contact")
        self.menu_del_contact = QAction(main_client_window)
        self.menu_del_contact.setObjectName("menu_del_contact")
        self.menu_file.addAction(self.menu_exit)
        self.menu_contacts.addAction(self.menu_add_contact)
        self.menu_contacts.addAction(self.menu_del_contact)
        self.menu_contacts.addSeparator()
        self.menu_profile_avatar = QAction(main_client_window)
        self.menu_profile_avatar.setObjectName("menu_profile_avatar")
        self.menu_profile.addAction(self.menu_profile_avatar)
        self.menu_profile.addSeparator()
        self.menubar.addAction(self.menu_file.menuAction())
        self.menubar.addAction(self.menu_contacts.menuAction())
        self.menubar.addAction(self.menu_profile.menuAction())
        self.retranslate_ui(main_client_window)
        self.btn_clear.clicked.connect(self.text_message.clear)
        QMetaObject.connectSlotsByName(main_client_window)

    def retranslate_ui(self, main_client_window):
        _translate = QCoreApplication.translate
        main_client_window.setWindowTitle(
            _translate("MainClientWindow", "SPELL Messenger - Client"))
        self.label_contacts.setText(
            _translate("MainClientWindow", "Список контактов:"))
        self.btn_add_contact.setText(
            _translate("MainClientWindow", "Добавить контакт"))
        self.btn_remove_contact.setText(
            _translate("MainClientWindow", "Удалить контакт"))
        self.label_history.setText(
            _translate("MainClientWindow", "История сообщений:"))
        self.label_new_message.setText(
            _translate("MainClientWindow", "Введите новое сообщение:"))
        self.btn_send.setText(
            _translate("MainClientWindow", "Отправить сообщение"))
        self.btn_clear.setText(_translate("MainClientWindow", "Очистить поле"))
        self.menu_file.setTitle(_translate("MainClientWindow", "Файл"))
        self.menu_contacts.setTitle(_translate("MainClientWindow", "Контакты"))
        self.menu_profile.setTitle(_translate("MainClientWindow", "Профиль"))
        self.menu_exit.setText(_translate("MainClientWindow", "Выход"))
        self.menu_profile_avatar.setText(
            _translate("MainClientWindow", "Аватар"))
        self.menu_add_contact.setText(
            _translate("MainClientWindow", "Добавить контакт"))
        self.menu_del_contact.setText(
            _translate("MainClientWindow", "Удалить контакт"))