示例#1
0
class RightPanel(FFrame):
    def __init__(self, app, parent=None):
        super().__init__(parent)
        self._app = app

        self.widget = None

        self._layout = QHBoxLayout(self)
        self.setLayout(self._layout)
        self.setObjectName('right_panel')
        self.set_theme_style()
        self.setup_ui()

    def set_theme_style(self):
        style_str = '''
            #{0} {{
                background: transparent;
            }}
        '''.format(self.objectName())
        self.setStyleSheet(style_str)

    def set_widget(self, widget):
        if self.widget and self.widget != widget:
            self._layout.replaceWidget(self.widget, widget)
        else:
            self._layout.addWidget(widget)
        self.widget = widget

    def setup_ui(self):
        self._layout.setContentsMargins(0, 0, 0, 0)
        self._layout.setSpacing(0)
示例#2
0
class MainWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.hex_data_manager = HexDataManager("")
        self.current_table_widget: TableWidget = TableWidget(
            self.hex_data_manager)
        self.controller_widget = ControllerWidget(self.current_table_widget,
                                                  self)
        self.box_l = QHBoxLayout()
        self.box_l.addWidget(self.current_table_widget)
        self.box_l.addWidget(self.controller_widget)
        self.setLayout(self.box_l)

    def open_file(self):

        file_name, _ = QFileDialog.getOpenFileName()
        if os.path.isfile(file_name):
            self.hex_data_manager = HexDataManager(file_name)
            table_widget = TableWidget(self.hex_data_manager)
            self.current_table_widget.close()
            self.box_l.replaceWidget(self.current_table_widget,
                                     table_widget)
            self.current_table_widget = table_widget
            self.controller_widget.set_table_widget(table_widget)
            self.controller_widget.update_current_page_label()

    def save_file(self):
        file_name, _ = QFileDialog.getSaveFileName()
        if file_name == "":
            return
        self.hex_data_manager.write_changes_in_file(file_name)

    def new_file(self):
        self.hex_data_manager = HexDataManager("")
        table_widget = TableWidget(self.hex_data_manager)
        self.current_table_widget.close()
        self.box_l.replaceWidget(self.current_table_widget,
                                 table_widget)
        self.current_table_widget = table_widget
        self.controller_widget.set_table_widget(table_widget)
        self.controller_widget.update_current_page_label()

    def keyPressEvent(self, a0: QKeyEvent) -> None:
        if a0.key() == 16777220:
            self.current_table_widget.disable_edit_mode()
        else:
            self.current_table_widget.handle_representation_symbol(a0.text())

    def wheelEvent(self, a0: QWheelEvent) -> None:
        if a0.angleDelta().y() > 0:
            self.controller_widget.get_right()
        else:
            self.controller_widget.get_left()
class FileDialog(QWidget):
    def __init__(self, parent: StatisticsGUI = None):
        super().__init__()
        self.parent = parent
        self.setWindowIcon(QIcon('../images/Logo.png'))
        layout = QVBoxLayout()
        # first line: select type of comparison, inter-dataset or intra-dataset
        self.layout_horizontal1 = QHBoxLayout()
        self.comp_type_label = QLabel("Select comparison type: ")
        self.comp_type = QComboBox()
        self.comp_type.addItem("Inter-Dataset Comparison")
        self.comp_type.addItem("Intra-Dataset Comparison")
        self.comp_type_crt_text = self.comp_type.currentText()
        self.comp_type.currentTextChanged.connect(self.comp_type_changed)
        self.layout_horizontal1.addWidget(self.comp_type_label, 1)
        self.layout_horizontal1.addWidget(self.comp_type, 2, alignment=Qt.AlignLeft)
        self.layout_horizontal1.addStretch(3)

        layout.addLayout(self.layout_horizontal1, 2)
        layout.addStretch(1)
        # second line: check datasets to compare (inter) / dataset to compare classes (intra)
        self.layout_vertical2_inter = QVBoxLayout()
        self.check_ds = QLabel('Check datasets to compare: ')
        self.layout_horizontal2 = QHBoxLayout()
        self.check_boxes = DataSetsManager.get_data_set_to_compare(self.layout_horizontal2)
        self.layout_vertical2_inter.addWidget(self.check_ds, 1, alignment=Qt.AlignLeft)
        self.layout_vertical2_inter.addLayout(self.layout_horizontal2, 2)

        self.wid_1_inter = QWidget()
        self.wid_1_inter.setLayout(self.layout_vertical2_inter)
        layout.addWidget(self.wid_1_inter, 2)
        layout.addStretch(1)

        # for intra
        self.layout_vertical2_intra = QHBoxLayout()
        self.select_ds = QLabel("Select dataset: ")
        self.list_ds = DataSetsManager.get_data_set_combo()
        self.list_ds.setCurrentIndex(0)
        self.intra_ds_current = self.list_ds.itemText(0)
        self.list_ds.currentTextChanged.connect(self.dataset_changed)
        self.layout_vertical2_intra.addWidget(self.select_ds, 1)
        self.layout_vertical2_intra.addWidget(self.list_ds, 2, alignment=Qt.AlignLeft)
        self.layout_vertical2_intra.addStretch(3)
        self.wid_1_intra = QWidget()
        self.wid_1_intra.setLayout(self.layout_vertical2_intra)
        # third line: select metric for comparison
        self.layout_hor3_inter = QHBoxLayout()
        self.select_metric_label = QLabel("Select metric: ")
        self.select_metric_inter = QComboBox()
        self.dict_metric_inter = {"Number of detections": ("detections_count", 'd'),
                             "Number of detections (unique per box)": ("unique_truth_count", 'd'),
                             "Precision": ("precision", 'f'),
                             "Recall": ("recall", 'f'),
                             "F1 score": ("F1-score", 'f'),
                             "True Positives": ("TP", 'd'),
                             "False Positives": ("FP", 'd'),
                             "False Negatives": ("FN", 'd'),
                             "Average IoU": ("average_IoU", 'p'),
                             "mean Average Precision": ("mAP", 'p'),
                             "Detection time": ("Time_seconds", 'd')
                             }
        for metric in self.dict_metric_inter:
            self.select_metric_inter.addItem(metric)
        self.layout_hor3_inter.addWidget(self.select_metric_label, 1)
        self.layout_hor3_inter.addWidget(self.select_metric_inter, 2, alignment=Qt.AlignLeft)
        self.layout_hor3_inter.addStretch(3)
        self.wid_2_inter = QWidget()
        self.wid_2_inter.setLayout(self.layout_hor3_inter)
        layout.addWidget(self.wid_2_inter, 2)

        # for intra
        self.layout_hor3_intra = QHBoxLayout()
        self.select_metric_intra = QComboBox()
        self.dict_metric_intra = {"Average precision": ("ap", 'p'),
                             "Number of detections (unique per box)": ("TC", 'd'),
                             "Precision": ("Precision", 'f'),
                             "Recall": ("Recall", 'f'),
                             "F1 score": ("F1-score", 'f'),
                             "True Positives": ("TP", 'd'),
                             "False Positives": ("FP", 'd'),
                             "False Negatives": ("FN", 'd'),
                             "Average IoU": ("Avg_IOU", 'p')
                             }
        for metric in self.dict_metric_intra:
            self.select_metric_intra.addItem(metric)
        self.select_metric_label_intra = QLabel("Select metric: ")
        self.layout_hor3_intra.addWidget(self.select_metric_label_intra, 1)
        self.layout_hor3_intra.addWidget(self.select_metric_intra, 2, alignment=Qt.AlignLeft)
        self.layout_hor3_intra.addStretch(3)
        self.wid_2_intra = QWidget()
        self.wid_2_intra.setLayout(self.layout_hor3_intra)

        # fourth line: select chart type
        self.layout_hor4 = QHBoxLayout()
        self.select_chart = QComboBox()
        self.select_chart.addItem("Horizontal Bar chart")
        self.select_chart.addItem("Vertical Bar chart")
        self.select_chart.addItem("Pie chart")
        self.select_chart_label = QLabel("Select chart type: ")
        self.layout_hor4.addWidget(self.select_chart_label, 1)
        self.layout_hor4.addWidget(self.select_chart, 2, alignment=Qt.AlignLeft)
        self.layout_hor4.addStretch(3)
        layout.addLayout(self.layout_hor4, 1)
        # fifth line: chart
        self.graph_layout = QHBoxLayout()
        self.crt_chart = QWidget()
        self.graph_layout.addWidget(self.crt_chart)
        layout.addLayout(self.graph_layout, 10)
        # sixth line: Run and Back
        self.back_button = QPushButton("Back")
        self.run_button = QPushButton("Run")
        self.hor_box = QHBoxLayout()
        self.hor_box.addStretch(9)
        self.hor_box.addWidget(self.back_button, 1)
        self.hor_box.addWidget(self.run_button, 1)
        self.back_button.clicked.connect(self.back_statistics)
        self.run_button.clicked.connect(self.run_statistics)
        layout.addLayout(self.hor_box, 1)

        self.wid_1_inter.setObjectName("StatisticsLayout")
        self.wid_1_intra.setObjectName("StatisticsLayout")
        self.wid_2_inter.setObjectName("StatisticsLayout")
        self.wid_2_intra.setObjectName("StatisticsLayout")

        self.run_statistics()
        self.setLayout(layout)

    def comp_type_changed(self, text):
        if text != self.comp_type_crt_text:
            self.comp_type_crt_text = text
            if text[:5] == "Inter":
                self.wid_1_intra.hide()
                self.wid_2_intra.hide()

                self.wid_1_inter.show()
                self.wid_2_inter.show()

                self.layout().replaceWidget(self.wid_1_intra, self.wid_1_inter)
                self.layout().replaceWidget(self.wid_2_intra, self.wid_2_inter)

            elif text[:5] == "Intra":
                self.wid_1_inter.hide()
                self.wid_2_inter.hide()

                self.wid_1_intra.show()
                self.wid_2_intra.show()

                self.layout().replaceWidget(self.wid_1_inter, self.wid_1_intra)
                self.layout().replaceWidget(self.wid_2_inter, self.wid_2_intra)

    def dataset_changed(self, text):
        self.intra_ds_current = text

    def back_statistics(self):
        self.parent.back_to_parent()

    def run_statistics(self):
        text = self.comp_type_crt_text
        x_vals = []
        y_vals = []
        if text[:5] == "Inter":
            data_sets = []
            for check_box in self.check_boxes:
                if check_box.isChecked():
                    data_sets.append(self.check_boxes[check_box])

            metric = self.select_metric_inter.currentText()
            metric_alias = self.dict_metric_inter[metric]
            res = ResultsManager.get_results_inter(metric_alias, data_sets)
            # self.graphWidget.plot(data_sets, res)
            x_vals = data_sets
            y_vals = res
        elif text[:5] == "Intra":
            ds_name = self.intra_ds_current
            metric = self.select_metric_intra.currentText()
            metric_alias = self.dict_metric_intra[metric]
            names, res = ResultsManager.get_results_intra(metric_alias, ds_name)
            # self.graphWidget.plot(names, res)
            x_vals = names
            y_vals = res
        #horizontal bar
        chart_view = None
        if self.select_chart.currentIndex() == 0:
            chart = QChart(flags=Qt.WindowFlags())
            series = QHorizontalBarSeries()
            for i in range(len(x_vals)):
                name = x_vals[i]
                set0 = QBarSet(name)
                set0.append(y_vals[i])
                series.append(set0)
            chart.addSeries(series)
            if text[:5] == "Inter":
                chart.setTitle(f"Comparison by {self.select_metric_inter.currentText()}")
            else:
                chart.setTitle(f"Comparison by {self.select_metric_intra.currentText()}")
            chart.setAnimationOptions(QChart.SeriesAnimations)

            chart_view = QChartView(chart)
            self.graph_layout.replaceWidget(self.crt_chart, chart_view)
            self.crt_chart = chart_view

            axisX = QValueAxis()
            chart.addAxis(axisX, Qt.AlignBottom)
            series.attachAxis(axisX)

            axisX.applyNiceNumbers()

            chart.legend().setVisible(True)
            chart.legend().setAlignment(Qt.AlignBottom)

            chart_view.setRenderHint(QPainter.Antialiasing)
            chart_view.setBackgroundBrush(QColor(0, 0, 0, 255))
            chart.setBackgroundBrush(QColor(255, 255, 0, 255))
        # vertical bar
        elif self.select_chart.currentIndex() == 1:
            chart = QChart(flags=Qt.WindowFlags())
            series = QBarSeries()
            for i in range(len(x_vals)):
                name = x_vals[i]
                set0 = QBarSet(name)
                set0.append(y_vals[i])
                series.append(set0)
            chart.addSeries(series)
            if text[:5] == "Inter":
                chart.setTitle(f"Comparison by {self.select_metric_inter.currentText()}")
            else:
                chart.setTitle(f"Comparison by {self.select_metric_intra.currentText()}")
            chart.setAnimationOptions(QChart.SeriesAnimations)

            chart_view = QChartView(chart)
            self.graph_layout.replaceWidget(self.crt_chart, chart_view)
            self.crt_chart = chart_view

            axisY = QValueAxis()
            chart.addAxis(axisY, Qt.AlignLeft)
            series.attachAxis(axisY)
            axisY.applyNiceNumbers()

            chart.legend().setVisible(True)
            chart.legend().setAlignment(Qt.AlignBottom)

            chart_view.setRenderHint(QPainter.Antialiasing)
            chart_view.setBackgroundBrush(QColor(0, 0, 0, 255))
            chart.setBackgroundBrush(QColor(255, 255, 0, 255))
        # pie chart
        elif self.select_chart.currentIndex() == 2:
            chart = QChart(flags=Qt.WindowFlags())
            series = QPieSeries()
            for i in range(len(x_vals)):
                series.append(x_vals[i], y_vals[i])
            chart.addSeries(series)
            if text[:5] == "Inter":
                chart.setTitle(f"Comparison by {self.select_metric_inter.currentText()}")
            else:
                chart.setTitle(f"Comparison by {self.select_metric_intra.currentText()}")
            chart.setAnimationOptions(QChart.SeriesAnimations)
            chart.legend().setAlignment(Qt.AlignBottom)
            # chart.legend().setFont(QFont("Arial", 12))

            chart_view = QChartView(chart)
            self.graph_layout.replaceWidget(self.crt_chart, chart_view)
            self.crt_chart = chart_view

            chart_view.setRenderHint(QPainter.Antialiasing)
            chart_view.setBackgroundBrush(QColor(0, 0, 0, 255))
            chart.setBackgroundBrush(QColor(255, 255, 0, 255))

        chart_view.setRubberBand(QChartView.HorizontalRubberBand)
        chart_view.setRubberBand(QChartView.VerticalRubberBand)
class OptionsSelector(BaseSelector):
    options = None

    # can stay not used

    def __init__(self,
                 *args,
                 base,
                 options=None,
                 add_none=False,
                 on_select=None,
                 **kwargs):
        super().__init__(*args, base=base, **kwargs)
        self.add_none = add_none
        self.on_select = on_select
        self.options = options or self.options
        self.option_items = list(options.items())
        self.options_selector = None
        assert self.options is not None, "Please provide either options as argument or subclass"

        self.layout = QHBoxLayout()

        self.setLayout(self.layout)

        self.buildOrReplaceSelector()

    def buildOrReplaceSelector(self):

        options_selector = QComboBox()

        if self.add_none: options_selector.addItem("-----")
        for key, option in self.option_items:
            options_selector.addItem(option)

        options_selector.currentIndexChanged.connect(self.indexChanged)

        if len(self.option_items) > 0 and not self.add_none:
            self.selected_option = self.option_items[0][
                0]  # We automatically select the first item once rebuilding

        if not self.options_selector:
            self.layout.addWidget(options_selector)
            self.model_selector = options_selector

        else:
            self.layout.replaceWidget(self.options_selector, options_selector)
            self.model_selector.close()
            self.model_selector.setParent(None)
            self.model_selector = options_selector

        self.layout.update()

        return self.options_selector

    def indexChanged(self, index):
        if self.add_none:
            if index == 0:
                self.selected_option = None
            else:
                self.selected_option = self.option_items[index - 1][
                    0]  # First item is the key not the label
        else:
            self.selected_option = self.option_items[index][0]

        if self.on_select: self.on_select(self.selected_option)

    def getValue(self):
        return self.selected_option
示例#5
0
class TableWidget(QWidget):
    def __init__(self, hex_data_manager):
        super().__init__()
        self.is_representation_mode_enabled = False
        self.representation_pointer = 0
        self.current_representation_edit: RepresentationEditWidget = None
        self.representation_unit_widgets_dict = {}
        self.hex_unit_widgets_dict = {}
        self.null_widgets_dict = {}
        self.verification = HexByteVerificator()
        self.translator = HexIntTranslator()
        self.hex_data_manager: HexDataManager = hex_data_manager
        self.integer_hexer = IntegerHexer()
        self.lines_per_page = 10
        self.bytes_per_page = 16 * self.lines_per_page
        self.hex_pages = {}
        self.repr_pages = {}
        self.current_edit: HexUnitEdit = None
        self.current_page = 0
        self.page_count = 0

        self.main_layout = QHBoxLayout()
        self.setLayout(self.main_layout)
        self.init_pages()

    def init_page(self, page_number):
        representation_page_widget = TablePageWidget(16)
        representation_widgets_list = []
        hex_widgets_list = []
        null_widgets = []
        self.null_widgets_dict[page_number] = null_widgets
        self.representation_unit_widgets_dict[
            page_number] = representation_widgets_list
        self.hex_unit_widgets_dict[page_number] = hex_widgets_list
        hex_page_widget = TablePageWidget(17)
        page_array = byte_value = self.hex_data_manager.get_page(page_number)
        self.hex_pages[page_number] = hex_page_widget
        self.repr_pages[page_number] = representation_page_widget
        for line in range(self.lines_per_page):
            self.hex_pages[page_number].add_table_element(
                QLabel(
                    self.integer_hexer.get_hex_string(self.bytes_per_page *
                                                      page_number +
                                                      line * 16)))
            for c in range(16):
                byte_position = 16 * line + c
                if byte_position >= len(page_array):
                    unit_representation =\
                        RepresentationUnitWidget("",
                                                 byte_position,
                                                 self)
                    representation_widgets_list.append(unit_representation)
                    self.repr_pages[page_number].add_table_element(
                        unit_representation)
                    null_widget = NullHexUnitWidget(self, byte_position)
                    self.hex_pages[page_number].add_table_element(null_widget)
                    null_widgets.append(null_widget)
                    continue
                unit_representation = RepresentationUnitWidget(
                    chr(page_array[byte_position]), byte_position, self)
                representation_widgets_list.append(unit_representation)
                self.repr_pages[page_number].add_table_element(
                    unit_representation)
                hex_unit = HexUnitWidget(page_number, 16 * line + c,
                                         page_array, self, unit_representation,
                                         self.translator)
                self.hex_pages[page_number].add_table_element(hex_unit)
                hex_widgets_list.append(hex_unit)

    def set_element(self, position_on_page, value_int):
        h_widget = self.hex_unit_widgets_dict[
            self.current_page][position_on_page]
        (h_widget.set_value(value_int))
        (self.representation_unit_widgets_dict[self.current_page]
         [position_on_page].setText(chr(value_int)))
        self.hex_data_manager.set_value(self.current_page, position_on_page,
                                        value_int)
        was_edit = value_int != h_widget.init_value
        h_widget.set_was_edit(was_edit)

    def init_pages(self):
        self.page_count = self.hex_data_manager.pages_count
        self.init_page(0)
        self.main_layout.addWidget(self.hex_pages[0])
        self.main_layout.addWidget(self.repr_pages[0])

    def open_null_widgets(self, null_widget):
        self.disable_representation_edit()
        null_widgets = self.null_widgets_dict[self.current_page]
        index = null_widgets.index(null_widget)
        grid_layout: QGridLayout = self.hex_pages[
            self.current_page].new_grid_layout
        self.hex_data_manager.append_empty_bytes_for_page(
            self.current_page, index + 1)
        for i in range(index + 1):
            hex_unit_widget = HexUnitWidget(
                self.current_page, null_widgets[i].position_on_page,
                self.hex_data_manager.get_page(self.current_page), self,
                self.representation_unit_widgets_dict[self.current_page][
                    null_widgets[i].position_on_page], self.translator)
            self.hex_unit_widgets_dict[self.current_page].append(
                hex_unit_widget)
            null_widgets[i].close()

            grid_layout.replaceWidget(null_widgets[i], hex_unit_widget)

        self.null_widgets_dict[self.current_page] = null_widgets[index + 1:]

    def set_representation_cursor(
            self, representation_unit_widget: RepresentationUnitWidget):
        self.disable_representation_edit()
        grid_layout: QGridLayout = self.repr_pages[
            self.current_page].new_grid_layout
        self.current_representation_edit = RepresentationEditWidget(
            representation_unit_widget)
        self.is_representation_mode_enabled = True
        self.representation_pointer = (
            representation_unit_widget.position_on_page)
        representation_unit_widget.hide()

        grid_layout.replaceWidget(representation_unit_widget,
                                  self.current_representation_edit)

    def handle_representation_symbol(self, key_text: str):
        if len(key_text) == 0:
            return
        if not key_text.isascii():
            return
        if self.current_representation_edit is not None:

            pointer = (self.current_representation_edit.
                       representation_unit_widget.position_on_page) + 1
            if pointer > len(self.hex_unit_widgets_dict[self.current_page]):
                return
            self.disable_representation_edit()

            self.set_element(pointer - 1, ord(key_text))
            if pointer > self.bytes_per_page - 1:
                return
            unit_widget = self.representation_unit_widgets_dict[
                self.current_page][pointer]
            if pointer > len(
                    self.hex_unit_widgets_dict[self.current_page]) - 1:
                return
            self.set_representation_cursor(unit_widget)

    def disable_representation_edit(self):
        if self.current_representation_edit is None:
            return
        grid_layout: QGridLayout = self.repr_pages[
            self.current_page].new_grid_layout
        rep_widget = (
            self.current_representation_edit.representation_unit_widget)
        grid_layout.replaceWidget(self.current_representation_edit, rep_widget)
        rep_widget.show()
        self.current_representation_edit.close()
        self.current_representation_edit = None
        self.representation_pointer = 0
        self.is_representation_mode_enabled = False

    def new_page(self):
        if len(self.null_widgets_dict[self.current_page]) > 0:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Critical)
            msg.setText("Для того, чтобы создать новую страницу, "
                        "необходимо избавиться от NULL на этой."
                        "\nНажмите на последний NULL. И все NULL перед ним "
                        "превратятся в действующие элементы.")
            msg.setWindowTitle("Не получилось!")

            msg.exec_()
            return
        self.page_count += 1
        self.init_page(self.current_page + 1)
        self.page_set(self.current_page + 1)

    def disable_edit_mode(self):
        self.disable_representation_edit()
        if self.current_edit is not None:
            self.current_edit.close()

            new_value = self.current_edit.text()

            if self.verification.verify_hex_byte(new_value):
                new_value_hex = new_value.upper()
                new_value_int = self.translator.get_int(new_value_hex)
            else:
                new_value_int = self.current_edit.hex_unit_widget.value

            was_edit = (new_value_int !=
                        self.current_edit.hex_unit_widget.init_value)
            self.current_edit.hex_unit_widget.set_was_edit(was_edit)

            representation: QLabel = (
                self.current_edit.hex_unit_widget.representation)

            representation.setText(chr(new_value_int))
            self.hex_data_manager.set_value(
                self.current_edit.hex_unit_widget.page_on,
                self.current_edit.hex_unit_widget.position_on_page,
                new_value_int)
            self.hex_pages[self.current_page].new_grid_layout.replaceWidget(
                self.current_edit, self.current_edit.hex_unit_widget)

            self.current_edit.hex_unit_widget.set_value(new_value_int)
            self.current_edit.hex_unit_widget.show()

    def make_edit(self, hex_unit_widget: HexUnitWidget):
        self.disable_edit_mode()
        qline = HexUnitEdit(hex_unit_widget)
        hex_unit_widget.hide()
        self.hex_pages[self.current_page].new_grid_layout.replaceWidget(
            hex_unit_widget, qline)
        self.current_edit = qline
        qline.setFocus()
        qline.selectAll()

    def page_set(self, number):
        self.disable_edit_mode()
        if number not in range(0, self.page_count):
            return
        if number not in self.hex_pages:
            self.init_page(number)

        self.hex_pages[self.current_page].hide()
        self.repr_pages[self.current_page].hide()
        self.main_layout.replaceWidget(
            self.hex_pages[self.current_page],
            self.hex_pages[number],
        )
        self.main_layout.replaceWidget(
            self.repr_pages[self.current_page],
            self.repr_pages[number],
        )
        self.current_page = number
        self.hex_pages[self.current_page].show()
        self.repr_pages[self.current_page].show()
示例#6
0
class RepresentationSelectorDialog(BaseMixin, QDialog):
    def __init__(self, *args, layer=None, **kwargs):
        super().__init__(*args, **kwargs)

        self.representations = QueryList(
            """
            query {
                myrepresentations(ordering: "-time") {
                    id
                    name
                    store
                    tags
                }
            }
        """, Representation).run()

        self.sidebar = self.buildSidebar()
        self.list = self.buildList()

        mainLayout = QVBoxLayout(self)

        self.main_widget = QWidget()
        # Central Widget
        self.layout = QHBoxLayout()
        self.layout.addWidget(self.sidebar)
        self.layout.setStretch(0, 30)
        self.layout.addWidget(self.list)
        self.layout.setStretch(1, 300)
        self.main_widget.setLayout(self.layout)

        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Cancel)
        buttonBox.accepted.connect(self.create)
        buttonBox.rejected.connect(self.reject)

        mainLayout.addWidget(self.main_widget)
        mainLayout.addWidget(buttonBox)
        self.setMinimumHeight(600)
        self.setMinimumWidth(1000)
        self.setLayout(mainLayout)

    def onPressFilter(self, *args):

        values = {
            "sample": self.sampleSelector.getValue(),
            "experiment": self.experimentSelector.getValue(),
            "tags": self.tagsField.getValue(),
            "ordering": self.orderingSelector.getValue()
        }

        print(values)

        self.representations = QueryList(
            """
            query FilteredMyRepresentations($tags: String,
                $ordering: String,
                $sample: ID,
                $experiment: ID
                ){
                myrepresentations(tags: $tags, ordering:$ordering, sample: $sample, experiment: $experiment) {
                    id
                    name
                    store
                    tags  
                }
            }
        """, Representation).run(variables=values)

        newlist = self.buildList()
        self.layout.replaceWidget(self.list, newlist)
        self.list.close()
        self.list.setParent(None)
        self.list = newlist

    @asyncSlot()
    async def create(self):
        indices = [i.row() for i in self.list.selectedIndexes()]
        self.selected_reps = [self.representations[i] for i in indices]
        self.accept()

    def buildSidebar(self):

        # Filter
        self.filterBox = QGroupBox("Filter")

        self.sampleSelector = SampleSelector(parent=self,
                                             base=self.base,
                                             with_form=False,
                                             add_none=True,
                                             on_select=self.onPressFilter)
        self.experimentSelector = ExperimentSelector(
            parent=self,
            base=self.base,
            with_form=False,
            add_none=True,
            on_select=self.onPressFilter)
        self.orderingSelector = OptionsSelector(parent=self,
                                                base=self.base,
                                                options={
                                                    "-time":
                                                    "Time (Descending)",
                                                    "time": "Time (Ascending)"
                                                },
                                                on_select=self.onPressFilter)
        self.tagsField = TextField(initial=" ",
                                   parent=self,
                                   base=self.base,
                                   on_select=self.onPressFilter)

        layout = QFormLayout()
        layout.addRow(QLabel("Sample"), self.sampleSelector)
        layout.addRow(QLabel("Experiment"), self.experimentSelector)
        layout.addRow(QLabel("Tags"), self.tagsField)
        layout.addRow(QLabel("Ordering"), self.orderingSelector)
        self.filterBox.setLayout(layout)

        # Filter Button
        self.filterButton = QPushButton("Filter")
        self.filterButton.clicked.connect(self.onPressFilter)

        # The Sidebar
        sidebar = QWidget()

        sidebarlayout = QVBoxLayout()

        sidebarlayout.addWidget(self.filterBox)
        sidebarlayout.addStretch()
        sidebarlayout.addWidget(self.filterButton)

        sidebar.setLayout(sidebarlayout)

        return sidebar

    def buildList(self):
        list = QListWidget()
        list.setSelectionMode(QAbstractItemView.ExtendedSelection)

        for rep in self.representations:

            item = QListWidgetItem(list)
            list.addItem(item)
            repwidget = RepresentationItemWidget(rep, base=self.base)
            item.setSizeHint(repwidget.minimumSizeHint())
            list.setItemWidget(item, repwidget)

        return list
示例#7
0
class CEditorContent ( QWidget ):

	def __init__ ( self, editor ):
		super ().__init__ ()
		self.editor = editor
		self.toolBarAreaManager = CToolBarAreaManager ( editor )

		self.mainLayout = QVBoxLayout ()
		self.mainLayout.setContentsMargins ( 0, 0, 0, 0 )
		self.mainLayout.setSpacing ( 0 )
		self.contentLayout = QHBoxLayout ()
		self.contentLayout.setContentsMargins ( 0, 0, 0, 0 )
		self.contentLayout.setSpacing ( 0 )

		self.content = QWidget ()
		self.content.setSizePolicy ( QSizePolicy.Expanding, QSizePolicy.Expanding )

		self.editor.signalAdaptiveLayoutChanged.connect ( self.onAdaptiveLayoutChanged )

		self.setLayout ( self.mainLayout )

	def initialize ( self ):
		self.toolBarAreaManager.initialize ()

		self.mainLayout.addWidget ( self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Top ) )
		self.mainLayout.addLayout ( self.contentLayout )
		self.mainLayout.addWidget ( self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Bottom ) )

		self.contentLayout.addWidget ( self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Left ) )
		self.contentLayout.addWidget ( self.content )
		self.contentLayout.addWidget ( self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Right ) )

	def getContent ( self ):
		return self.content

	def setContent ( self, content ):
		if isinstance ( content, QWidget ):
			self.content.setSizePolicy ( QSizePolicy.Expanding, QSizePolicy.Expanding )
			self.contentLayout.replaceWidget ( self.content, content )
			self.content.setObjectName ( "CEditorContent" )
			self.content.deleteLater ()
			self.content = content
		elif isinstance ( content, QLayout ):
			contentLayout = content
			content = QWidget ()
			content.setSizePolicy ( QSizePolicy.Expanding, QSizePolicy.Expanding )
			content.setLayout ( contentLayout )
			content.setObjectName ( "CEditorContent" )
			contentLayout.setContentsMargins ( 0, 0, 0, 0 )
			contentLayout.setSpacing ( 0 )

			self.contentLayout.replaceWidget ( self.content, content )
			self.content.deleteLater ()
			self.content = content

	def customizeToolBar ( self ):
		# TODO: CToolBarCustomizeDialog
		return self.content

	def toggleToolBarLock ( self ):
		return self.toolBarAreaManager.toggleLock ()

	def addExpandingSpacer ( self ):
		return self.toolBarAreaManager.addExpandingSpacer ()

	def addFixedSpacer ( self ):
		return self.toolBarAreaManager.addFixedSpacer ()

	def getMinimumSizeForOrientation ( self, orientation ) -> QSize:
		isDefaultOrientation = orientation == self.editor.GetDefaultOrientation ()
		contentMinSize = self.content.layout ().minimumSize ()

		topArea = self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Top )
		bottomArea = self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Bottom )
		leftArea = self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Left )
		rightArea = self.toolBarAreaManager.getWidget ( CToolBarAreaManagerArea.Right )

		result = QSize ( 0, 0 )
		if isDefaultOrientation:
			# Take width from left and right areas if we're switching to the editor's default orientation
			result.setWidth ( result.width () + leftArea.getLargestItemMinimumSize ().width () )
			result.setWidth ( result.width () + rightArea.getLargestItemMinimumSize ().width () )

			# Use top and bottom area to calculate min height
			result.setHeight ( result.height () + leftArea.getLargestItemMinimumSize ().height () )
			result.setHeight ( result.height () + rightArea.getLargestItemMinimumSize ().height () )

			# Add content min size
			result += contentMinSize

			# Take the area layout size hints into account. Expand the current result with the toolbar area layout's size hint.
			# We use size hint rather than minimum size since toolbar area item's size policy is set to preferred.
			result = result.expandedTo ( QSize ( topArea.layout ().sizeHint ().height (), leftArea.layout ().sizeHint ().width () ) )
			result = result.expandedTo ( QSize ( bottomArea.layout ().sizeHint ().height (), rightArea.layout ().sizeHint ().width () ) )
		else:
			# If we're not switching to the default orientation, then we need to use the top and bottom toolbar areas' width
			# since these areas will be placed at the left and right of the editor content in this case of adaptive layouts
			result.setWidth ( result.width () + topArea.getLargestItemMinimumSize ().width () )
			result.setWidth ( result.width () + bottomArea.getLargestItemMinimumSize ().width () )

			# We must also flip where we get toolbar area min height from
			result.setHeight ( result.height () + leftArea.getLargestItemMinimumSize ().height () )
			result.setHeight ( result.height () + rightArea.getLargestItemMinimumSize ().height () )

			# Add flipped content min size
			result += QSize ( contentMinSize.height (), contentMinSize.width () )

			result = result.expandedTo ( QSize ( leftArea.layout ().sizeHint ().height (), topArea.layout ().sizeHint ().width () ) )
			result = result.expandedTo ( QSize ( rightArea.layout ().sizeHint ().height (), bottomArea.layout ().sizeHint ().width () ) )

		return result


	def onAdaptiveLayoutChanged ( self ):
		isDefaultOrientation = self.editor.GetOrientation () == self.editor.GetDefaultOrientation ()
		self.mainLayout.setDirection ( QBoxLayout.TopToBottom if isDefaultOrientation else QBoxLayout.LeftToRight )
		self.contentLayout.setDirection ( QBoxLayout.LeftToRight if isDefaultOrientation else QBoxLayout.TopToBottom )

	def paintEvent ( self, event ):
		styleOption = QStyleOption ()
		styleOption.initFrom ( self )
		painter = QPainter ( self )
		self.style ().drawPrimitive ( QStyle.PE_Widget, styleOption, painter, self )
示例#8
0
class VidCutter(QWidget):
    def __init__(self, parent):
        super(VidCutter, self).__init__(parent)
        self.parent = parent
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.videoWidget = VideoWidget()
        self.videoService = VideoService(self)

        QFontDatabase.addApplicationFont(
            os.path.join(self.getAppPath(), 'fonts', 'DroidSansMono.ttf'))
        QFontDatabase.addApplicationFont(
            os.path.join(self.getAppPath(), 'fonts', 'HelveticaNeue.ttf'))
        qApp.setFont(QFont('Helvetica Neue', 10))

        self.clipTimes = []
        self.inCut = False
        self.movieFilename = ''
        self.movieLoaded = False
        self.timeformat = 'hh:mm:ss'
        self.finalFilename = ''
        self.totalRuntime = 0

        self.initIcons()
        self.initActions()

        self.toolbar = QToolBar(
            floatable=False,
            movable=False,
            iconSize=QSize(28, 28),
            toolButtonStyle=Qt.ToolButtonTextUnderIcon,
            styleSheet=
            'QToolBar QToolButton { min-width:82px; margin-left:10px; margin-right:10px; font-size:14px; }'
        )
        self.initToolbar()

        self.aboutMenu, self.cliplistMenu = QMenu(), QMenu()
        self.initMenus()

        self.seekSlider = VideoSlider(parent=self,
                                      sliderMoved=self.setPosition)
        self.seekSlider.installEventFilter(self)

        self.initNoVideo()

        self.cliplist = QListWidget(
            sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding),
            contextMenuPolicy=Qt.CustomContextMenu,
            uniformItemSizes=True,
            iconSize=QSize(100, 700),
            dragDropMode=QAbstractItemView.InternalMove,
            alternatingRowColors=True,
            customContextMenuRequested=self.itemMenu,
            styleSheet='QListView::item { margin:10px 5px; }')
        self.cliplist.setFixedWidth(185)
        self.cliplist.model().rowsMoved.connect(self.syncClipList)

        listHeader = QLabel(pixmap=QPixmap(
            os.path.join(self.getAppPath(), 'images', 'clipindex.png'), 'PNG'),
                            alignment=Qt.AlignCenter)
        listHeader.setStyleSheet(
            '''padding:5px; padding-top:8px; border:1px solid #b9b9b9; border-bottom:none;
                                    background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFF,
                                    stop: 0.5 #EAEAEA, stop: 0.6 #EAEAEA stop:1 #FFF);'''
        )

        self.runtimeLabel = QLabel('<div align="right">00:00:00</div>',
                                   textFormat=Qt.RichText)
        self.runtimeLabel.setStyleSheet(
            '''font-family:Droid Sans Mono; font-size:10pt; color:#FFF;
                                           background:rgb(106, 69, 114) url(:images/runtime.png)
                                           no-repeat left center; padding:2px; padding-right:8px;
                                           border:1px solid #b9b9b9; border-top:none;'''
        )

        self.clipindexLayout = QVBoxLayout(spacing=0)
        self.clipindexLayout.setContentsMargins(0, 0, 0, 0)
        self.clipindexLayout.addWidget(listHeader)
        self.clipindexLayout.addWidget(self.cliplist)
        self.clipindexLayout.addWidget(self.runtimeLabel)

        self.videoLayout = QHBoxLayout()
        self.videoLayout.setContentsMargins(0, 0, 0, 0)
        self.videoLayout.addWidget(self.novideoWidget)
        self.videoLayout.addLayout(self.clipindexLayout)

        self.timeCounter = QLabel('00:00:00 / 00:00:00',
                                  autoFillBackground=True,
                                  alignment=Qt.AlignCenter,
                                  sizePolicy=QSizePolicy(
                                      QSizePolicy.Expanding,
                                      QSizePolicy.Fixed))
        self.timeCounter.setStyleSheet(
            'color:#FFF; background:#000; font-family:Droid Sans Mono; font-size:10.5pt; padding:4px;'
        )

        videoplayerLayout = QVBoxLayout(spacing=0)
        videoplayerLayout.setContentsMargins(0, 0, 0, 0)
        videoplayerLayout.addWidget(self.videoWidget)
        videoplayerLayout.addWidget(self.timeCounter)

        self.videoplayerWidget = QWidget(self, visible=False)
        self.videoplayerWidget.setLayout(videoplayerLayout)

        self.menuButton = QPushButton(icon=self.aboutIcon,
                                      flat=True,
                                      toolTip='About',
                                      statusTip='About',
                                      iconSize=QSize(24, 24),
                                      cursor=Qt.PointingHandCursor)
        self.menuButton.setMenu(self.aboutMenu)

        self.muteButton = QPushButton(icon=self.unmuteIcon,
                                      flat=True,
                                      toolTip='Mute',
                                      statusTip='Toggle audio mute',
                                      cursor=Qt.PointingHandCursor,
                                      clicked=self.muteAudio)

        self.volumeSlider = QSlider(Qt.Horizontal,
                                    toolTip='Volume',
                                    statusTip='Adjust volume level',
                                    cursor=Qt.PointingHandCursor,
                                    value=50,
                                    sizePolicy=QSizePolicy(
                                        QSizePolicy.Fixed,
                                        QSizePolicy.Minimum),
                                    minimum=0,
                                    maximum=100,
                                    sliderMoved=self.setVolume)
        self.volumeSlider.setStyleSheet(
            '''QSlider::groove:horizontal { height:40px; }
                                           QSlider::sub-page:horizontal { border:1px outset #6A4572; background:#6A4572; margin:2px; }
                                           QSlider::handle:horizontal { image: url(:images/knob.png) no-repeat top left; width:20px; }'''
        )

        self.saveAction = QPushButton(
            self.parent,
            icon=self.saveIcon,
            text='Save Video',
            flat=True,
            toolTip='Save Video',
            clicked=self.cutVideo,
            cursor=Qt.PointingHandCursor,
            iconSize=QSize(30, 30),
            statusTip='Save video clips merged as a new video file',
            enabled=False)
        self.saveAction.setStyleSheet(
            '''QPushButton { color:#FFF; padding:8px; font-size:12pt;
                                            border:1px inset #481953; border-radius:4px;
                                            background-color:rgb(106, 69, 114); }
                                         QPushButton:!enabled { background-color:rgba(0, 0, 0, 0.1);
                                            color:rgba(0, 0, 0, 0.3); border:1px inset #CDCDCD; }
                                         QPushButton:hover { background-color:rgba(255, 255, 255, 0.8); color:#444; }
                                         QPushButton:pressed { background-color:rgba(218, 218, 219, 0.8); color:#444; }'''
        )

        controlsLayout = QHBoxLayout()
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(self.toolbar)
        controlsLayout.addSpacerItem(QSpacerItem(20, 1))
        controlsLayout.addWidget(self.saveAction)
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(self.muteButton)
        controlsLayout.addWidget(self.volumeSlider)
        controlsLayout.addSpacing(1)
        controlsLayout.addWidget(self.menuButton)

        layout = QVBoxLayout()
        layout.setContentsMargins(10, 10, 10, 4)
        layout.addLayout(self.videoLayout)
        layout.addWidget(self.seekSlider)
        layout.addLayout(controlsLayout)

        self.setLayout(layout)

        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.error.connect(self.handleError)

    def initNoVideo(self) -> None:
        novideoImage = QLabel(alignment=Qt.AlignCenter,
                              autoFillBackground=False,
                              pixmap=QPixmap(
                                  os.path.join(self.getAppPath(), 'images',
                                               'novideo.png'), 'PNG'),
                              sizePolicy=QSizePolicy(
                                  QSizePolicy.Expanding,
                                  QSizePolicy.MinimumExpanding))
        novideoImage.setBackgroundRole(QPalette.Dark)
        novideoImage.setContentsMargins(0, 20, 0, 20)
        self.novideoLabel = QLabel(alignment=Qt.AlignCenter,
                                   autoFillBackground=True,
                                   sizePolicy=QSizePolicy(
                                       QSizePolicy.Expanding,
                                       QSizePolicy.Minimum))
        self.novideoLabel.setBackgroundRole(QPalette.Dark)
        self.novideoLabel.setContentsMargins(0, 20, 15, 60)
        novideoLayout = QVBoxLayout(spacing=0)
        novideoLayout.addWidget(novideoImage)
        novideoLayout.addWidget(self.novideoLabel, alignment=Qt.AlignTop)
        self.novideoMovie = QMovie(
            os.path.join(self.getAppPath(), 'images', 'novideotext.gif'))
        self.novideoMovie.frameChanged.connect(self.setNoVideoText)
        self.novideoMovie.start()
        self.novideoWidget = QWidget(self, autoFillBackground=True)
        self.novideoWidget.setBackgroundRole(QPalette.Dark)
        self.novideoWidget.setLayout(novideoLayout)

    def initIcons(self) -> None:
        self.appIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'vidcutter.png'))
        self.openIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'addmedia.png'))
        self.playIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'play.png'))
        self.pauseIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'pause.png'))
        self.cutStartIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'cut-start.png'))
        self.cutEndIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'cut-end.png'))
        self.saveIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'save.png'))
        self.muteIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'muted.png'))
        self.unmuteIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'unmuted.png'))
        self.upIcon = QIcon(os.path.join(self.getAppPath(), 'images',
                                         'up.png'))
        self.downIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'down.png'))
        self.removeIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'remove.png'))
        self.removeAllIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'remove-all.png'))
        self.successIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'success.png'))
        self.aboutIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'about.png'))
        self.completePlayIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-play.png'))
        self.completeOpenIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-open.png'))
        self.completeRestartIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-restart.png'))
        self.completeExitIcon = QIcon(
            os.path.join(self.getAppPath(), 'images', 'complete-exit.png'))

    def initActions(self) -> None:
        self.openAction = QAction(self.openIcon,
                                  'Add Media',
                                  self,
                                  statusTip='Select media source',
                                  triggered=self.openFile)
        self.playAction = QAction(self.playIcon,
                                  'Play Video',
                                  self,
                                  statusTip='Play selected media',
                                  triggered=self.playVideo,
                                  enabled=False)
        self.cutStartAction = QAction(self.cutStartIcon,
                                      'Set Start',
                                      self,
                                      toolTip='Set Start',
                                      statusTip='Set start marker',
                                      triggered=self.cutStart,
                                      enabled=False)
        self.cutEndAction = QAction(self.cutEndIcon,
                                    'Set End',
                                    self,
                                    statusTip='Set end marker',
                                    triggered=self.cutEnd,
                                    enabled=False)
        self.moveItemUpAction = QAction(
            self.upIcon,
            'Move Up',
            self,
            statusTip='Move clip position up in list',
            triggered=self.moveItemUp,
            enabled=False)
        self.moveItemDownAction = QAction(
            self.downIcon,
            'Move Down',
            self,
            statusTip='Move clip position down in list',
            triggered=self.moveItemDown,
            enabled=False)
        self.removeItemAction = QAction(
            self.removeIcon,
            'Remove clip',
            self,
            statusTip='Remove selected clip from list',
            triggered=self.removeItem,
            enabled=False)
        self.removeAllAction = QAction(self.removeAllIcon,
                                       'Clear list',
                                       self,
                                       statusTip='Clear all clips from list',
                                       triggered=self.clearList,
                                       enabled=False)
        self.aboutAction = QAction('About %s' % qApp.applicationName(),
                                   self,
                                   statusTip='Credits and acknowledgements',
                                   triggered=self.aboutInfo)
        self.aboutQtAction = QAction('About Qt',
                                     self,
                                     statusTip='About Qt',
                                     triggered=qApp.aboutQt)
        self.mediaInfoAction = QAction(
            'Media Information',
            self,
            statusTip='Media information from loaded video file',
            triggered=self.mediaInfo,
            enabled=False)

    def initToolbar(self) -> None:
        self.toolbar.addAction(self.openAction)
        self.toolbar.addAction(self.playAction)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.cutStartAction)
        self.toolbar.addAction(self.cutEndAction)
        self.toolbar.addSeparator()

    def initMenus(self) -> None:
        self.aboutMenu.addAction(self.mediaInfoAction)
        self.aboutMenu.addSeparator()
        self.aboutMenu.addAction(self.aboutQtAction)
        self.aboutMenu.addAction(self.aboutAction)
        self.cliplistMenu.addAction(self.moveItemUpAction)
        self.cliplistMenu.addAction(self.moveItemDownAction)
        self.cliplistMenu.addSeparator()
        self.cliplistMenu.addAction(self.removeItemAction)
        self.cliplistMenu.addAction(self.removeAllAction)

    def setRunningTime(self, runtime: str) -> None:
        self.runtimeLabel.setText('<div align="right">%s</div>' % runtime)

    @pyqtSlot(int)
    def setNoVideoText(self, frame: int) -> None:
        self.novideoLabel.setPixmap(self.novideoMovie.currentPixmap())

    def itemMenu(self, pos: QPoint) -> None:
        globalPos = self.cliplist.mapToGlobal(pos)
        self.moveItemUpAction.setEnabled(False)
        self.moveItemDownAction.setEnabled(False)
        self.removeItemAction.setEnabled(False)
        self.removeAllAction.setEnabled(False)
        index = self.cliplist.currentRow()
        if index != -1:
            if not self.inCut:
                if index > 0:
                    self.moveItemUpAction.setEnabled(True)
                if index < self.cliplist.count() - 1:
                    self.moveItemDownAction.setEnabled(True)
            if self.cliplist.count() > 0:
                self.removeItemAction.setEnabled(True)
        if self.cliplist.count() > 0:
            self.removeAllAction.setEnabled(True)
        self.cliplistMenu.exec_(globalPos)

    def moveItemUp(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index - 1, tmpItem)
        self.renderTimes()

    def moveItemDown(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index + 1, tmpItem)
        self.renderTimes()

    def removeItem(self) -> None:
        index = self.cliplist.currentRow()
        del self.clipTimes[index]
        if self.inCut and index == self.cliplist.count() - 1:
            self.inCut = False
            self.initMediaControls()
        self.renderTimes()

    def clearList(self) -> None:
        self.clipTimes.clear()
        self.cliplist.clear()
        self.inCut = False
        self.renderTimes()
        self.initMediaControls()

    def mediaInfo(self) -> None:
        if self.mediaPlayer.isMetaDataAvailable():
            content = '<table cellpadding="4">'
            for key in self.mediaPlayer.availableMetaData():
                val = self.mediaPlayer.metaData(key)
                if type(val) is QSize:
                    val = '%s x %s' % (val.width(), val.height())
                content += '<tr><td align="right"><b>%s:</b></td><td>%s</td></tr>\n' % (
                    key, val)
            content += '</table>'
            mbox = QMessageBox(windowTitle='Media Information',
                               windowIcon=self.parent.windowIcon(),
                               textFormat=Qt.RichText)
            mbox.setText('<b>%s</b>' % os.path.basename(
                self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()))
            mbox.setInformativeText(content)
            mbox.exec_()
        else:
            QMessageBox.critical(
                self.parent, 'Could not retrieve media information',
                '''There was a problem in tring to retrieve media information.
                                    This DOES NOT mean there is a problem with the file and you should
                                    be able to continue using it.''')

    def aboutInfo(self) -> None:
        about_html = '''<style>
    a { color:#441d4e; text-decoration:none; font-weight:bold; }
    a:hover { text-decoration:underline; }
</style>
<p style="font-size:26pt; font-weight:bold;">%s</p>
<p>
    <span style="font-size:13pt;"><b>Version: %s</b></span>
    <span style="font-size:10pt;position:relative;left:5px;">( %s )</span>
</p>
<p style="font-size:13px;">
    Copyright &copy; 2016 <a href="mailto:[email protected]">Pete Alexandrou</a>
    <br/>
    Website: <a href="%s">%s</a>
</p>
<p style="font-size:13px;">
    Thanks to the folks behind the <b>Qt</b>, <b>PyQt</b> and <b>FFmpeg</b>
    projects for all their hard and much appreciated work.
</p>
<p style="font-size:11px;">
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.
</p>
<p style="font-size:11px;">
    This software uses libraries from the <a href="https://www.ffmpeg.org">FFmpeg</a> project under the
    <a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html">LGPLv2.1</a>
</p>''' % (qApp.applicationName(), qApp.applicationVersion(),
           platform.architecture()[0], qApp.organizationDomain(),
           qApp.organizationDomain())
        QMessageBox.about(self.parent, 'About %s' % qApp.applicationName(),
                          about_html)

    def openFile(self) -> None:
        filename, _ = QFileDialog.getOpenFileName(self.parent,
                                                  caption='Select video',
                                                  directory=QDir.homePath())
        if filename != '':
            self.loadFile(filename)

    def loadFile(self, filename: str) -> None:
        self.movieFilename = filename
        if not os.path.exists(filename):
            return
        self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
        self.initMediaControls(True)
        self.cliplist.clear()
        self.clipTimes = []
        self.parent.setWindowTitle(
            '%s - %s' % (qApp.applicationName(), os.path.basename(filename)))
        if not self.movieLoaded:
            self.videoLayout.replaceWidget(self.novideoWidget,
                                           self.videoplayerWidget)
            self.novideoMovie.stop()
            self.novideoMovie.deleteLater()
            self.novideoWidget.deleteLater()
            self.videoplayerWidget.show()
            self.videoWidget.show()
            self.movieLoaded = True
        if self.mediaPlayer.isVideoAvailable():
            self.mediaPlayer.setPosition(1)
        self.mediaPlayer.play()
        self.mediaPlayer.pause()

    def playVideo(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
            self.playAction.setText('Play Video')
        else:
            self.mediaPlayer.play()
            self.playAction.setText('Pause Video')

    def initMediaControls(self, flag: bool = True) -> None:
        self.playAction.setEnabled(flag)
        self.saveAction.setEnabled(False)
        self.cutStartAction.setEnabled(flag)
        self.cutEndAction.setEnabled(False)
        self.mediaInfoAction.setEnabled(flag)
        if flag:
            self.seekSlider.setRestrictValue(0)

    def setPosition(self, position: int) -> None:
        self.mediaPlayer.setPosition(position)

    def positionChanged(self, progress: int) -> None:
        self.seekSlider.setValue(progress)
        currentTime = self.deltaToQTime(progress)
        totalTime = self.deltaToQTime(self.mediaPlayer.duration())
        self.timeCounter.setText('%s / %s' % (currentTime.toString(
            self.timeformat), totalTime.toString(self.timeformat)))

    @pyqtSlot()
    def mediaStateChanged(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playAction.setIcon(self.pauseIcon)
        else:
            self.playAction.setIcon(self.playIcon)

    def durationChanged(self, duration: int) -> None:
        self.seekSlider.setRange(0, duration)

    def muteAudio(self, muted: bool) -> None:
        if self.mediaPlayer.isMuted():
            self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted())
            self.muteButton.setIcon(self.unmuteIcon)
            self.muteButton.setToolTip('Mute')
        else:
            self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted())
            self.muteButton.setIcon(self.muteIcon)
            self.muteButton.setToolTip('Unmute')

    def setVolume(self, volume: int) -> None:
        self.mediaPlayer.setVolume(volume)

    def toggleFullscreen(self) -> None:
        self.videoWidget.setFullScreen(not self.videoWidget.isFullScreen())

    def cutStart(self) -> None:
        self.clipTimes.append([
            self.deltaToQTime(self.mediaPlayer.position()), '',
            self.captureImage()
        ])
        self.cutStartAction.setDisabled(True)
        self.cutEndAction.setEnabled(True)
        self.seekSlider.setRestrictValue(self.seekSlider.value() + 1000)
        self.mediaPlayer.setPosition(self.seekSlider.restrictValue)
        self.inCut = True
        self.renderTimes()

    def cutEnd(self) -> None:
        item = self.clipTimes[len(self.clipTimes) - 1]
        selected = self.deltaToQTime(self.mediaPlayer.position())
        if selected.__lt__(item[0]):
            QMessageBox.critical(
                self.parent, 'Invalid END Time',
                'The clip end time must come AFTER it\'s start time. Please try again.'
            )
            return
        item[1] = selected
        self.cutStartAction.setEnabled(True)
        self.cutEndAction.setDisabled(True)
        self.seekSlider.setRestrictValue(0)
        self.inCut = False
        self.renderTimes()

    @pyqtSlot(QModelIndex, int, int, QModelIndex, int)
    def syncClipList(self, parent: QModelIndex, start: int, end: int,
                     destination: QModelIndex, row: int) -> None:
        if start < row:
            index = row - 1
        else:
            index = row
        clip = self.clipTimes.pop(start)
        self.clipTimes.insert(index, clip)

    def renderTimes(self) -> None:
        self.cliplist.clear()
        self.seekSlider.setCutMode(self.inCut)
        if len(self.clipTimes) > 4:
            self.cliplist.setFixedWidth(200)
        else:
            self.cliplist.setFixedWidth(185)
        self.totalRuntime = 0
        for item in self.clipTimes:
            endItem = ''
            if type(item[1]) is QTime:
                endItem = item[1].toString(self.timeformat)
                self.totalRuntime += item[0].msecsTo(item[1])
            listitem = QListWidgetItem()
            listitem.setTextAlignment(Qt.AlignVCenter)
            if type(item[2]) is QPixmap:
                listitem.setIcon(QIcon(item[2]))
            self.cliplist.addItem(listitem)
            marker = QLabel(
                '''<style>b { font-size:8pt; } p { margin:5px; }</style>
                            <p><b>START</b><br/>%s</p><p><b>END</b><br/>%s</p>'''
                % (item[0].toString(self.timeformat), endItem))
            self.cliplist.setItemWidget(listitem, marker)
            listitem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled
                              | Qt.ItemIsEnabled)
        if len(self.clipTimes) and not self.inCut:
            self.saveAction.setEnabled(True)
        if self.inCut or len(self.clipTimes) == 0 or not type(
                self.clipTimes[0][1]) is QTime:
            self.saveAction.setEnabled(False)
        self.setRunningTime(
            self.deltaToQTime(self.totalRuntime).toString(self.timeformat))

    @staticmethod
    def deltaToQTime(millisecs: int) -> QTime:
        secs = millisecs / 1000
        return QTime((secs / 3600) % 60, (secs / 60) % 60, secs % 60,
                     (secs * 1000) % 1000)

    def captureImage(self) -> None:
        frametime = self.deltaToQTime(
            self.mediaPlayer.position()).addSecs(1).toString(self.timeformat)
        inputfile = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile(
        )
        imagecap = self.videoService.capture(inputfile, frametime)
        if type(imagecap) is QPixmap:
            return imagecap

    def cutVideo(self) -> bool:
        self.setCursor(Qt.BusyCursor)
        clips = len(self.clipTimes)
        filename, filelist = '', []
        source = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()
        _, sourceext = os.path.splitext(source)
        if clips > 0:
            self.finalFilename, _ = QFileDialog.getSaveFileName(
                self.parent, 'Save video', source,
                'Video files (*%s)' % sourceext)
            if self.finalFilename != '':
                self.saveAction.setDisabled(True)
                self.showProgress(clips)
                file, ext = os.path.splitext(self.finalFilename)
                index = 1
                self.progress.setLabelText('Cutting video clips...')
                qApp.processEvents()
                for clip in self.clipTimes:
                    duration = self.deltaToQTime(clip[0].msecsTo(
                        clip[1])).toString(self.timeformat)
                    filename = '%s_%s%s' % (file, '{0:0>2}'.format(index), ext)
                    filelist.append(filename)
                    self.videoService.cut(source, filename,
                                          clip[0].toString(self.timeformat),
                                          duration)
                    index += 1
                if len(filelist) > 1:
                    self.joinVideos(filelist, self.finalFilename)
                else:
                    QFile.remove(self.finalFilename)
                    QFile.rename(filename, self.finalFilename)
                self.unsetCursor()
                self.progress.setLabelText('Complete...')
                qApp.processEvents()
                self.saveAction.setEnabled(True)
                self.progress.close()
                self.progress.deleteLater()
                self.complete()
                self.saveAction.setEnabled(True)
            self.unsetCursor()
            self.saveAction.setDisabled(True)
            return True
        self.unsetCursor()
        self.saveAction.setDisabled(True)
        return False

    def joinVideos(self, joinlist: list, filename: str) -> None:
        listfile = os.path.normpath(
            os.path.join(os.path.dirname(joinlist[0]), '.vidcutter.list'))
        fobj = open(listfile, 'w')
        for file in joinlist:
            fobj.write('file \'%s\'\n' % file.replace("'", "\\'"))
        fobj.close()
        self.videoService.join(listfile, filename)
        try:
            QFile.remove(listfile)
            for file in joinlist:
                if os.path.isfile(file):
                    QFile.remove(file)
        except:
            pass

    def showProgress(self,
                     steps: int,
                     label: str = 'Processing video...') -> None:
        self.progress = QProgressDialog(label,
                                        None,
                                        0,
                                        steps,
                                        self.parent,
                                        windowModality=Qt.ApplicationModal,
                                        windowIcon=self.parent.windowIcon(),
                                        minimumDuration=0,
                                        minimumWidth=500)
        self.progress.show()
        for i in range(steps):
            self.progress.setValue(i)
            qApp.processEvents()
            time.sleep(1)

    def complete(self) -> None:
        info = QFileInfo(self.finalFilename)
        mbox = QMessageBox(windowTitle='Success',
                           windowIcon=self.parent.windowIcon(),
                           minimumWidth=500,
                           iconPixmap=self.successIcon.pixmap(48, 49),
                           textFormat=Qt.RichText)
        mbox.setText(
            '''
<style>
    table.info { margin:8px; padding:4px 15px; }
    td.label { font-weight:bold; font-size:9pt; text-align:right; background-color:#444; color:#FFF; }
    td.value { background-color:#FFF !important; font-size:10pt; }
</style>
<p>Your video was successfully created.</p>
<p align="center">
    <table class="info" cellpadding="6" cellspacing="0">
        <tr>
            <td class="label"><b>Filename</b></td>
            <td class="value" nowrap>%s</td>
        </tr>
        <tr>
            <td class="label"><b>Size</b></td>
            <td class="value">%s</td>
        </tr>
        <tr>
            <td class="label"><b>Runtime</b></td>
            <td class="value">%s</td>
        </tr>
    </table>
</p>
<p>How would you like to proceed?</p>''' %
            (QDir.toNativeSeparators(
                self.finalFilename), self.sizeof_fmt(int(info.size())),
             self.deltaToQTime(self.totalRuntime).toString(self.timeformat)))
        play = mbox.addButton('Play', QMessageBox.AcceptRole)
        play.setIcon(self.completePlayIcon)
        play.clicked.connect(self.openResult)
        fileman = mbox.addButton('Open', QMessageBox.AcceptRole)
        fileman.setIcon(self.completeOpenIcon)
        fileman.clicked.connect(self.openFolder)
        end = mbox.addButton('Exit', QMessageBox.AcceptRole)
        end.setIcon(self.completeExitIcon)
        end.clicked.connect(self.close)
        new = mbox.addButton('Restart', QMessageBox.AcceptRole)
        new.setIcon(self.completeRestartIcon)
        new.clicked.connect(self.startNew)
        mbox.setDefaultButton(new)
        mbox.setEscapeButton(new)
        mbox.exec_()

    def sizeof_fmt(self, num: float, suffix: chr = 'B') -> str:
        for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
            if abs(num) < 1024.0:
                return "%3.1f%s%s" % (num, unit, suffix)
            num /= 1024.0
        return "%.1f%s%s" % (num, 'Y', suffix)

    @pyqtSlot()
    def openFolder(self) -> None:
        self.openResult(pathonly=True)

    @pyqtSlot(bool)
    def openResult(self, pathonly: bool = False) -> None:
        self.startNew()
        if len(self.finalFilename) and os.path.exists(self.finalFilename):
            target = self.finalFilename if not pathonly else os.path.dirname(
                self.finalFilename)
            QDesktopServices.openUrl(QUrl.fromLocalFile(target))

    @pyqtSlot()
    def startNew(self) -> None:
        self.unsetCursor()
        self.clearList()
        self.seekSlider.setValue(0)
        self.seekSlider.setRange(0, 0)
        self.mediaPlayer.setMedia(QMediaContent())
        self.initNoVideo()
        self.videoLayout.replaceWidget(self.videoplayerWidget,
                                       self.novideoWidget)
        self.initMediaControls(False)
        self.parent.setWindowTitle('%s' % qApp.applicationName())

    def wheelEvent(self, event: QWheelEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            if event.angleDelta().y() > 0:
                newval = self.seekSlider.value() - 1000
            else:
                newval = self.seekSlider.value() + 1000
            self.seekSlider.setValue(newval)
            self.seekSlider.setSliderPosition(newval)
            self.mediaPlayer.setPosition(newval)
        event.accept()

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            addtime = 0
            if event.key() == Qt.Key_Left:
                addtime = -1000
            elif event.key() == Qt.Key_PageUp or event.key() == Qt.Key_Up:
                addtime = -10000
            elif event.key() == Qt.Key_Right:
                addtime = 1000
            elif event.key() == Qt.Key_PageDown or event.key() == Qt.Key_Down:
                addtime = 10000
            elif event.key() == Qt.Key_Enter:
                self.toggleFullscreen()
            elif event.key(
            ) == Qt.Key_Escape and self.videoWidget.isFullScreen():
                self.videoWidget.setFullScreen(False)
            if addtime != 0:
                newval = self.seekSlider.value() + addtime
                self.seekSlider.setValue(newval)
                self.seekSlider.setSliderPosition(newval)
                self.mediaPlayer.setPosition(newval)
        event.accept()

    def mousePressEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.BackButton and self.cutStartAction.isEnabled():
            self.cutStart()
            event.accept()
        elif event.button(
        ) == Qt.ForwardButton and self.cutEndAction.isEnabled():
            self.cutEnd()
            event.accept()
        else:
            super(VidCutter, self).mousePressEvent(event)

    def eventFilter(self, obj: QObject, event: QEvent) -> bool:
        if event.type() == QEvent.MouseButtonRelease and isinstance(
                obj, VideoSlider):
            if obj.objectName() == 'VideoSlider' and (
                    self.mediaPlayer.isVideoAvailable()
                    or self.mediaPlayer.isAudioAvailable()):
                obj.setValue(
                    QStyle.sliderValueFromPosition(obj.minimum(),
                                                   obj.maximum(), event.x(),
                                                   obj.width()))
                self.mediaPlayer.setPosition(obj.sliderPosition())
        return QWidget.eventFilter(self, obj, event)

    @pyqtSlot(QMediaPlayer.Error)
    def handleError(self, error: QMediaPlayer.Error) -> None:
        self.unsetCursor()
        self.startNew()
        if error == QMediaPlayer.ResourceError:
            QMessageBox.critical(
                self.parent, 'Error',
                'Invalid media file detected at:<br/><br/><b>%s</b><br/><br/>%s'
                % (self.movieFilename, self.mediaPlayer.errorString()))
        else:
            QMessageBox.critical(self.parent, 'Error',
                                 self.mediaPlayer.errorString())

    def getAppPath(self) -> str:
        return ':'

    def closeEvent(self, event: QCloseEvent) -> None:
        self.parent.closeEvent(event)
示例#9
0
class VidCutter(QWidget):
    def __init__(self, parent):
        super(VidCutter, self).__init__(parent)
        self.novideoWidget = QWidget(self, autoFillBackground=True)
        self.parent = parent
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.videoWidget = VideoWidget(self)
        self.videoService = VideoService(self)

        QFontDatabase.addApplicationFont(
            MainWindow.get_path('fonts/DroidSansMono.ttf'))
        QFontDatabase.addApplicationFont(
            MainWindow.get_path('fonts/OpenSans.ttf'))

        fontSize = 12 if sys.platform == 'darwin' else 10
        appFont = QFont('Open Sans', fontSize, 300)
        qApp.setFont(appFont)

        self.clipTimes = []
        self.inCut = False
        self.movieFilename = ''
        self.movieLoaded = False
        self.timeformat = 'hh:mm:ss'
        self.finalFilename = ''
        self.totalRuntime = 0

        self.initIcons()
        self.initActions()

        self.toolbar = QToolBar(floatable=False,
                                movable=False,
                                iconSize=QSize(40, 36))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.toolbar.setStyleSheet('''QToolBar { spacing:10px; }
            QToolBar QToolButton { border:1px solid transparent; min-width:95px; font-size:11pt; font-weight:400;
                border-radius:5px; padding:1px 2px; color:#444; }
            QToolBar QToolButton:hover { border:1px inset #6A4572; color:#6A4572; background-color:rgba(255, 255, 255, 0.85); }
            QToolBar QToolButton:pressed { border:1px inset #6A4572; color:#6A4572; background-color:rgba(255, 255, 255, 0.25); }
            QToolBar QToolButton:disabled { color:#999; }''')
        self.initToolbar()

        self.appMenu, self.cliplistMenu = QMenu(), QMenu()
        self.initMenus()

        self.seekSlider = VideoSlider(parent=self,
                                      sliderMoved=self.setPosition)

        self.initNoVideo()

        self.cliplist = QListWidget(
            sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding),
            contextMenuPolicy=Qt.CustomContextMenu,
            uniformItemSizes=True,
            iconSize=QSize(100, 700),
            dragDropMode=QAbstractItemView.InternalMove,
            alternatingRowColors=True,
            customContextMenuRequested=self.itemMenu,
            dragEnabled=True)
        self.cliplist.setStyleSheet(
            'QListView { border-radius:0; border:none; border-left:1px solid #B9B9B9; '
            +
            'border-right:1px solid #B9B9B9; } QListView::item { padding:10px 0; }'
        )
        self.cliplist.setFixedWidth(185)
        self.cliplist.model().rowsMoved.connect(self.syncClipList)

        listHeader = QLabel(pixmap=QPixmap(
            MainWindow.get_path('images/clipindex.png'), 'PNG'),
                            alignment=Qt.AlignCenter)
        listHeader.setStyleSheet(
            '''padding:5px; padding-top:8px; border:1px solid #b9b9b9;
                                    background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFF,
                                    stop: 0.5 #EAEAEA, stop: 0.6 #EAEAEA stop:1 #FFF);'''
        )

        self.runtimeLabel = QLabel('<div align="right">00:00:00</div>',
                                   textFormat=Qt.RichText)
        self.runtimeLabel.setStyleSheet(
            '''font-family:Droid Sans Mono; font-size:10pt; color:#FFF;
                                           background:rgb(106, 69, 114) url(:images/runtime.png)
                                           no-repeat left center; padding:2px; padding-right:8px;
                                           border:1px solid #B9B9B9;''')

        self.clipindexLayout = QVBoxLayout(spacing=0)
        self.clipindexLayout.setContentsMargins(0, 0, 0, 0)
        self.clipindexLayout.addWidget(listHeader)
        self.clipindexLayout.addWidget(self.cliplist)
        self.clipindexLayout.addWidget(self.runtimeLabel)

        self.videoLayout = QHBoxLayout()
        self.videoLayout.setContentsMargins(0, 0, 0, 0)
        self.videoLayout.addWidget(self.novideoWidget)
        self.videoLayout.addLayout(self.clipindexLayout)

        self.timeCounter = QLabel('00:00:00 / 00:00:00',
                                  autoFillBackground=True,
                                  alignment=Qt.AlignCenter,
                                  sizePolicy=QSizePolicy(
                                      QSizePolicy.Expanding,
                                      QSizePolicy.Fixed))
        self.timeCounter.setStyleSheet(
            'color:#FFF; background:#000; font-family:Droid Sans Mono; font-size:10.5pt; padding:4px;'
        )

        videoplayerLayout = QVBoxLayout(spacing=0)
        videoplayerLayout.setContentsMargins(0, 0, 0, 0)
        videoplayerLayout.addWidget(self.videoWidget)
        videoplayerLayout.addWidget(self.timeCounter)

        self.videoplayerWidget = QWidget(self, visible=False)
        self.videoplayerWidget.setLayout(videoplayerLayout)

        self.muteButton = QPushButton(icon=self.unmuteIcon,
                                      flat=True,
                                      toolTip='Mute',
                                      statusTip='Toggle audio mute',
                                      iconSize=QSize(16, 16),
                                      cursor=Qt.PointingHandCursor,
                                      clicked=self.muteAudio)

        self.volumeSlider = QSlider(Qt.Horizontal,
                                    toolTip='Volume',
                                    statusTip='Adjust volume level',
                                    cursor=Qt.PointingHandCursor,
                                    value=50,
                                    minimum=0,
                                    maximum=100,
                                    sliderMoved=self.setVolume)

        self.menuButton = QPushButton(
            icon=self.menuIcon,
            flat=True,
            toolTip='Menu',
            statusTip='Media + application information',
            iconSize=QSize(24, 24),
            cursor=Qt.PointingHandCursor)
        self.menuButton.setMenu(self.appMenu)

        toolbarLayout = QHBoxLayout()
        toolbarLayout.addWidget(self.toolbar)
        toolbarLayout.setContentsMargins(2, 2, 2, 2)

        toolbarGroup = QGroupBox()
        toolbarGroup.setFlat(False)
        toolbarGroup.setCursor(Qt.PointingHandCursor)
        toolbarGroup.setLayout(toolbarLayout)

        toolbarGroup.setStyleSheet(
            '''QGroupBox { background-color:rgba(0, 0, 0, 0.1);
            border:1px inset #888; border-radius:5px; }''')

        controlsLayout = QHBoxLayout(spacing=0)
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(toolbarGroup)
        controlsLayout.addStretch(1)
        controlsLayout.addWidget(self.muteButton)
        controlsLayout.addWidget(self.volumeSlider)
        controlsLayout.addSpacing(1)
        controlsLayout.addWidget(self.menuButton)

        layout = QVBoxLayout()
        layout.setContentsMargins(10, 10, 10, 4)
        layout.addLayout(self.videoLayout)
        layout.addWidget(self.seekSlider)
        layout.addSpacing(5)
        layout.addLayout(controlsLayout)
        layout.addSpacing(2)

        self.setLayout(layout)

        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.error.connect(self.handleError)

    def initNoVideo(self) -> None:
        novideoImage = QLabel(
            alignment=Qt.AlignCenter,
            autoFillBackground=False,
            pixmap=QPixmap(MainWindow.get_path('images/novideo.png'), 'PNG'),
            sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.MinimumExpanding))
        novideoImage.setBackgroundRole(QPalette.Dark)
        novideoImage.setContentsMargins(0, 20, 0, 20)
        self.novideoLabel = QLabel(alignment=Qt.AlignCenter,
                                   autoFillBackground=True,
                                   sizePolicy=QSizePolicy(
                                       QSizePolicy.Expanding,
                                       QSizePolicy.Minimum))
        self.novideoLabel.setBackgroundRole(QPalette.Dark)
        self.novideoLabel.setContentsMargins(0, 20, 15, 60)
        novideoLayout = QVBoxLayout(spacing=0)
        novideoLayout.addWidget(novideoImage)
        novideoLayout.addWidget(self.novideoLabel, alignment=Qt.AlignTop)
        self.novideoMovie = QMovie(
            MainWindow.get_path('images/novideotext.gif'))
        self.novideoMovie.frameChanged.connect(self.setNoVideoText)
        self.novideoMovie.start()
        self.novideoWidget.setBackgroundRole(QPalette.Dark)
        self.novideoWidget.setLayout(novideoLayout)

    def initIcons(self) -> None:
        self.appIcon = QIcon(MainWindow.get_path('images/vidcutter.png'))
        self.openIcon = icon('fa.film',
                             color='#444',
                             color_active='#6A4572',
                             scale_factor=0.9)
        self.playIcon = icon('fa.play-circle-o',
                             color='#444',
                             color_active='#6A4572',
                             scale_factor=1.1)
        self.pauseIcon = icon('fa.pause-circle-o',
                              color='#444',
                              color_active='#6A4572',
                              scale_factor=1.1)
        self.cutStartIcon = icon('fa.scissors',
                                 scale_factor=1.15,
                                 color='#444',
                                 color_active='#6A4572')
        endicon_normal = icon('fa.scissors', scale_factor=1.15,
                              color='#444').pixmap(QSize(36, 36)).toImage()
        endicon_active = icon('fa.scissors',
                              scale_factor=1.15,
                              color='#6A4572').pixmap(QSize(36, 36)).toImage()
        self.cutEndIcon = QIcon()
        self.cutEndIcon.addPixmap(
            QPixmap.fromImage(
                endicon_normal.mirrored(horizontal=True, vertical=False)),
            QIcon.Normal, QIcon.Off)
        self.cutEndIcon.addPixmap(
            QPixmap.fromImage(
                endicon_active.mirrored(horizontal=True, vertical=False)),
            QIcon.Active, QIcon.Off)
        self.saveIcon = icon('fa.video-camera',
                             color='#6A4572',
                             color_active='#6A4572')
        self.muteIcon = QIcon(MainWindow.get_path('images/muted.png'))
        self.unmuteIcon = QIcon(MainWindow.get_path('images/unmuted.png'))
        self.upIcon = icon('ei.caret-up', color='#444')
        self.downIcon = icon('ei.caret-down', color='#444')
        self.removeIcon = icon('ei.remove', color='#B41D1D')
        self.removeAllIcon = icon('ei.trash', color='#B41D1D')
        self.successIcon = QIcon(MainWindow.get_path('images/success.png'))
        self.menuIcon = icon('fa.cog', color='#444', scale_factor=1.15)
        self.completePlayIcon = icon('fa.play', color='#444')
        self.completeOpenIcon = icon('fa.folder-open', color='#444')
        self.completeRestartIcon = icon('fa.retweet', color='#444')
        self.completeExitIcon = icon('fa.sign-out', color='#444')
        self.mediaInfoIcon = icon('fa.info-circle', color='#444')
        self.updateCheckIcon = icon('fa.cloud-download', color='#444')

    def initActions(self) -> None:
        self.openAction = QAction(self.openIcon,
                                  'Open',
                                  self,
                                  statusTip='Open media file',
                                  triggered=self.openMedia)
        self.playAction = QAction(self.playIcon,
                                  'Play',
                                  self,
                                  statusTip='Play media file',
                                  triggered=self.playMedia,
                                  enabled=False)
        self.cutStartAction = QAction(self.cutStartIcon,
                                      ' Start',
                                      self,
                                      toolTip='Start',
                                      statusTip='Set clip start marker',
                                      triggered=self.setCutStart,
                                      enabled=False)
        self.cutEndAction = QAction(self.cutEndIcon,
                                    ' End',
                                    self,
                                    toolTip='End',
                                    statusTip='Set clip end marker',
                                    triggered=self.setCutEnd,
                                    enabled=False)
        self.saveAction = QAction(self.saveIcon,
                                  'Save',
                                  self,
                                  statusTip='Save clips to a new video file',
                                  triggered=self.cutVideo,
                                  enabled=False)
        self.moveItemUpAction = QAction(
            self.upIcon,
            'Move up',
            self,
            statusTip='Move clip position up in list',
            triggered=self.moveItemUp,
            enabled=False)
        self.moveItemDownAction = QAction(
            self.downIcon,
            'Move down',
            self,
            statusTip='Move clip position down in list',
            triggered=self.moveItemDown,
            enabled=False)
        self.removeItemAction = QAction(
            self.removeIcon,
            'Remove clip',
            self,
            statusTip='Remove selected clip from list',
            triggered=self.removeItem,
            enabled=False)
        self.removeAllAction = QAction(self.removeAllIcon,
                                       'Clear list',
                                       self,
                                       statusTip='Clear all clips from list',
                                       triggered=self.clearList,
                                       enabled=False)
        self.mediaInfoAction = QAction(
            self.mediaInfoIcon,
            'Media information',
            self,
            statusTip='View current media file information',
            triggered=self.mediaInfo,
            enabled=False)
        self.updateCheckAction = QAction(
            self.updateCheckIcon,
            'Check for updates...',
            self,
            statusTip='Check for application updates',
            triggered=self.updateCheck)
        self.aboutQtAction = QAction('About Qt',
                                     self,
                                     statusTip='About Qt',
                                     triggered=qApp.aboutQt)
        self.aboutAction = QAction('About %s' % qApp.applicationName(),
                                   self,
                                   statusTip='Credits and licensing',
                                   triggered=self.aboutInfo)

    def initToolbar(self) -> None:
        self.toolbar.addAction(self.openAction)
        self.toolbar.addAction(self.playAction)
        self.toolbar.addAction(self.cutStartAction)
        self.toolbar.addAction(self.cutEndAction)
        self.toolbar.addAction(self.saveAction)

    def initMenus(self) -> None:
        self.appMenu.addAction(self.mediaInfoAction)
        self.appMenu.addAction(self.updateCheckAction)
        self.appMenu.addSeparator()
        self.appMenu.addAction(self.aboutQtAction)
        self.appMenu.addAction(self.aboutAction)

        self.cliplistMenu.addAction(self.moveItemUpAction)
        self.cliplistMenu.addAction(self.moveItemDownAction)
        self.cliplistMenu.addSeparator()
        self.cliplistMenu.addAction(self.removeItemAction)
        self.cliplistMenu.addAction(self.removeAllAction)

    @staticmethod
    def getSpacer() -> QWidget:
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        return spacer

    def setRunningTime(self, runtime: str) -> None:
        self.runtimeLabel.setText('<div align="right">%s</div>' % runtime)

    @pyqtSlot(int)
    def setNoVideoText(self) -> None:
        self.novideoLabel.setPixmap(self.novideoMovie.currentPixmap())

    def itemMenu(self, pos: QPoint) -> None:
        globalPos = self.cliplist.mapToGlobal(pos)
        self.moveItemUpAction.setEnabled(False)
        self.moveItemDownAction.setEnabled(False)
        self.removeItemAction.setEnabled(False)
        self.removeAllAction.setEnabled(False)
        index = self.cliplist.currentRow()
        if index != -1:
            if not self.inCut:
                if index > 0:
                    self.moveItemUpAction.setEnabled(True)
                if index < self.cliplist.count() - 1:
                    self.moveItemDownAction.setEnabled(True)
            if self.cliplist.count() > 0:
                self.removeItemAction.setEnabled(True)
        if self.cliplist.count() > 0:
            self.removeAllAction.setEnabled(True)
        self.cliplistMenu.exec_(globalPos)

    def moveItemUp(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index - 1, tmpItem)
        self.renderTimes()

    def moveItemDown(self) -> None:
        index = self.cliplist.currentRow()
        tmpItem = self.clipTimes[index]
        del self.clipTimes[index]
        self.clipTimes.insert(index + 1, tmpItem)
        self.renderTimes()

    def removeItem(self) -> None:
        index = self.cliplist.currentRow()
        del self.clipTimes[index]
        if self.inCut and index == self.cliplist.count() - 1:
            self.inCut = False
            self.initMediaControls()
        self.renderTimes()

    def clearList(self) -> None:
        self.clipTimes.clear()
        self.cliplist.clear()
        self.inCut = False
        self.renderTimes()
        self.initMediaControls()

    def mediaInfo(self) -> None:
        if self.mediaPlayer.isMetaDataAvailable():
            content = '<table cellpadding="4">'
            for key in self.mediaPlayer.availableMetaData():
                val = self.mediaPlayer.metaData(key)
                if type(val) is QSize:
                    val = '%s x %s' % (val.width(), val.height())
                content += '<tr><td align="right"><b>%s:</b></td><td>%s</td></tr>\n' % (
                    key, val)
            content += '</table>'
            mbox = QMessageBox(windowTitle='Media Information',
                               windowIcon=self.parent.windowIcon(),
                               textFormat=Qt.RichText)
            mbox.setText('<b>%s</b>' % os.path.basename(
                self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()))
            mbox.setInformativeText(content)
            mbox.exec_()
        else:
            QMessageBox.critical(
                self.parent, 'MEDIA ERROR',
                '<h3>Could not probe media file.</h3>' +
                '<p>An error occurred while analyzing the media file for its metadata details.'
                +
                '<br/><br/><b>This DOES NOT mean there is a problem with the file and you should '
                + 'be able to continue using it.</b></p>')

    def aboutInfo(self) -> None:
        about_html = '''<style>
    a { color:#441d4e; text-decoration:none; font-weight:bold; }
    a:hover { text-decoration:underline; }
</style>
<div style="min-width:650px;">
<p style="font-size:26pt; font-weight:bold; color:#6A4572;">%s</p>
<p>
    <span style="font-size:13pt;"><b>Version: %s</b></span>
    <span style="font-size:10pt;position:relative;left:5px;">( %s )</span>
</p>
<p style="font-size:13px;">
    Copyright &copy; 2016 <a href="mailto:[email protected]">Pete Alexandrou</a>
    <br/>
    Website: <a href="%s">%s</a>
</p>
<p style="font-size:13px;">
    Thanks to the folks behind the <b>Qt</b>, <b>PyQt</b> and <b>FFmpeg</b>
    projects for all their hard and much appreciated work.
</p>
<p style="font-size:11px;">
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.
</p>
<p style="font-size:11px;">
    This software uses libraries from the <a href="https://www.ffmpeg.org">FFmpeg</a> project under the
    <a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html">LGPLv2.1</a>
</p></div>''' % (qApp.applicationName(), qApp.applicationVersion(),
                 platform.architecture()[0], qApp.organizationDomain(),
                 qApp.organizationDomain())
        QMessageBox.about(self.parent, 'About %s' % qApp.applicationName(),
                          about_html)

    def openMedia(self) -> None:
        filename, _ = QFileDialog.getOpenFileName(self.parent,
                                                  caption='Select video',
                                                  directory=QDir.homePath())
        if filename != '':
            self.loadFile(filename)

    def loadFile(self, filename: str) -> None:
        self.movieFilename = filename
        if not os.path.exists(filename):
            return
        self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
        self.initMediaControls(True)
        self.cliplist.clear()
        self.clipTimes = []
        self.parent.setWindowTitle(
            '%s - %s' % (qApp.applicationName(), os.path.basename(filename)))
        if not self.movieLoaded:
            self.videoLayout.replaceWidget(self.novideoWidget,
                                           self.videoplayerWidget)
            self.novideoMovie.stop()
            self.novideoMovie.deleteLater()
            self.novideoWidget.deleteLater()
            self.videoplayerWidget.show()
            self.videoWidget.show()
            self.movieLoaded = True
        if self.mediaPlayer.isVideoAvailable():
            self.mediaPlayer.setPosition(1)
        self.mediaPlayer.play()
        self.mediaPlayer.pause()

    def playMedia(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
            self.playAction.setText('Play')
        else:
            self.mediaPlayer.play()
            self.playAction.setText('Pause')

    def initMediaControls(self, flag: bool = True) -> None:
        self.playAction.setEnabled(flag)
        self.saveAction.setEnabled(False)
        self.cutStartAction.setEnabled(flag)
        self.cutEndAction.setEnabled(False)
        self.mediaInfoAction.setEnabled(flag)
        if flag:
            self.seekSlider.setRestrictValue(0)

    def setPosition(self, position: int) -> None:
        self.mediaPlayer.setPosition(position)

    def positionChanged(self, progress: int) -> None:
        self.seekSlider.setValue(progress)
        currentTime = self.deltaToQTime(progress)
        totalTime = self.deltaToQTime(self.mediaPlayer.duration())
        self.timeCounter.setText('%s / %s' % (currentTime.toString(
            self.timeformat), totalTime.toString(self.timeformat)))

    @pyqtSlot()
    def mediaStateChanged(self) -> None:
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.playAction.setIcon(self.pauseIcon)
        else:
            self.playAction.setIcon(self.playIcon)

    def durationChanged(self, duration: int) -> None:
        self.seekSlider.setRange(0, duration)

    def muteAudio(self) -> None:
        if self.mediaPlayer.isMuted():
            self.muteButton.setIcon(self.unmuteIcon)
            self.muteButton.setToolTip('Mute')
        else:
            self.muteButton.setIcon(self.muteIcon)
            self.muteButton.setToolTip('Unmute')
        self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted())

    def setVolume(self, volume: int) -> None:
        self.mediaPlayer.setVolume(volume)

    def toggleFullscreen(self) -> None:
        self.videoWidget.setFullScreen(not self.videoWidget.isFullScreen())

    def setCutStart(self) -> None:
        self.clipTimes.append([
            self.deltaToQTime(self.mediaPlayer.position()), '',
            self.captureImage()
        ])
        self.cutStartAction.setDisabled(True)
        self.cutEndAction.setEnabled(True)
        self.seekSlider.setRestrictValue(self.seekSlider.value(), True)
        self.inCut = True
        self.renderTimes()

    def setCutEnd(self) -> None:
        item = self.clipTimes[len(self.clipTimes) - 1]
        selected = self.deltaToQTime(self.mediaPlayer.position())
        if selected.__lt__(item[0]):
            QMessageBox.critical(
                self.parent, 'Invalid END Time',
                'The clip end time must come AFTER it\'s start time. Please try again.'
            )
            return
        item[1] = selected
        self.cutStartAction.setEnabled(True)
        self.cutEndAction.setDisabled(True)
        self.seekSlider.setRestrictValue(0, False)
        self.inCut = False
        self.renderTimes()

    @pyqtSlot(QModelIndex, int, int, QModelIndex, int)
    def syncClipList(self, parent: QModelIndex, start: int, end: int,
                     destination: QModelIndex, row: int) -> None:
        if start < row:
            index = row - 1
        else:
            index = row
        clip = self.clipTimes.pop(start)
        self.clipTimes.insert(index, clip)

    def renderTimes(self) -> None:
        self.cliplist.clear()
        if len(self.clipTimes) > 4:
            self.cliplist.setFixedWidth(200)
        else:
            self.cliplist.setFixedWidth(185)
        self.totalRuntime = 0
        for item in self.clipTimes:
            endItem = ''
            if type(item[1]) is QTime:
                endItem = item[1].toString(self.timeformat)
                self.totalRuntime += item[0].msecsTo(item[1])
            listitem = QListWidgetItem()
            listitem.setTextAlignment(Qt.AlignVCenter)
            if type(item[2]) is QPixmap:
                listitem.setIcon(QIcon(item[2]))
            self.cliplist.addItem(listitem)
            marker = QLabel(
                '''<style>b { font-size:7pt; } p { margin:2px 5px; }</style>
                            <p><b>START</b><br/>%s<br/><b>END</b><br/>%s</p>'''
                % (item[0].toString(self.timeformat), endItem))
            marker.setStyleSheet('border:none;')
            self.cliplist.setItemWidget(listitem, marker)
            listitem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled
                              | Qt.ItemIsEnabled)
        if len(self.clipTimes) and not self.inCut:
            self.saveAction.setEnabled(True)
        if self.inCut or len(self.clipTimes) == 0 or not type(
                self.clipTimes[0][1]) is QTime:
            self.saveAction.setEnabled(False)
        self.setRunningTime(
            self.deltaToQTime(self.totalRuntime).toString(self.timeformat))

    @staticmethod
    def deltaToQTime(millisecs: int) -> QTime:
        secs = millisecs / 1000
        return QTime((secs / 3600) % 60, (secs / 60) % 60, secs % 60,
                     (secs * 1000) % 1000)

    def captureImage(self) -> QPixmap:
        frametime = self.deltaToQTime(self.mediaPlayer.position()).toString(
            self.timeformat)
        inputfile = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile(
        )
        imagecap = self.videoService.capture(inputfile, frametime)
        if type(imagecap) is QPixmap:
            return imagecap

    def cutVideo(self) -> bool:
        clips = len(self.clipTimes)
        filename, filelist = '', []
        source = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile()
        _, sourceext = os.path.splitext(source)
        if clips > 0:
            self.finalFilename, _ = QFileDialog.getSaveFileName(
                self.parent, 'Save video', source,
                'Video files (*%s)' % sourceext)
            if self.finalFilename == '':
                return False
            qApp.setOverrideCursor(Qt.BusyCursor)
            self.saveAction.setDisabled(True)
            self.showProgress(clips)
            file, ext = os.path.splitext(self.finalFilename)
            index = 1
            self.progress.setLabelText('Cutting media files...')
            qApp.processEvents()
            for clip in self.clipTimes:
                duration = self.deltaToQTime(clip[0].msecsTo(
                    clip[1])).toString(self.timeformat)
                filename = '%s_%s%s' % (file, '{0:0>2}'.format(index), ext)
                filelist.append(filename)
                self.videoService.cut(source, filename,
                                      clip[0].toString(self.timeformat),
                                      duration)
                index += 1
            if len(filelist) > 1:
                self.joinVideos(filelist, self.finalFilename)
            else:
                QFile.remove(self.finalFilename)
                QFile.rename(filename, self.finalFilename)
            self.progress.setLabelText('Complete...')
            self.progress.setValue(100)
            qApp.processEvents()
            self.progress.close()
            self.progress.deleteLater()
            qApp.restoreOverrideCursor()
            self.complete()
            return True
        return False

    def joinVideos(self, joinlist: list, filename: str) -> None:
        listfile = os.path.normpath(
            os.path.join(os.path.dirname(joinlist[0]), '.vidcutter.list'))
        fobj = open(listfile, 'w')
        for file in joinlist:
            fobj.write('file \'%s\'\n' % file.replace("'", "\\'"))
        fobj.close()
        self.videoService.join(listfile, filename)
        QFile.remove(listfile)
        for file in joinlist:
            if os.path.isfile(file):
                QFile.remove(file)

    def updateCheck(self) -> None:
        self.updater = Updater()
        self.updater.updateAvailable.connect(self.updateHandler)
        self.updater.start()

    def updateHandler(self, updateExists: bool, version: str = None):
        if updateExists:
            if Updater.notify_update(self, version) == QMessageBox.AcceptRole:
                self.updater.install_update(self)
        else:
            Updater.notify_no_update(self)

    def showProgress(self,
                     steps: int,
                     label: str = 'Analyzing media...') -> None:
        self.progress = QProgressDialog(label,
                                        None,
                                        0,
                                        steps,
                                        self.parent,
                                        windowModality=Qt.ApplicationModal,
                                        windowIcon=self.parent.windowIcon(),
                                        minimumDuration=0,
                                        minimumWidth=500)
        self.progress.show()
        for i in range(steps):
            self.progress.setValue(i)
            qApp.processEvents()
            time.sleep(1)

    def complete(self) -> None:
        info = QFileInfo(self.finalFilename)
        mbox = QMessageBox(windowTitle='VIDEO PROCESSING COMPLETE',
                           minimumWidth=500,
                           textFormat=Qt.RichText)
        mbox.setText(
            '''
    <style>
        table.info { margin:6px; padding:4px 15px; }
        td.label { font-weight:bold; font-size:10.5pt; text-align:right; }
        td.value { font-size:10.5pt; }
    </style>
    <table class="info" cellpadding="4" cellspacing="0">
        <tr>
            <td class="label"><b>File:</b></td>
            <td class="value" nowrap>%s</td>
        </tr>
        <tr>
            <td class="label"><b>Size:</b></td>
            <td class="value">%s</td>
        </tr>
        <tr>
            <td class="label"><b>Length:</b></td>
            <td class="value">%s</td>
        </tr>
    </table><br/>''' %
            (QDir.toNativeSeparators(
                self.finalFilename), self.sizeof_fmt(int(info.size())),
             self.deltaToQTime(self.totalRuntime).toString(self.timeformat)))
        play = mbox.addButton('Play', QMessageBox.AcceptRole)
        play.setIcon(self.completePlayIcon)
        play.clicked.connect(self.openResult)
        fileman = mbox.addButton('Open', QMessageBox.AcceptRole)
        fileman.setIcon(self.completeOpenIcon)
        fileman.clicked.connect(self.openFolder)
        end = mbox.addButton('Exit', QMessageBox.AcceptRole)
        end.setIcon(self.completeExitIcon)
        end.clicked.connect(self.close)
        new = mbox.addButton('Restart', QMessageBox.AcceptRole)
        new.setIcon(self.completeRestartIcon)
        new.clicked.connect(self.parent.restart)
        mbox.setDefaultButton(new)
        mbox.setEscapeButton(new)
        mbox.adjustSize()
        mbox.exec_()

    def sizeof_fmt(self, num: float, suffix: chr = 'B') -> str:
        for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
            if abs(num) < 1024.0:
                return "%3.1f%s%s" % (num, unit, suffix)
            num /= 1024.0
        return "%.1f%s%s" % (num, 'Y', suffix)

    @pyqtSlot()
    def openFolder(self) -> None:
        self.openResult(pathonly=True)

    @pyqtSlot(bool)
    def openResult(self, pathonly: bool = False) -> None:
        self.parent.restart()
        if len(self.finalFilename) and os.path.exists(self.finalFilename):
            target = self.finalFilename if not pathonly else os.path.dirname(
                self.finalFilename)
            QDesktopServices.openUrl(QUrl.fromLocalFile(target))

    @pyqtSlot()
    def startNew(self) -> None:
        qApp.restoreOverrideCursor()
        self.clearList()
        self.seekSlider.setValue(0)
        self.seekSlider.setRange(0, 0)
        self.mediaPlayer.setMedia(QMediaContent())
        self.initNoVideo()
        self.videoLayout.replaceWidget(self.videoplayerWidget,
                                       self.novideoWidget)
        self.initMediaControls(False)
        self.parent.setWindowTitle('%s' % qApp.applicationName())

    def wheelEvent(self, event: QWheelEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            if event.angleDelta().y() > 0:
                newval = self.seekSlider.value() - 1000
            else:
                newval = self.seekSlider.value() + 1000
            self.seekSlider.setValue(newval)
            self.seekSlider.setSliderPosition(newval)
            self.mediaPlayer.setPosition(newval)
        event.accept()

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if self.mediaPlayer.isVideoAvailable(
        ) or self.mediaPlayer.isAudioAvailable():
            addtime = 0
            if event.key() == Qt.Key_Left:
                addtime = -1000
            elif event.key() == Qt.Key_PageUp or event.key() == Qt.Key_Up:
                addtime = -10000
            elif event.key() == Qt.Key_Right:
                addtime = 1000
            elif event.key() == Qt.Key_PageDown or event.key() == Qt.Key_Down:
                addtime = 10000
            elif event.key() == Qt.Key_Enter:
                self.toggleFullscreen()
            elif event.key(
            ) == Qt.Key_Escape and self.videoWidget.isFullScreen():
                self.videoWidget.setFullScreen(False)
            if addtime != 0:
                newval = self.seekSlider.value() + addtime
                self.seekSlider.setValue(newval)
                self.seekSlider.setSliderPosition(newval)
                self.mediaPlayer.setPosition(newval)
        event.accept()

    def mousePressEvent(self, event: QMouseEvent) -> None:
        if event.button() == Qt.BackButton and self.cutStartAction.isEnabled():
            self.setCutStart()
            event.accept()
        elif event.button(
        ) == Qt.ForwardButton and self.cutEndAction.isEnabled():
            self.setCutEnd()
            event.accept()
        else:
            super(VidCutter, self).mousePressEvent(event)

    @pyqtSlot(QMediaPlayer.Error)
    def handleError(self, error: QMediaPlayer.Error) -> None:
        qApp.restoreOverrideCursor()
        self.startNew()
        if error == QMediaPlayer.ResourceError:
            QMessageBox.critical(
                self.parent, 'INVALID MEDIA',
                'Invalid media file detected at:<br/><br/><b>%s</b><br/><br/>%s'
                % (self.movieFilename, self.mediaPlayer.errorString()))
        else:
            QMessageBox.critical(self.parent, 'ERROR NOTIFICATION',
                                 self.mediaPlayer.errorString())

    def closeEvent(self, event: QCloseEvent) -> None:
        self.parent.closeEvent(event)
示例#10
0
class ArnheimModelSelector(BaseSelector):
    list_query = None
    model = None
    new_form = None

    # can stay not used

    def __init__(self,
                 *args,
                 base,
                 add_none=False,
                 variables=None,
                 with_form=True,
                 on_select=None,
                 **kwargs):
        super().__init__(*args, base=base, **kwargs)
        self.selected_model = None
        self.variables = variables
        self.selector = None
        self.model_selector = None
        self.with_form = with_form
        self.add_none = add_none
        self.on_select = on_select
        self.loadModels()

        self.layout = QHBoxLayout()

        self.setLayout(self.layout)

        self.buildOrReplaceSelector()
        self.buildButton()

    def loadModels(self):
        self.models = QueryList(self.list_query,
                                self.model).run(variables=self.variables)

    def requestNewModel(self):
        form = self.new_form(base=self.base, parent=self)
        model = form.getModel()
        if model:
            self.models = [
                model
            ] + self.models  # We are adding it to the first list
            self.buildOrReplaceSelector()

    def buildOrReplaceSelector(self):
        assert self.models is not None, "Please load Models beforee"

        model_selector = QComboBox()

        if self.add_none: model_selector.addItem("-----")
        for model in self.models:
            model_selector.addItem(model.name)

        model_selector.currentIndexChanged.connect(self.indexChanged)

        if len(self.models) > 0:
            self.selected_model = self.models[
                0]  # We automatically select the first item once rebuilding

        if not self.model_selector:
            self.layout.addWidget(model_selector)
            self.model_selector = model_selector

        else:
            self.layout.replaceWidget(self.model_selector, model_selector)
            self.model_selector.close()
            self.model_selector.setParent(None)
            self.model_selector = model_selector

        self.layout.update()

        return self.model_selector

    def buildButton(self):
        if self.new_form and self.with_form:
            new_model_button = QPushButton("+")
            new_model_button.clicked.connect(self.requestNewModel)
            self.layout.addWidget(new_model_button)
            self.layout.update()

    def indexChanged(self, index):
        if self.add_none:
            if index == 0:
                self.selected_model = None
            else:
                self.selected_model = self.models[index -
                                                  1]  # First item is now add
        else:
            self.selected_model = self.models[index]

        if self.on_select: self.on_select(self.selected_model)

    def getValue(self):
        return str(self.selected_model.id
                   ) if self.selected_model is not None else None
class VistaListaMenuAmministratore(QWidget):
    def __init__(self):
        super(VistaListaMenuAmministratore, self).__init__()

        self.controller = ControlloreListaMenu()

        self.h_layout = QHBoxLayout()
        self.list_view = QListView()
        self.listview_model = QStandardItemModel(self.list_view)
        for ProdottoSingolo in self.controller.get_lista_menu():
            item = QStandardItem()
            item.setText("{} ".format(ProdottoSingolo.prodotto) +
                         "{}€".format(ProdottoSingolo.prezzo))
            item.setEditable(False)
            font = item.font()
            font.setPointSize(18)
            item.setFont(font)
            self.listview_model.appendRow(item)
        self.list_view.setModel(self.listview_model)
        self.h_layout.addWidget(self.list_view)

        buttons_layout = QVBoxLayout()

        open_button = QPushButton("Apri")
        open_button.clicked.connect(self.show_selected_info)
        buttons_layout.addWidget(open_button)

        add_button = QPushButton("Inserisci")
        add_button.clicked.connect(self.add_info)
        buttons_layout.addWidget(add_button)

        delete_button = QPushButton("Elimina")
        delete_button.clicked.connect(self.delete_selected_info)
        buttons_layout.addWidget(delete_button)

        buttons_layout.addStretch()
        self.h_layout.addLayout(buttons_layout)

        self.setLayout(self.h_layout)
        self.resize(600, 300)
        self.setWindowTitle("Lista Menu")

    def closeEvent(self, event):
        self.controller.save_data()
        event.accept()

    def show_selected_info(self):
        if len(self.list_view.selectedIndexes()) > 0:
            selected = self.list_view.selectedIndexes()[0].row()
            prodotto_selezionato = self.controller.get_prodotto_by_index(
                selected)
            self.vista_prodotto = VistaProdotto(prodotto_selezionato)
            self.vista_prodotto.show()

    def delete_selected_info(self):
        if len(self.list_view.selectedIndexes()) > 0:
            selected = self.list_view.selectedIndexes()[0].row()
            prodotto_selezionato = self.controller.get_prodotto_by_index(
                selected)
            self.controller.elimina_prodotto(prodotto_selezionato)
            self.update_ui()
            self.h_layout.replaceWidget(self.list_view, self.list_view)

    def add_info(self):
        self.vista_inserisci_prodotto = VistaInserisciProdotto(
            self.controller, self.update_ui)
        self.vista_inserisci_prodotto.show()

    def update_ui(self):
        self.listview_model = QStandardItemModel(self.list_view)
        for prodotto in self.controller.get_lista_menu():
            item = QStandardItem()
            item.setText(prodotto.prodotto + " {}".format(prodotto.prezzo) +
                         "€")
            item.setEditable(False)
            font = item.font()
            font.setPointSize(18)
            item.setFont(font)
            self.listview_model.appendRow(item)
        self.list_view.setModel(self.listview_model)
示例#12
0
class GuiWindow(QMainWindow):
    def __init__(self, controller, base_img_fp: str):
        super(GuiWindow, self).__init__()
        # self.app = QApplication([])

        self.img_manager = ImageManager(base_img_fp)

        self._file_path = base_img_fp

        self._anim_thread = None
        self._anim_worker = None
        self.animation_widget = None

        self._current_chunk_size = 0

        self.setWindowTitle("Don't Feel Good Inc.")

        self.controller = controller
        self.create_content()
        self.show()

    def create_content(self):
        self.snap_sound = PyQt5.QtMultimedia.QSound("snap_ex.wav")

        # Build the components first

        # Build a textbox for the filepath.

        self.outer_widget = PyQt5.QtWidgets.QWidget(
            self)  # Dummy outer widget needed so that we can add others to it

        # This is the frame to hold the options.
        self.main_frame = PyQt5.QtWidgets.QGroupBox(
            self.outer_widget
        )  # Specifying parent=self locks it within the current window
        # self.main_frame.setTitle("Parameters")

        # The main layout for the param box and the image
        self.main_layout_wide = QHBoxLayout(self.main_frame)

        self.input_frame = QWidget(self.outer_widget)

        self.main_layout_wide.addWidget(self.input_frame)

        self.input_frame_layout = QVBoxLayout(self.input_frame)

        # Now, adding the parts back in...

        self.textbox = QLabel(self.input_frame)
        self.textbox.setText(self._file_path)

        self.load_file_button = PyQt5.QtWidgets.QPushButton(self.input_frame)
        self.load_file_button.setText("Browse for file")

        self.load_file_button.clicked.connect(self.on_load)

        self.gain_slider_label = PyQt5.QtWidgets.QLabel(self.input_frame)
        self.gain_slider_label.setText("Custom adjustment")

        self.gain_slider = PyQt5.QtWidgets.QSlider(Qt.Horizontal,
                                                   self.input_frame)
        self.gain_slider.valueChanged.connect(self.on_gain_slider_adjust)

        # size slider

        self.chunk_slider_label = PyQt5.QtWidgets.QLabel(self.input_frame)
        self.chunk_slider_label.setText("Chunk size")

        self.chunk_size_slider = PyQt5.QtWidgets.QSlider(
            Qt.Horizontal, self.input_frame)

        # Start this in the middle so we don't get a div/0

        self.chunk_size_slider.setSliderPosition(40)

        self.chunk_size_slider.valueChanged.connect(
            self.on_chunk_slider_adjust)

        # This needs to be controlled with a single button press as it'll involve reloading the object
        self.chunk_size_button = PyQt5.QtWidgets.QPushButton(
            "Apply chunk size", self.input_frame)

        self.chunk_size_button.clicked.connect(self.on_chunk_click)

        # self.go_button = PyQt5.QtWidgets.QPushButton("*snap*", self.input_frame)
        #
        # self.go_button.clicked.connect(self.on_snap)

        self.full_snap_button = PyQt5.QtWidgets.QPushButton(
            "*snap*", self.input_frame)

        self.full_snap_button.clicked.connect(self.display_animation)

        self.reset_button = PyQt5.QtWidgets.QPushButton(
            "Use the time stone (reset)", self.input_frame)

        self.reset_button.clicked.connect(self.on_reset)

        self.input_frame_layout.addWidget(self.textbox)
        self.input_frame_layout.addWidget(self.load_file_button)
        self.input_frame_layout.addWidget(self.chunk_slider_label)
        self.input_frame_layout.addWidget(self.chunk_size_slider)
        self.input_frame_layout.addWidget(self.chunk_size_button)
        self.input_frame_layout.addWidget(self.gain_slider_label)
        self.input_frame_layout.addWidget(self.gain_slider)
        # self.input_frame_layout.addWidget(self.go_button)
        self.input_frame_layout.addWidget(self.full_snap_button)
        self.input_frame_layout.addWidget(self.reset_button)

        # Trying something else out

        self.image_frame = QFrame(self)

        # Creating the second container

        self.image_widget = ImageWidget(self._file_path, self.outer_widget)

        self.main_layout_wide.addWidget(self.image_widget)

        # self.main_layout.addLayout(self.main_frame)

        self.setCentralWidget(self.outer_widget)

        self.setFixedSize(self.main_layout_wide.sizeHint())

        self.on_chunk_click()

        # Make sure we're ready to handle an animation if the need arises
        # AnimationReadyEmitter.trigger.connect(self.display_animation)

        # self.main_frame.show()

    def on_load(self):
        dlg = QFileDialog()
        dlg.setFileMode(QFileDialog.ExistingFile)
        new_path, _ = dlg.getOpenFileName(None, "Open file",
                                          "%userprofile%\\Pictures\\")
        if new_path == ("", ""):
            return
        if not os.path.exists(new_path):
            self.textbox.setStyleSheet("color: rgb(255, 0, 0);")
        else:
            self.textbox.setStyleSheet("color: rgb(0, 0, 0);")

            self._file_path = new_path
            self.textbox.setText(self._file_path)
            self.img_manager = ImageManager(new_path)
            q_pixmap = PyQt5.QtGui.QPixmap(self._file_path)

            self.image_widget.setPixmap(q_pixmap)
            self.image_widget.resize(q_pixmap.width(), q_pixmap.height())
            self.main_frame.resize(self.main_layout_wide.sizeHint())
            self.setFixedSize(self.main_layout_wide.sizeHint())

    def display_animation(self):
        self.snap_sound.play()
        self._anim_worker = AnimWorker(self.img_manager, True)
        self._anim_thread = QThread()
        self._anim_worker.moveToThread(self._anim_thread)

        self._anim_worker.anim_done.connect(self.on_animation_complete)

        self._anim_thread.started.connect(self._anim_worker.work)

        self.full_snap_button.setDisabled(True)

        self._anim_thread.start()

    @pyqtSlot(str)
    def on_animation_complete(self, file_path: str):
        """Replace the original image widget until the movie completes, and then change it back"""
        self.animation_widget = AnimationWidget(file_path, self.outer_widget)
        self.full_snap_button.setDisabled(False)

        # self.main_layout_wide.replaceWidget(self.image_widget, self.animation_widget)
        prev_image_ix = self.main_layout_wide.indexOf(self.image_widget)
        # self.main_layout_wide.removeWidget(self.image_widget)
        self.image_widget.hide()  # FIXME Yuck
        self.main_layout_wide.addWidget(self.animation_widget, Qt.Horizontal)
        self.animation_widget.movie.finished.connect(
            self.replace_original_img_widget)
        # self.animation_widget.movie.setScaledSize(QSize(900, 900))
        self.animation_widget.movie.start()
        self.animation_widget.show()

    @pyqtSlot()
    def replace_original_img_widget(self):
        self.animation_widget.movie.stop()  # This is needed to
        self.animation_widget.hide()
        self.image_widget.show()
        self.main_layout_wide.removeWidget(self.animation_widget)
        self.main_layout_wide.insertWidget(1, self.image_widget)
        self.main_layout_wide.replaceWidget(self.animation_widget,
                                            self.image_widget)

    def on_snap(self):
        self.snap_sound.play()
        self.reload_image()

    def on_full_snap(self):
        for i in reversed(range(1, 101)):
            self.gain_slider.setSliderPosition(i)
            self.reload_image()
            sleep(3 / i**2)
            # sleep(1)

    def reload_image(self):
        """
        Reload the image within the current frame, asking the underlying functions to recalculate
        based on its current values.
        """
        img = self.img_manager.update_image()

        q_image = PyQt5.QtGui.QImage.fromData(img.read())
        q_pixmap = PyQt5.QtGui.QPixmap.fromImage(q_image)

        self.image_widget.setPixmap(q_pixmap)

    def on_gain_slider_adjust(self):
        new_value = self.gain_slider.value()
        # TODO Scale this
        print(new_value)
        self.img_manager.gain = new_value
        if self.img_manager.chunk_size > 10:
            self.reload_image()

    def on_chunk_slider_adjust(self):
        if self.chunk_size_slider.value() != self._current_chunk_size:
            self.chunk_size_button.setDisabled(False)

    def on_chunk_click(self):
        new_value = self.chunk_size_slider.value(
        ) + 1  # Make sure it's never equal to 0
        self.img_manager.chunk_size = new_value
        self.chunk_size_button.setDisabled(True)

    def on_reset(self):
        self.img_manager.reset()
        self.gain_slider.setValue(0)
        q_pixmap = PyQt5.QtGui.QPixmap(self._file_path)

        self.image_widget.setPixmap(q_pixmap)