Ejemplo n.º 1
0
    def __init__(self, ):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.fig_dict = {}

        self.tabWidget.setCurrentIndex(0)

        self.plotAreaVerticalLayout = QtWidgets.QVBoxLayout()
        self.plotsFrame.setLayout(self.plotAreaVerticalLayout)

        #initialize Table
        self.headerV = self.tableWidget.verticalHeader()
        self.headerV.show()

        # add a widget for previewing plots, they can then be added to the actual plot
        self.plotPreview = PlotWidget()
        self.plotAreaVerticalLayout.addWidget(self.plotPreview)

        # Create tree model to store sim data items and connect it to views
        self.simDataItemTreeModel = SimDataItemTreeModel()
        self.bdTableModel = BdTableModel()
        self.simDataItemTreeView.setModel(self.simDataItemTreeModel)
        self.plotPreview.tableView.setModel(self.bdTableModel)

        # connect a double clicked section of the bd table to a change of the anchor
        self.plotPreview.tableView.horizontalHeader().sectionDoubleClicked.connect(self.update_bd_table)

        # Set custom selection model, so that sub items are automatically
        # selected if parent is selected
        self._selection_model = QRecursiveSelectionModel(self.simDataItemTreeView.model())
        self.simDataItemTreeView.setSelectionModel(self._selection_model)

        # Connect list view with model for the selected values of tree view
        self.selectedSimulationDataItemListModel = OrderedDictModel()
        self.simDataItemListView.setModel(self.selectedSimulationDataItemListModel)
        self._selection_model.selectionChanged.connect(self.change_list)

        # set up signals and slots
        self.selectedSimulationDataItemListModel.items_changed.connect(
            self.update_variable_tree
        )

        # Connect signals of menus
        self.actionOpen_File.triggered.connect(
            self.simDataItemTreeView.add_file
        )
        self.actionOpen_Directory.triggered.connect(
            self.simDataItemTreeView.add_folder
        )
        self.actionOpen_Directory_List.triggered.connect(
            self.simDataItemTreeView.add_folder_list
        )
        self.actionHide_PlotSettings.triggered.connect(
            self.set_plot_settings_visibility
        )
        self.actionHide_Sequence.triggered.connect(
            self.set_sequence_widget_visibility
        )
        self.actionHide_Status.triggered.connect(
            self.set_status_widget_visibility
        )

        self.actionSave_Table.triggered.connect(
            self.save_bd_table
        )

        self.actionExport_Figure_as_Tikzpicture.triggered.connect(
            self.plotPreview.export_plot_tikz
        )

        self.actionExport_TableWidget.triggered.connect(
            self.export_table_to_csv
        )

        self.actionSave_Data.triggered.connect(
            self.save_current_selection
        )

        self.action_About.triggered.connect(
            self.open_about_page
        )

        self.variableTreeModel = VariableTreeModel()
        self.variableTreeView.setModel(self.variableTreeModel)
        self.plotsettings.visibilityChanged.connect(self.plot_settings_visibility_changed)
        self.sequenceWidget.visibilityChanged.connect(self.sequence_widget_visibility_changed)
        self.statusWidget.visibilityChanged.connect(self.status_widget_visibility_changed)

        # Set recursive selection model for variable view
        self._variable_tree_selection_model = QRecursiveSelectionModel(
            self.variableTreeView.model()
        )
        self.variableTreeView.setSelectionModel(
            self._variable_tree_selection_model
        )

        # set up combo boxes for rate/psnr and interpolation options
        self.combo_interp.addItems(["pchip", "pol"])
        self.combo_rate_psnr.addItems(["drate", "dsnr"])
        self.combo_interp.currentIndexChanged.connect(self.on_combo_box)
        self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box)

        # set up bd plot checkbox
        self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot)
Ejemplo n.º 2
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, ):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.fig_dict = {}

        self.tabWidget.setCurrentIndex(0)

        self.plotAreaVerticalLayout = QtWidgets.QVBoxLayout()
        self.plotsFrame.setLayout(self.plotAreaVerticalLayout)

        #initialize Table
        self.headerV = self.tableWidget.verticalHeader()
        self.headerV.show()

        # add a widget for previewing plots, they can then be added to the actual plot
        self.plotPreview = PlotWidget()
        self.plotAreaVerticalLayout.addWidget(self.plotPreview)

        # Create tree model to store sim data items and connect it to views
        self.simDataItemTreeModel = SimDataItemTreeModel()
        self.bdTableModel = BdTableModel()
        self.bdUserGeneratedTableModel = BdUserGeneratedCurvesTableModel()
        self.simDataItemTreeView.setModel(self.simDataItemTreeModel)
        self.plotPreview.tableView.setModel(self.bdTableModel)

        # connect a double clicked section of the bd table to a change of the anchor
        self.plotPreview.tableView.horizontalHeader(
        ).sectionDoubleClicked.connect(self.update_bd_table)
        self.plotPreview.tableView.verticalHeader(
        ).sectionDoubleClicked.connect(
            self.update_bd_user_generated_curves_table)

        # Set custom selection model, so that sub items are automatically
        # selected if parent is selected
        self._selection_model = QRecursiveSelectionModel(
            self.simDataItemTreeView.model())
        self.simDataItemTreeView.setSelectionModel(self._selection_model)

        # Connect list view with model for the selected values of tree view
        self.selectedSimulationDataItemListModel = OrderedDictModel()
        self.simDataItemListView.setModel(
            self.selectedSimulationDataItemListModel)
        self._selection_model.selectionChanged.connect(self.change_list)

        # set up signals and slots
        self.selectedSimulationDataItemListModel.items_changed.connect(
            self.update_variable_tree)

        # Connect signals of menus
        self.actionOpen_File.triggered.connect(
            self.simDataItemTreeView.add_file)
        self.actionOpen_Directory.triggered.connect(
            self.simDataItemTreeView.add_folder)
        self.actionOpen_Directory_List.triggered.connect(
            self.simDataItemTreeView.add_folder_list)
        self.actionHide_PlotSettings.triggered.connect(
            self.set_plot_settings_visibility)
        self.actionHide_Sequence.triggered.connect(
            self.set_sequence_widget_visibility)
        self.actionHide_Status.triggered.connect(
            self.set_status_widget_visibility)

        self.actionSave_Table.triggered.connect(self.save_bd_table)

        self.actionExport_Figure_as_Tikzpicture.triggered.connect(
            self.plotPreview.export_plot_tikz)

        self.actionExport_TableWidget.triggered.connect(
            self.export_table_to_csv)

        self.actionSave_Data.triggered.connect(self.save_current_selection)

        self.action_About.triggered.connect(self.open_about_page)

        self.variableTreeModel = VariableTreeModel()
        self.variableTreeView.setModel(self.variableTreeModel)
        self.plotsettings.visibilityChanged.connect(
            self.plot_settings_visibility_changed)
        self.sequenceWidget.visibilityChanged.connect(
            self.sequence_widget_visibility_changed)
        self.statusWidget.visibilityChanged.connect(
            self.status_widget_visibility_changed)

        # Set recursive selection model for variable view
        self._variable_tree_selection_model = QRecursiveSelectionModel(
            self.variableTreeView.model())
        self.variableTreeView.setSelectionModel(
            self._variable_tree_selection_model)

        # set up combo boxes for rate/psnr and interpolation options
        self.combo_interp.addItems(["pchip", "pol"])
        self.combo_rate_psnr.addItems(["drate", "dsnr"])
        self.combo_interp.currentIndexChanged.connect(self.on_combo_box)
        self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box)

        # set up bd plot checkbox
        self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot)

        self.curveWidget.hide()
        self.curveListModel = OrderedDictModel()
        self.curveListView.setModel(self.curveListModel)
        self.curveListSelectionModel = QItemSelectionModel(self.curveListModel)
        self.curveListView.setSelectionModel(self.curveListSelectionModel)
        self.curveListView.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.curveWidget.visibilityChanged.connect(
            self.curve_widget_visibility_changed)

        self.actionGenerate_curve.triggered.connect(self.generate_new_curve)
        self.actionRemove_items.triggered.connect(self.remove)
        self.actionReload_files.triggered.connect(self.reload_files)

        self.settings = QSettings()
        self.get_recent_files()
        self.simDataItemTreeView.itemsOpened.connect(self.add_recent_files)

        self.watcher = QFileSystemWatcher(self)
        self.watcher.fileChanged.connect(self.warning_file_change)
        self.watcher.directoryChanged.connect(self.warning_file_change)
        self.simDataItemTreeView.parserThread.newParsedData.connect(
            self.add_files_to_watcher)
        self.show_file_changed_message = True
        self.reset_timer = QTimer(self)
        self.reset_timer.setSingleShot(True)
        self.reset_timer.setInterval(15000)
        self.reset_timer.timeout.connect(self._reset_file_changed_message)

        self.simDataItemTreeView.customContextMenuRequested.connect(
            self.show_sequences_context_menu)
        # self.curveListView.actionCalculateBD.triggered.connect(self.bd_user_generated_curves)

    # sets Visibility for the Plotsettings Widget
    def set_plot_settings_visibility(self):
        self.plotsettings.visibilityChanged.disconnect(
            self.plot_settings_visibility_changed)
        if self.plotsettings.isHidden():
            self.plotsettings.setVisible(True)
        else:
            self.plotsettings.setHidden(True)
        self.plotsettings.visibilityChanged.connect(
            self.plot_settings_visibility_changed)

    # updates the QAction if Visibility is changed
    def plot_settings_visibility_changed(self):
        if self.plotsettings.isHidden():
            self.actionHide_PlotSettings.setChecked(True)
        else:
            self.actionHide_PlotSettings.setChecked(False)
        self._variable_tree_selection_model.selectionChanged.connect(
            self.update_plot)
        self.curveListSelectionModel.selectionChanged.connect(self.update_plot)
        self.simDataItemTreeView.deleteKey.connect(self.remove)

    # sets Visibility for the Sequence Widget
    def set_sequence_widget_visibility(self):
        self.sequenceWidget.visibilityChanged.disconnect(
            self.sequence_widget_visibility_changed)
        if self.sequenceWidget.isHidden():
            self.sequenceWidget.setVisible(True)
        else:
            self.sequenceWidget.setHidden(True)
        self.sequenceWidget.visibilityChanged.connect(
            self.sequence_widget_visibility_changed)

    def sequence_widget_visibility_changed(self):
        if self.sequenceWidget.isHidden():
            self.actionHide_Sequence.setChecked(True)
        else:
            self.actionHide_Sequence.setChecked(False)

    # Sets Visibility for the Status Widget
    def set_status_widget_visibility(self):
        self.statusWidget.visibilityChanged.disconnect(
            self.status_widget_visibility_changed)
        if self.statusWidget.isHidden():
            self.statusWidget.setVisible(True)
        else:
            self.statusWidget.setHidden(True)
        self.statusWidget.visibilityChanged.connect(
            self.status_widget_visibility_changed)

    def status_widget_visibility_changed(self):
        if self.statusWidget.isHidden():
            self.actionHide_Status.setChecked(True)
        else:
            self.actionHide_Status.setChecked(False)

    def curve_widget_visibility_changed(self):
        if self.curveWidget.isHidden():
            self.curveListView.delete_key.disconnect()
        else:
            self.curveListView.delete_key.connect(self.remove_curves)

    def remove(self):
        values = self.selectedSimulationDataItemListModel.values()

        for value in values:
            self.watcher.removePath(value.path)
        # List call necessary to avoid runtime error because of elements changing
        # during iteration
        self._variable_tree_selection_model.selectionChanged.disconnect()
        # disconnect slot to avoid multiple function triggers by selectionChanged signal
        # not disconnecting slows program down significantly
        self._selection_model.selectionChanged.disconnect(self.change_list)
        self.simDataItemTreeModel.remove(list(values))
        self.change_list(QItemSelection(), QItemSelection())
        self._selection_model.selectionChanged.connect(self.change_list)
        self._variable_tree_selection_model.selectionChanged.connect(
            self.update_plot)
        if len(self.selectedSimulationDataItemListModel.values()) == 0:
            self.update_plot()

    def change_list(self, q_selected, q_deselected):
        """Extend superclass behavior by automatically adding the values of
           all selected items in :param: `q_selected` to value list model. """

        selected_q_indexes = q_deselected.indexes()

        q_reselect_indexes = []
        for q_index in self.simDataItemTreeView.selectedIndexes():
            if q_index not in selected_q_indexes:
                q_reselect_indexes.append(q_index)

        # Find all all values that are contained by selected tree items
        tuples = []
        for q_index in q_selected.indexes() + q_reselect_indexes:
            # Add values, ie. sim data items stored at the item, to the list
            # model.
            sim_data_items = q_index.internalPointer().values
            tuples.extend((e.path, e) for e in sim_data_items)

        # Overwrite all elements in dictionary by selected values
        # Note, that overwriting only issues one `updated` signal, and thus,
        # only rerenders the plots one time. Therefore, simply overwriting
        # is much more efficient, despite it would seem, that selectively
        # overwriting keys is.
        self.selectedSimulationDataItemListModel.clear_and_update_from_tuples(
            tuples)

    def get_selected_simulation_data_items(self):
        return [
            self.selectedSimulationDataItemListModel[key]
            for key in self.selectedSimulationDataItemListModel
        ]

    def get_plot_data_collection_from_selected_variables(self):
        """Get a :class: `dict` with y-variable as key and values as items
        from the current selection of the variable tree.

        :rtype: :class: `dict` of :class: `string` and :class: `list`
        """

        plot_data_collection = []
        for q_index in self.variableTreeView.selectedIndexes():
            item = q_index.internalPointer()
            if len(item.values) > 0:
                plot_data_collection.extend(item.values)

        return plot_data_collection

    def update_variable_tree(self):
        """Collect all SimDataItems currently selected, and create variable
        tree and corresponding data from it. Additionaly reselect all previously
        selected variables.
        """

        # Create a list of all currently selected paths
        # Note, that *q_index* can not be used directly, because it might
        # change if the variable tree is recreated
        selected_path_collection = []
        for q_index in self.variableTreeView.selectedIndexes():
            selected_path_collection.append(
                # Create list of identifiers from path
                # Note, that the root node is excluded from the path
                [
                    item.identifier
                    for item in q_index.internalPointer().path[1:]
                ])

        # Join the data of all currently selected items to a dictionary
        # tree
        sim_data_items = self.get_selected_simulation_data_items()
        dict_tree = dict_tree_from_sim_data_items(sim_data_items)
        # check if qp values are the same
        # self.check_qp(sim_data_items)

        # Reset variable tree and update it with *dict_tree*
        self.variableTreeModel.clear_and_update_from_dict_tree(dict_tree)

        # Auto expand variable tree
        self.variableTreeView.expandToDepth(1)

        # Reselect all variables, which also exist on the new tree
        for path in selected_path_collection:
            # Try to reselect, and do nothing, if path does not exist anymore
            try:
                # Reselect new item corresponding to the previously selected
                # path
                item = self.variableTreeModel.get_item_from_path(*path)
                self.variableTreeView.selectionModel().select(
                    self.variableTreeModel._get_index_from_item(item),
                    QItemSelectionModel.Select,
                )
            except KeyError:
                pass

    # TODO: it might be that some log files do not have a QP value, therefore the check_qp method must be
    #       implemented in a way that these files are not affected
    # def check_qp(self, sim_data_items): # check if qp values are the same for each sequence
    #     if len(sim_data_items) < 2: return
    #     sim_data_items.sort(key=lambda item: (item.sequence))
    #
    #     qp_list,list = [],[]
    #     seq = sim_data_items[0].sequence
    #     config = sim_data_items[0].config
    #
    #     for item in sim_data_items:
    #         if ((seq == item.sequence) & (config == item.config)):
    #             list.append(item.qp)
    #         elif (seq == item.sequence): #same sequence different config
    #             config = item.config
    #             qp_list.append(list)
    #             list = []
    #             list.append(item.qp)
    #         else:  # different sequence
    #             seq = item.sequence
    #             config = item.config
    #             qp_list.append(list)
    #             if not(all(list == qp_list[0] for list in qp_list)):
    #                 QtWidgets.QMessageBox.warning(self, "Warning",
    #                                               "Be careful! You chose a sequence with different QP.")
    #                 return
    #             list, qp_list = [], []
    #             list.append(item.qp)
    #     qp_list.append(list)
    #
    #     if not(all(list == qp_list[0] for list in qp_list )):
    #         QtWidgets.QMessageBox.warning(self, "Warning",
    #                                         "Be careful! You chose a sequence with different QP.")

    def check_labels(self):
        selectionmodel = self.variableTreeView.selectionModel()
        selected = self.variableTreeView.selectedIndexes()
        # return if no comparison needed
        if len(selected) < 2:
            return
        labelx = []
        labely = []
        for index in selected:
            x = index.internalPointer()
            if len(x.values) > 0:
                labelx.append(x.values[0].label[0])
                labely.append(x.values[0].label[1])

        if all(x == labelx[0] for x in labelx) and all(x == labely[0]
                                                       for x in labely):
            return

        else:
            QtWidgets.QMessageBox.information(
                self, "Error!",
                "You should not choose curves with different units.")
            selectionmodel.clearSelection()

    # updates the plot if the plot variable is changed
    def update_plot(self):
        # user-generated curves and curves loaded from files are not supposed to be mixed
        user_generated_curves = False
        if self.sender() == self._variable_tree_selection_model or self.sender(
        ) == self.curveListSelectionModel:
            self.check_labels()
            data_collection = self.get_plot_data_collection_from_selected_variables(
            )
            data_collection_user_generated = []
            for index in self.curveListView.selectedIndexes():
                data_collection_user_generated.append(
                    self.curveListModel[index.data()])
        else:
            return

        plot_data_collection = data_collection + data_collection_user_generated
        if len(data_collection_user_generated):
            self.plotPreview.tableView.setModel(self.bdUserGeneratedTableModel)
            self.plotPreview.change_plot(plot_data_collection, True)
        else:
            self.plotPreview.tableView.setModel(self.bdTableModel)
            self.update_table(data_collection)
            self.plotPreview.change_plot(plot_data_collection, False)
        if len(data_collection) and len(data_collection_user_generated):
            # don't mix user-generated and normal curves
            self.plotPreview.tableView.hide()
            self.plotPreview.label_warning.show()
            return

        self.plotPreview.tableView.show()
        self.plotPreview.label_warning.hide()
        self.plotPreview.tableView.model().update(
            plot_data_collection, self.combo_rate_psnr.currentText(),
            self.combo_interp.currentText(),
            not (self.checkBox_bdplot.isChecked()))

    def get_table_header(self, plot_data_collection):
        tmp_legend = []
        tmp_config = []

        # make legend
        for plot_data in plot_data_collection:
            tmp = []
            for identifiers in plot_data.identifiers[1:]:
                tmp += identifiers.split(sep)
            tmp2 = tmp + plot_data.path
            tmp_legend.append(tmp2)
            tmp_config.append(tmp)

        legend = []
        config = []
        for c in tmp_legend:
            result = list(
                filter(lambda x: all(x in l for l in tmp_legend) == False, c))
            if result == []: result = [plot_data.path[-1]]
            legend.append(" ".join(result))
        if len(tmp_legend) == 1:
            legend = [plot_data.path[-1]]

        #make config
        for c in tmp_config:
            result = list(
                filter(lambda x: all(x in l for l in tmp_config) == False, c))
            if ((set([" ".join(result)]) - set(config) != set()) &
                (result != [])):
                config.append(" ".join(result))

        result = (legend, config)
        return result

    #updates the table
    def update_table(self, plot_data_collection):

        self.tableWidget.clear()
        self.tableWidget.setColumnCount(0)
        self.tableWidget.setRowCount(0)

        if plot_data_collection != []:
            if 'Temporal' in plot_data_collection[0].path:
                self.change_table_temporal(plot_data_collection)
            else:
                self.change_table_summary(plot_data_collection)

        self.tableWidget.resizeColumnsToContents()

    def change_table_temporal(self, plot_data_collect):

        plot_data_collection = plot_data_collect
        self.tableWidget.setRowCount(len(plot_data_collection))
        plot_count = data_count = 0
        data_names = []
        plot_data_collection.sort(
            key=lambda plot_data: (plot_data.identifiers))
        legend = self.get_table_header(plot_data_collection)
        header = legend[0]

        for plot_data in plot_data_collection:
            values = ((float(x), float(y)) for (x, y) in plot_data.values)

            sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
            [xs, ys] = list(zip(*sorted_value_pairs))

            # make header
            if plot_data.identifiers[0] not in data_names:
                self.tableWidget.insertRow(plot_count)
                v_item = QtWidgets.QTableWidgetItem(
                    str(plot_data.identifiers[0]))
                font = self.tableWidget.font()
                v_item.setData(
                    6, QtGui.QFont(self.tableWidget.font().setBold(True)))
                v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setVerticalHeaderItem(plot_count, v_item)
                header_count = plot_count
                data_names.append(plot_data.identifiers[0])
                plot_count += 1

            # round data
            if plot_data.label[1] == 'dB':
                ys = tuple(map(lambda i: round(i, 1), ys))

            self.tableWidget.horizontalHeader().setVisible(False)
            # fill up column per column
            for column_count in range(0, len(xs)):

                self.tableWidget.setCurrentCell(plot_count, column_count)
                if column_count > self.tableWidget.currentColumn():
                    self.tableWidget.insertColumn(column_count)
                self.tableWidget.setItem(
                    plot_count, column_count,
                    QtWidgets.QTableWidgetItem(str(ys[column_count])))
                self.tableWidget.setVerticalHeaderItem(
                    plot_count,
                    QtWidgets.QTableWidgetItem(
                        str(header[data_count]) + " [" +
                        str(plot_data.label[1]) + "] "))
                new_item = QtWidgets.QTableWidgetItem(str(xs[column_count]))
                new_item.setData(6, QtGui.QFont("Ubuntu", 11,
                                                QtGui.QFont.Bold))
                self.tableWidget.setItem(header_count, column_count, new_item)

                column_count += 1

            plot_count += 1
            data_count += 1

    def change_table_summary(self, plot_data_collect):

        plot_data_collection = plot_data_collect
        header_count = plot_count = data_count = config_count = column_saver = 0
        data_names = []
        plot_data_collection.sort(key=lambda plot_data: plot_data.path[-1])
        plot_data_collection.sort(
            key=lambda plot_data: plot_data.identifiers[0])
        legend = self.get_table_header(plot_data_collection)
        header = legend[0]
        config = legend[1]

        if ((config == []) | (len(config) == 1)):
            self.change_table_temporal(plot_data_collection)
            return

        self.tableWidget.setRowCount(len(plot_data_collection) / len(config))

        for plot_data in plot_data_collection:

            values = ((float(x), float(y)) for (x, y) in plot_data.values)

            sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
            [xs, ys] = list(zip(*sorted_value_pairs))

            # make header, important if more than one plot
            if plot_data.identifiers[0] not in data_names:
                self.tableWidget.insertRow(plot_count)
                v_item = QtWidgets.QTableWidgetItem(
                    str(plot_data.identifiers[0]))
                v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setVerticalHeaderItem(plot_count, v_item)
                header_count = plot_count
                data_names.append(plot_data.identifiers[0])
                plot_count += 1

            # round data
            if plot_data.label[1] == 'dB':
                ys = tuple(map(lambda i: round(i, 1), ys))

            #horizontal header if more than one config
            if len(config) > 1:
                self.tableWidget.horizontalHeader().setVisible(True)
            else:
                self.tableWidget.horizontalHeader().setVisible(False)

            for column_count in range(0, len(xs)):

                columns = column_saver + column_count
                if (((column_saver + column_count) >=
                     self.tableWidget.columnCount()) |
                    (self.tableWidget.columnCount() == 0)):
                    self.tableWidget.insertColumn(column_saver + column_count)
                if plot_count >= self.tableWidget.rowCount():
                    self.tableWidget.insertRow(plot_count)
                # units in first row of table
                new_item = QtWidgets.QTableWidgetItem(plot_data.label[0] +
                                                      ' | ' +
                                                      plot_data.label[1])
                new_item.setData(6, QtGui.QFont("Ubuntu", 11,
                                                QtGui.QFont.Bold))
                self.tableWidget.setItem(header_count,
                                         column_saver + column_count, new_item)
                #self.tableWidget.setItem(header_count, column_saver + column_count, QtWidgets.QTableWidgetItem(plot_data.label[0] + ' | ' + plot_data.label[1]))
                # x and y-value in one cell
                self.tableWidget.setItem(
                    plot_count, columns,
                    QtWidgets.QTableWidgetItem(
                        str(xs[column_count]) + ' | ' + str(ys[column_count])))
                # header
                self.tableWidget.setHorizontalHeaderItem(
                    column_saver + column_count,
                    QtWidgets.QTableWidgetItem(str(config[config_count])))
                column_count += 1

            if config[config_count] == header[data_count]:
                header[data_count] = header[data_count].replace(
                    config[config_count], plot_data.path[-1])
            elif config[config_count] in header[data_count]:
                header[data_count] = header[data_count].replace(
                    config[config_count], '')

            self.tableWidget.setVerticalHeaderItem(
                plot_count,
                QtWidgets.QTableWidgetItem(str(header[data_count])))
            column_saver = column_saver + column_count
            config_count += 1

            if config_count == len(config):
                plot_count += 1
                column_saver = config_count = 0
            data_count += 1

    def update_bd_table(self, index):
        # update bd table, the index determines the anchor,
        # if it is non integer per default the first config is regarded as
        # anchor

        self.bdTableModel.update_table(self.combo_rate_psnr.currentText(),
                                       self.combo_interp.currentText(), index,
                                       not (self.checkBox_bdplot.isChecked()))

    def update_bd_user_generated_curves_table(self, index):
        clicked_text = self.bdUserGeneratedTableModel.headerData(
            index, Qt.Vertical, Qt.DisplayRole)
        self.bdUserGeneratedTableModel.update(
            None, self.combo_rate_psnr.currentText(),
            self.combo_interp.currentText(),
            not (self.checkBox_bdplot.isChecked()), clicked_text)

    def update_bd_plot(self):
        data_collection = self.get_plot_data_collection_from_selected_variables(
        )
        data_collection_user_generated = []
        for index in self.curveListSelectionModel.selectedIndexes():
            data_collection_user_generated.append(
                self.curveListModel[index.data()])

        if len(data_collection):
            self.bdTableModel.update(data_collection,
                                     self.combo_rate_psnr.currentText(),
                                     self.combo_interp.currentText(),
                                     not (self.checkBox_bdplot.isChecked()))
        elif len(data_collection_user_generated):
            self.bdTableModel.update(data_collection_user_generated,
                                     self.combo_rate_psnr.currentText(),
                                     self.combo_interp.currentText(),
                                     not (self.checkBox_bdplot.isChecked()))

    def export_table_to_csv(self):
        # remember that the decimal mark is '.'
        if self.tableWidget.rowCount() > 0:
            path, extension = QtWidgets.QFileDialog.getSaveFileName(
                self, 'Save Table View as', '.', 'CSV (*.csv)')
            if path != '':
                if '.csv' not in path:
                    path += '.csv'
                with open(str(path), 'w', newline='') as stream:
                    writer = csv.writer(stream)
                    for row in range(self.tableWidget.rowCount()):
                        rowdata = []
                        rowdata.append(
                            str(
                                self.tableWidget.verticalHeaderItem(row).data(
                                    0)))  #data(0) = data(Qt.displayRole)
                        for column in range(self.tableWidget.columnCount()):
                            item = self.tableWidget.item(row, column)
                            if item is not None:
                                rowdata.append(str(item.text()))
                            else:
                                rowdata.append('')
                        writer.writerow(rowdata)

    def save_bd_table(self):
        if self.bdTableModel.rowCount(self) == 0:
            return
        filename, extension = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save Table as', '.', 'Latex (*.tex)')
        if filename != '':
            if '.tex' not in filename:
                filename += '.tex'
            self.bdTableModel.export_to_latex(filename)

    def on_combo_box(self):
        # just update the bd table but do not change the anchor
        self.update_bd_table(-1)

    def save_current_selection(self):
        """Saves the current selected sim data item collection"""
        if not self.get_selected_simulation_data_items():
            msg = QtWidgets.QMessageBox(self)  # use self as parent here
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText(
                "You did not select any simulation data item to store\n"
                "Please make a selection and try again.")
            msg.setWindowTitle("Info")
            msg.show()
            return
        filename, extension = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save RD data as', '.', 'RDPlot (*.rd)')
        if filename != '':
            if '.rd' not in filename:
                filename += '.rd'
            f = open(filename, 'w')
            f.write(
                jsonpickle.encode(self.get_selected_simulation_data_items()))
            f.close()

    def process_cmd_line_args(self, args):
        """Processes cmd line arguments. Those are only pathes or files."""
        for path in args[1:]:
            if not isdir(path) and not isfile(path):
                continue
            if path.endswith('.rd'):
                f = open(path, 'r')
                json_str = f.read()
                sim_data_items = jsonpickle.decode(json_str)
                self.simDataItemTreeModel.update(sim_data_items, False)
                f.close()
                continue

            self.simDataItemTreeView.msg.show()
            self.simDataItemTreeView.parserThread.add_path(path)
            self.simDataItemTreeView.parserThread.start()

    def open_about_page(self):
        """Opens and displays an Html About file"""
        try:
            html_path = path.abspath(here + '/docs/about.html')
            html_file = open(html_path, 'r', encoding='utf-8', errors='ignore')
            source_code = html_file.read()

            try:
                f = open(here + '/version.txt', 'r')
                app_version = f.readline()
            except:
                app_version = 'could not detect version'

            source_code = source_code.replace("##VERSION##", app_version)
            source_code = source_code.replace("##here##", here)
            about_dialog = QtWidgets.QDialog(self)
            about_dialog.setWindowTitle("About RDPlot")
            about_dialog.setMaximumSize(950, 800)
            about_text = QtWidgets.QTextBrowser(about_dialog)
            about_text.setMinimumWidth(950)
            about_text.setMinimumHeight(800)
            about_text.setHtml(source_code)
            about_text.setOpenExternalLinks(True)
            about_text.show()
            about_dialog.exec_()
            about_dialog.close()
            about_text.close()
        except IOError:
            html_error = QtWidgets.QMessageBox()
            html_error.setIcon(QtWidgets.QMessageBox.Critical)
            html_error.setText("Error opening about or help")
            html_error.setInformativeText(
                "The html file from the resource could not be loaded.")
            html_error.exec_()

    def generate_new_curve(self):
        plot_data_collection = self.get_plot_data_collection_from_selected_variables(
        )
        if plot_data_collection:
            new_plot_values = []
            for _plot_data in plot_data_collection:
                new_plot_values.extend(_plot_data.values)
            if len(new_plot_values) < 4:
                QtWidgets.QMessageBox.warning(
                    self, "Warning!", "You didn't select at least 4 points.")
            else:
                curve_name, ok = QtWidgets.QInputDialog.getText(
                    self, "New curve",
                    "Please enter a name for the new curve.\n"
                    "If you enter an already existing name,\nits data will be overwritten."
                )
                curve_name = curve_name.strip()
                if curve_name is not '':
                    new_plot_data = PlotData([curve_name], new_plot_values, [],
                                             plot_data_collection[0].label)
                    self.add_curve(curve_name, new_plot_data)
                else:
                    QtWidgets.QMessageBox.warning(
                        self, "Warning!", "Please enter a valid name.")
        else:
            QtWidgets.QMessageBox.warning(
                self, "Warning!", "You didn't select at least 4 points.")

    def add_curve(self, name, data):
        if self.curveWidget.isHidden():
            self.curveWidget.show()
        data_tuple = (name, data)
        self.curveListModel.update_from_tuples((data_tuple, ))
        # all_indexes = QItemSelection(self.curveListModel.index(0),
        #                             self.curveListModel.index(self.curveListModel.rowCount(QModelIndex())))
        # self.curveListSelectionModel.select(all_indexes, QItemSelectionModel.Clear)
        # self.curveListSelectionModel.select(self.curveListModel.index(self.curveListModel.rowCount(QModelIndex())-1),
        #                                    QItemSelectionModel.Select)
        # self.curveListView.setFocus()

    def remove_curves(self):
        # todo integrate bjontegaard for generated curves(should be fully functional already)
        curves_to_remove = []
        for index in self.curveListSelectionModel.selectedIndexes():
            curves_to_remove.append(index.data())
        self.curveListModel.remove_keys(curves_to_remove)
        if len(self.curveListModel) > 0:
            self.curveListSelectionModel.select(self.curveListModel.index(0),
                                                QItemSelectionModel.Select)
        else:
            self.curveWidget.hide()
            self.update_plot()

    def get_recent_files(self):
        recent_files = self.settings.value('recentFiles')
        if recent_files is not None:
            for recent_file in recent_files:
                if path.exists(recent_file):
                    action = self.menuRecent_files.addAction(recent_file)
                    action.triggered.connect(self.open_recent_file)

    def open_recent_file(self):
        path_recent = self.sender().text()
        if path.isdir(path_recent):
            self.simDataItemTreeView.add_folder(path_recent)
        else:
            self.simDataItemTreeView.add_file(path_recent)

    def add_recent_files(self, files, reload):
        # files doesn't necessarily have to just be a list of files
        # it can also be a directory
        if not reload:
            recent_files = self.settings.value('recentFiles')
            if recent_files is None:
                recent_files = []
            for file in files:

                if file in recent_files:
                    # put our file on top of the list
                    recent_files.remove(file)
                recent_files.insert(0, file)
            while len(recent_files) > 5:
                del recent_files[-1]
            self.settings.setValue('recentFiles', recent_files)

            self.menuRecent_files.clear()
            for recent_file in recent_files:
                if path.exists(recent_file):
                    action = self.menuRecent_files.addAction(recent_file)
                    action.triggered.connect(self.open_recent_file)

    def add_files_to_watcher(self, items):
        for item in items:
            if isfile(item.path):
                self.watcher.addPath(item.path)

    def warning_file_change(self, path_item):
        # inform user about the fact that one of the loaded files has been changed since the application has started
        # timer is used to avoid spamming the user when multiple files are deleted in a row
        # retrieve affected notes and parent nodes
        # change their style in the tree view to indicate which files are affected
        if self.show_file_changed_message:
            self.show_file_changed_message = False
            self.reset_timer.start()
            QtWidgets.QMessageBox.warning(
                self, 'File change',
                'One or more of your loaded files have been changed.\n'
                'You can choose to reload them.\n'
                'Hint: Changed files are greyed out in the sequences widget.')
        else:
            self.reset_timer.stop()
            self.reset_timer.start()

        affected_notes = []
        for leaf in self.simDataItemTreeModel.root.leafs:
            for value in leaf.values:
                if value.path == path_item:
                    affected_notes.append(leaf)
        for node in affected_notes:
            node_index = self.simDataItemTreeModel._get_index_from_item(node)
            node.setProperty('needs_reload', 'True')
            parent = self.simDataItemTreeModel.parent(node_index)
            level = 0
            while parent.isValid() and level < 2:  #MAX_LEVEL
                parent.internalPointer().setProperty('needs_reload', 'True')
                parent = self.simDataItemTreeModel.parent(parent)
                level += 1

    def _reset_file_changed_message(self):
        self.show_file_changed_message = True

    def reload_files(self):
        # remove all selected files first
        # reload available files
        # could possibly limit this to only files that we know have been changed
        def check_children(parent):
            if len(parent.children) > 0:
                for child in parent.children:
                    if not check_children(child):
                        return False
                parent.setProperty('needs_reload', 'False')
                return True
            else:
                if parent.property('needs_reload') == 'True':
                    return False
                return True

        values = self.selectedSimulationDataItemListModel.values()
        if len(values) == 0:
            for index in self.simDataItemTreeModel.root.leafs:
                for sim_data_item in index.values:
                    values.append(sim_data_item)
        items_to_be_reloaded = []
        for value in values:
            if path.exists(value.path):
                # reload file
                items_to_be_reloaded.append(value)

        self._variable_tree_selection_model.selectionChanged.disconnect()
        self._selection_model.selectionChanged.disconnect(self.change_list)
        self.simDataItemTreeModel.remove(values)

        self.simDataItemTreeView.msg.show()
        for item in items_to_be_reloaded:
            self.simDataItemTreeView.add_file(item.path, reload=True)

        self.change_list(QItemSelection(), QItemSelection())
        self._selection_model.selectionChanged.connect(self.change_list)
        self._variable_tree_selection_model.selectionChanged.connect(
            self.update_plot)

        for node in self.simDataItemTreeModel.root.children:
            # remove grey font color if all changed files have been reloaded
            # have to check every single item because possible deletion of older nodes makes things very difficult
            check_children(node)

    def show_sequences_context_menu(self, position):
        self.menuEdit.exec(self.simDataItemTreeView.mapToGlobal(position))
Ejemplo n.º 3
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, ):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.fig_dict = {}

        self.tabWidget.setCurrentIndex(0)

        self.plotAreaVerticalLayout = QtWidgets.QVBoxLayout()
        self.plotsFrame.setLayout(self.plotAreaVerticalLayout)

        #initialize Table
        self.headerV = self.tableWidget.verticalHeader()
        self.headerV.show()

        # add a widget for previewing plots, they can then be added to the actual plot
        self.plotPreview = PlotWidget()
        self.plotAreaVerticalLayout.addWidget(self.plotPreview)

        # Create tree model to store sim data items and connect it to views
        self.simDataItemTreeModel = SimDataItemTreeModel()
        self.bdTableModel = BdTableModel()
        self.simDataItemTreeView.setModel(self.simDataItemTreeModel)
        self.plotPreview.tableView.setModel(self.bdTableModel)

        # connect a double clicked section of the bd table to a change of the anchor
        self.plotPreview.tableView.horizontalHeader().sectionDoubleClicked.connect(self.update_bd_table)

        # Set custom selection model, so that sub items are automatically
        # selected if parent is selected
        self._selection_model = QRecursiveSelectionModel(self.simDataItemTreeView.model())
        self.simDataItemTreeView.setSelectionModel(self._selection_model)

        # Connect list view with model for the selected values of tree view
        self.selectedSimulationDataItemListModel = OrderedDictModel()
        self.simDataItemListView.setModel(self.selectedSimulationDataItemListModel)
        self._selection_model.selectionChanged.connect(self.change_list)

        # set up signals and slots
        self.selectedSimulationDataItemListModel.items_changed.connect(
            self.update_variable_tree
        )

        # Connect signals of menus
        self.actionOpen_File.triggered.connect(
            self.simDataItemTreeView.add_file
        )
        self.actionOpen_Directory.triggered.connect(
            self.simDataItemTreeView.add_folder
        )
        self.actionOpen_Directory_List.triggered.connect(
            self.simDataItemTreeView.add_folder_list
        )
        self.actionHide_PlotSettings.triggered.connect(
            self.set_plot_settings_visibility
        )
        self.actionHide_Sequence.triggered.connect(
            self.set_sequence_widget_visibility
        )
        self.actionHide_Status.triggered.connect(
            self.set_status_widget_visibility
        )

        self.actionSave_Table.triggered.connect(
            self.save_bd_table
        )

        self.actionExport_Figure_as_Tikzpicture.triggered.connect(
            self.plotPreview.export_plot_tikz
        )

        self.actionExport_TableWidget.triggered.connect(
            self.export_table_to_csv
        )

        self.actionSave_Data.triggered.connect(
            self.save_current_selection
        )

        self.action_About.triggered.connect(
            self.open_about_page
        )

        self.variableTreeModel = VariableTreeModel()
        self.variableTreeView.setModel(self.variableTreeModel)
        self.plotsettings.visibilityChanged.connect(self.plot_settings_visibility_changed)
        self.sequenceWidget.visibilityChanged.connect(self.sequence_widget_visibility_changed)
        self.statusWidget.visibilityChanged.connect(self.status_widget_visibility_changed)

        # Set recursive selection model for variable view
        self._variable_tree_selection_model = QRecursiveSelectionModel(
            self.variableTreeView.model()
        )
        self.variableTreeView.setSelectionModel(
            self._variable_tree_selection_model
        )

        # set up combo boxes for rate/psnr and interpolation options
        self.combo_interp.addItems(["pchip", "pol"])
        self.combo_rate_psnr.addItems(["drate", "dsnr"])
        self.combo_interp.currentIndexChanged.connect(self.on_combo_box)
        self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box)

        # set up bd plot checkbox
        self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot)

    # sets Visibility for the Plotsettings Widget
    def set_plot_settings_visibility(self):
        self.plotsettings.visibilityChanged.disconnect(self.plot_settings_visibility_changed)
        if self.plotsettings.isHidden():
            self.plotsettings.setVisible(True)
        else:
            self.plotsettings.setHidden(True)
        self.plotsettings.visibilityChanged.connect(self.plot_settings_visibility_changed)

    # updates the QAction if Visibility is changed
    def plot_settings_visibility_changed(self):
        if self.plotsettings.isHidden():
            self.actionHide_PlotSettings.setChecked(True)
        else:
            self.actionHide_PlotSettings.setChecked(False)
        self._variable_tree_selection_model.selectionChanged.connect(self.update_plot)

        self.simDataItemTreeView.deleteKey.connect(self.remove)

    # sets Visibility for the Sequence Widget
    def set_sequence_widget_visibility(self):
        self.sequenceWidget.visibilityChanged.disconnect(self.sequence_widget_visibility_changed)
        if self.sequenceWidget.isHidden():
            self.sequenceWidget.setVisible(True)
        else:
            self.sequenceWidget.setHidden(True)
        self.sequenceWidget.visibilityChanged.connect(self.sequence_widget_visibility_changed)

    def sequence_widget_visibility_changed(self):
        if self.sequenceWidget.isHidden():
            self.actionHide_Sequence.setChecked(True)
        else:
            self.actionHide_Sequence.setChecked(False)

    # Sets Visibility for the Status Widget
    def set_status_widget_visibility(self):
        self.statusWidget.visibilityChanged.disconnect(self.status_widget_visibility_changed)
        if self.statusWidget.isHidden():
            self.statusWidget.setVisible(True)
        else:
            self.statusWidget.setHidden(True)
        self.statusWidget.visibilityChanged.connect(self.status_widget_visibility_changed)

    def status_widget_visibility_changed(self):
        if self.statusWidget.isHidden():
            self.actionHide_Status.setChecked(True)
        else:
            self.actionHide_Status.setChecked(False)

    def remove(self):
        values = self.selectedSimulationDataItemListModel.values()
        # List call necessary to avoid runtime error because of elements changing
        # during iteration
        self._variable_tree_selection_model.selectionChanged.disconnect()
        self.simDataItemTreeModel.remove(list(values))
        self._variable_tree_selection_model.selectionChanged.connect(self.update_plot)

    def change_list(self, q_selected, q_deselected):
        """Extend superclass behavior by automatically adding the values of
           all selected items in :param: `q_selected` to value list model. """

        selected_q_indexes = q_deselected.indexes()

        q_reselect_indexes = []
        for q_index in self.simDataItemTreeView.selectedIndexes():
            if q_index not in selected_q_indexes:
                q_reselect_indexes.append(q_index)

        # Find all all values that are contained by selected tree items
        tuples = []
        for q_index in q_selected.indexes() + q_reselect_indexes:
            # Add values, ie. sim data items stored at the item, to the list
            # model.
            sim_data_items = q_index.internalPointer().values
            tuples.extend((e.path, e) for e in sim_data_items)

        # Overwrite all elements in dictionary by selected values
        # Note, that overwriting only issues one `updated` signal, and thus,
        # only rerenders the plots one time. Therefore, simply overwriting
        # is much more efficient, despite it would seem, that selectively
        # overwriting keys is.
        self.selectedSimulationDataItemListModel.clear_and_update_from_tuples(tuples)

    def get_selected_simulation_data_items(self):
        return [self.selectedSimulationDataItemListModel[key] for key in self.selectedSimulationDataItemListModel]

    def get_plot_data_collection_from_selected_variables(self):
        """Get a :class: `dict` with y-variable as key and values as items
        from the current selection of the variable tree.

        :rtype: :class: `dict` of :class: `string` and :class: `list`
        """

        plot_data_collection = []
        for q_index in self.variableTreeView.selectedIndexes():
            item = q_index.internalPointer()
            if len(item.values) > 0:
                plot_data_collection.extend(item.values)

        return plot_data_collection

    def update_variable_tree(self):
        """Collect all SimDataItems currently selected, and create variable
        tree and corresponding data from it. Additionaly reselect all previously
        selected variables.
        """

        # Create a list of all currently selected paths
        # Note, that *q_index* can not be used directly, because it might
        # change if the variable tree is recreated
        selected_path_collection = []
        for q_index in self.variableTreeView.selectedIndexes():
            selected_path_collection.append(
                # Create list of identifiers from path
                # Note, that the root node is excluded from the path
                [item.identifier for item in q_index.internalPointer().path[1:]]
            )

        # Join the data of all currently selected items to a dictionary
        # tree
        sim_data_items = self.get_selected_simulation_data_items()
        dict_tree = dict_tree_from_sim_data_items(sim_data_items)
        # check if qp values are the same
        # self.check_qp(sim_data_items)

        # Reset variable tree and update it with *dict_tree*
        self.variableTreeModel.clear_and_update_from_dict_tree(dict_tree)

        # Auto expand variable tree
        self.variableTreeView.expandToDepth(1)

        # Reselect all variables, which also exist on the new tree
        for path in selected_path_collection:
            # Try to reselect, and do nothing, if path does not exist anymore
            try:
                # Reselect new item corresponding to the previously selected
                # path
                item = self.variableTreeModel.get_item_from_path(*path)
                self.variableTreeView.selectionModel().select(
                    self.variableTreeModel._get_index_from_item(item),
                    QItemSelectionModel.Select,
                )
            except KeyError:
                pass

    # TODO: it might be that some log files do not have a QP value, therefore the check_qp method must be
    #       implemented in a way that these files are not affected
    # def check_qp(self, sim_data_items): # check if qp values are the same for each sequence
    #     if len(sim_data_items) < 2: return
    #     sim_data_items.sort(key=lambda item: (item.sequence))
    #
    #     qp_list,list = [],[]
    #     seq = sim_data_items[0].sequence
    #     config = sim_data_items[0].config
    #
    #     for item in sim_data_items:
    #         if ((seq == item.sequence) & (config == item.config)):
    #             list.append(item.qp)
    #         elif (seq == item.sequence): #same sequence different config
    #             config = item.config
    #             qp_list.append(list)
    #             list = []
    #             list.append(item.qp)
    #         else:  # different sequence
    #             seq = item.sequence
    #             config = item.config
    #             qp_list.append(list)
    #             if not(all(list == qp_list[0] for list in qp_list)):
    #                 QtWidgets.QMessageBox.warning(self, "Warning",
    #                                               "Be careful! You chose a sequence with different QP.")
    #                 return
    #             list, qp_list = [], []
    #             list.append(item.qp)
    #     qp_list.append(list)
    #
    #     if not(all(list == qp_list[0] for list in qp_list )):
    #         QtWidgets.QMessageBox.warning(self, "Warning",
    #                                         "Be careful! You chose a sequence with different QP.")

    def check_labels(self):
        selectionmodel = self.variableTreeView.selectionModel()
        selected = self.variableTreeView.selectedIndexes()
        # return if no comparison needed
        if len(selected) < 2:
            return
        labelx = []
        labely = []
        for index in selected:
            x = index.internalPointer()
            if len(x.values) > 0:
                labelx.append(x.values[0].label[0])
                labely.append(x.values[0].label[1])

        if all(x == labelx[0] for x in labelx) and all(x == labely[0] for x in labely):
            return

        else:
            QtWidgets.QMessageBox.information(self, "Error!",
                                              "You should not choose curves with different units.")
            selectionmodel.clearSelection()

    # updates the plot if the plot variable is changed
    def update_plot(self):
        self.check_labels()
        plot_data_collection = self.get_plot_data_collection_from_selected_variables()

        self.plotPreview.change_plot(plot_data_collection)
        self.update_table(plot_data_collection)

        # update the model for the bd table, note the anchor is always
        # the first config if new simDataItems are selected
        self.bdTableModel.update(plot_data_collection, self.combo_rate_psnr.currentText(),
                                 self.combo_interp.currentText(), not(self.checkBox_bdplot.isChecked()))

    def get_table_header(self, plot_data_collection):
        tmp_legend = []
        tmp_config = []

        # make legend
        for plot_data in plot_data_collection:
            tmp = []
            for identifiers in plot_data.identifiers[1:]:
                tmp += identifiers.split(sep)
            tmp2 = tmp + plot_data.path
            tmp_legend.append(tmp2)
            tmp_config.append(tmp)

        legend = []
        config = []
        for c in tmp_legend:
            result = list(filter(lambda x: all(x in l for l in tmp_legend) == False, c))
            if result == []: result = [plot_data.path[-1]]
            legend.append(" ".join(result))
        if len(tmp_legend) == 1:
            legend = [plot_data.path[-1]]

        #make config
        for c in tmp_config:
            result = list(filter(lambda x: all(x in l for l in tmp_config) == False, c))
            if ((set([" ".join(result)]) - set(config) != set()) & (result != [])): config.append(" ".join(result))

        result = (legend, config)
        return result

    #updates the table
    def update_table(self,plot_data_collection):

        self.tableWidget.clear()
        self.tableWidget.setColumnCount(0)
        self.tableWidget.setRowCount(0)

        if plot_data_collection != []:
            if 'Temporal' in plot_data_collection[0].path: self.change_table_temporal(plot_data_collection)
            else: self.change_table_summary(plot_data_collection)

        self.tableWidget.resizeColumnsToContents()

    def change_table_temporal(self, plot_data_collect):

        plot_data_collection = plot_data_collect
        self.tableWidget.setRowCount(len(plot_data_collection))
        plot_count = data_count = 0
        data_names = []
        plot_data_collection.sort(key=lambda plot_data: (plot_data.identifiers))
        legend = self.get_table_header(plot_data_collection)
        header = legend[0]

        for plot_data in plot_data_collection:
            values = ((float(x), float(y)) for (x, y) in plot_data.values)

            sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
            [xs, ys] = list(zip(*sorted_value_pairs))

            # make header
            if plot_data.identifiers[0] not in data_names:
                self.tableWidget.insertRow(plot_count)
                v_item = QtWidgets.QTableWidgetItem(str(plot_data.identifiers[0]))
                font = self.tableWidget.font()
                v_item.setData(6, QtGui.QFont(self.tableWidget.font().setBold(True)))
                v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setVerticalHeaderItem(plot_count, v_item)
                header_count = plot_count
                data_names.append(plot_data.identifiers[0])
                plot_count += 1

            # round data
            if plot_data.label[1] == 'dB':
                ys = tuple(map(lambda i: round(i, 1), ys))

            self.tableWidget.horizontalHeader().setVisible(False)
            # fill up column per column
            for column_count in range(0, len(xs)):

                self.tableWidget.setCurrentCell(plot_count, column_count)
                if column_count > self.tableWidget.currentColumn():   self.tableWidget.insertColumn(column_count)
                self.tableWidget.setItem(plot_count, column_count, QtWidgets.QTableWidgetItem(str(ys[column_count])))
                self.tableWidget.setVerticalHeaderItem(plot_count, QtWidgets.QTableWidgetItem(str(header[data_count])+ " [" + str(plot_data.label[1]) + "] "))
                new_item = QtWidgets.QTableWidgetItem(str(xs[column_count]))
                new_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setItem(header_count, column_count, new_item)

                column_count += 1

            plot_count += 1
            data_count += 1

    def change_table_summary(self, plot_data_collect):

        plot_data_collection = plot_data_collect
        header_count = plot_count = data_count = config_count = column_saver = 0
        data_names = []
        plot_data_collection.sort(key=lambda plot_data: plot_data.path[-1])
        plot_data_collection.sort(key=lambda plot_data: plot_data.identifiers[0])
        legend = self.get_table_header(plot_data_collection)
        header = legend[0]
        config = legend[1]

        if ((config == []) | (len(config) == 1)):
            self.change_table_temporal(plot_data_collection)
            return

        self.tableWidget.setRowCount(len(plot_data_collection)/len(config))

        for plot_data in plot_data_collection:

            values = ((float(x), float(y)) for (x, y) in plot_data.values)

            sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
            [xs, ys] = list(zip(*sorted_value_pairs))

            # make header, important if more than one plot
            if plot_data.identifiers[0] not in data_names:
                self.tableWidget.insertRow(plot_count)
                v_item = QtWidgets.QTableWidgetItem(str(plot_data.identifiers[0]))
                v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setVerticalHeaderItem(plot_count, v_item)
                header_count = plot_count
                data_names.append(plot_data.identifiers[0])
                plot_count += 1

            # round data
            if plot_data.label[1] == 'dB':
                ys = tuple(map(lambda i: round(i, 1), ys))

            #horizontal header if more than one config
            if len(config) > 1: self.tableWidget.horizontalHeader().setVisible(True)
            else: self.tableWidget.horizontalHeader().setVisible(False)

            for column_count in range(0, len(xs)):

                columns = column_saver + column_count
                if (((column_saver+column_count) >= self.tableWidget.columnCount()) | (self.tableWidget.columnCount()==0) ):
                    self.tableWidget.insertColumn(column_saver + column_count)
                if plot_count >= self.tableWidget.rowCount():
                    self.tableWidget.insertRow(plot_count)
                # units in first row of table
                new_item = QtWidgets.QTableWidgetItem(plot_data.label[0] + ' | ' + plot_data.label[1])
                new_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setItem(header_count, column_saver + column_count, new_item)
                #self.tableWidget.setItem(header_count, column_saver + column_count, QtWidgets.QTableWidgetItem(plot_data.label[0] + ' | ' + plot_data.label[1]))
                # x and y-value in one cell
                self.tableWidget.setItem(plot_count, columns, QtWidgets.QTableWidgetItem(str(xs[column_count]) + ' | ' + str(ys[column_count])))
                # header
                self.tableWidget.setHorizontalHeaderItem(column_saver+column_count, QtWidgets.QTableWidgetItem(str(config[config_count])))
                column_count += 1

            if config[config_count] == header[data_count]: header[data_count] =  header[data_count].replace(config[config_count], plot_data.path[-1])
            elif config[config_count] in header[data_count]: header[data_count] = header[data_count].replace(config[config_count], '')

            self.tableWidget.setVerticalHeaderItem(plot_count, QtWidgets.QTableWidgetItem(str(header[data_count])))
            column_saver = column_saver + column_count
            config_count += 1

            if config_count == len(config):
                plot_count += 1
                column_saver = config_count = 0
            data_count += 1

    def update_bd_table(self, index):
        # update bd table, the index determines the anchor,
        # if it is non integer per default the first config is regarded as
        # anchor
        self.bdTableModel.update_table(self.combo_rate_psnr.currentText(),
                                           self.combo_interp.currentText(), index, not(self.checkBox_bdplot.isChecked()))
    def update_bd_plot(self):
        plot_data_collection = self.get_plot_data_collection_from_selected_variables()
        self.bdTableModel.update(plot_data_collection, self.combo_rate_psnr.currentText(),
                                 self.combo_interp.currentText(), not (self.checkBox_bdplot.isChecked()))

    def export_table_to_csv(self):
        # remember that the decimal mark is '.'
        if self.tableWidget.rowCount() > 0:
            path = QtWidgets.QFileDialog.getSaveFileName(self, 'Save Table View as', '.', '*.csv')
            path = path[0] + path[1][1:]
            if len(path) > 0:
                with open(str(path), 'w', newline='') as stream:
                    writer = csv.writer(stream)
                    for row in range(self.tableWidget.rowCount()):
                        rowdata = []
                        rowdata.append(str(self.tableWidget.verticalHeaderItem(row).data(0))) #data(0) = data(Qt.displayRole)
                        for column in range(self.tableWidget.columnCount()):
                            item = self.tableWidget.item(row, column)
                            if item is not None:
                                rowdata.append(str(item.text()))
                            else:
                                rowdata.append('')
                        writer.writerow(rowdata)

    def save_bd_table(self):
        if self.bdTableModel.rowCount(self) == 0:
            return
        filename = QtWidgets.QFileDialog.getSaveFileName(self, 'Save Table as', '.', 'tex')
        filename = '.'.join(filename)
        if not len(filename) == 0:
            self.bdTableModel.export_to_latex(filename)

    def on_combo_box(self):
        # just update the bd table but do not change the anchor
        self.update_bd_table(-1)

    def save_current_selection(self):
        """Saves the current selected sim data item collection"""
        if not self.get_selected_simulation_data_items():
            msg = QtWidgets.QMessageBox(self)  # use self as parent here
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText("You did not select any simulation data item to store\n"
                        "Please make a selection and try again.")
            msg.setWindowTitle("Info")
            msg.show()
            return
        filename = QtWidgets.QFileDialog.getSaveFileName(self, 'Save RD data as', '.', '*.rd')
        filename = filename[0] + filename[1][1:]
        if not len(filename) == 0:
            f = open(filename, 'w')
            f.write(jsonpickle.encode(self.get_selected_simulation_data_items()))
            f.close()

    def process_cmd_line_args(self, args):
        """Processes cmd line arguments. Those are only pathes or files."""
        for path in args[1:]:
            if not isdir(path) and not isfile(path):
                continue
            if path.endswith('.rd'):
                f = open(path, 'r')
                json_str = f.read()
                sim_data_items = jsonpickle.decode(json_str)
                self.simDataItemTreeModel.update(sim_data_items,False)
                f.close()
                continue

            self.simDataItemTreeView.msg.show()
            self.simDataItemTreeView.parserThread.add_path(path)
            self.simDataItemTreeView.parserThread.start()

    def open_about_page(self):
        """Opens and displays an Html About file"""
        try:
            html_path = path.abspath(here + '/docs/about.html')
            html_file = open(html_path, 'r', encoding='utf-8', errors='ignore')
            source_code = html_file.read()

            try:
                f = open(here + '/version.txt', 'r')
                app_version = f.readline()
            except:
                app_version = 'could not detect version'

            source_code = source_code.replace("##VERSION##", app_version)
            source_code = source_code.replace("##here##", here)
            about_dialog = QtWidgets.QDialog(self)
            about_dialog.setWindowTitle("About RDPlot")
            about_dialog.setMaximumSize(950, 800)
            about_text = QtWidgets.QTextBrowser(about_dialog)
            about_text.setMinimumWidth(950)
            about_text.setMinimumHeight(800)
            about_text.setHtml(source_code)
            about_text.setOpenExternalLinks(True)
            about_text.show()
            about_dialog.exec_()
            about_dialog.close()
            about_text.close()
        except IOError:
            html_error = QtWidgets.QMessageBox()
            html_error.setIcon(QtWidgets.QMessageBox.Critical)
            html_error.setText("Error opening about or help")
            html_error.setInformativeText("The html file from the resource could not be loaded.")
            html_error.exec_()
Ejemplo n.º 4
0
    def __init__(self, ):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.fig_dict = {}

        self.tabWidget.setCurrentIndex(0)

        self.plotAreaVerticalLayout = QtWidgets.QVBoxLayout()
        self.plotsFrame.setLayout(self.plotAreaVerticalLayout)

        #initialize Table
        self.headerV = self.tableWidget.verticalHeader()
        self.headerV.show()

        # add a widget for previewing plots, they can then be added to the actual plot
        self.plotPreview = PlotWidget()
        self.plotAreaVerticalLayout.addWidget(self.plotPreview)

        # Create tree model to store sim data items and connect it to views
        self.simDataItemTreeModel = SimDataItemTreeModel()
        self.bdTableModel = BdTableModel()
        self.bdUserGeneratedTableModel = BdUserGeneratedCurvesTableModel()
        self.simDataItemTreeView.setModel(self.simDataItemTreeModel)
        self.plotPreview.tableView.setModel(self.bdTableModel)

        # connect a double clicked section of the bd table to a change of the anchor
        self.plotPreview.tableView.horizontalHeader(
        ).sectionDoubleClicked.connect(
            self.update_doubleClicked_horizontalHeader)
        self.plotPreview.tableView.verticalHeader(
        ).sectionDoubleClicked.connect(
            self.update_bd_user_generated_curves_table)

        # Set custom selection model, so that sub items are automatically
        # selected if parent is selected
        self._selection_model = QRecursiveSelectionModel(
            self.simDataItemTreeView.model())
        self.simDataItemTreeView.setSelectionModel(self._selection_model)

        # Connect list view with model for the selected values of tree view
        self.selectedSimulationDataItemListModel = OrderedDictModel()
        self.simDataItemListView.setModel(
            self.selectedSimulationDataItemListModel)
        self._selection_model.selectionChanged.connect(self.change_list)

        # set up signals and slots
        self.selectedSimulationDataItemListModel.items_changed.connect(
            self.update_variable_tree)

        # Connect signals of menus
        self.actionOpen_File.triggered.connect(
            self.simDataItemTreeView.add_file)
        self.actionOpen_Directory.triggered.connect(
            self.simDataItemTreeView.add_folder)
        self.actionOpen_Directory_List.triggered.connect(
            self.simDataItemTreeView.add_folder_list)
        self.actionHide_PlotSettings.triggered.connect(
            self.set_plot_settings_visibility)
        self.actionHide_Sequence.triggered.connect(
            self.set_sequence_widget_visibility)
        self.actionHide_Status.triggered.connect(
            self.set_status_widget_visibility)

        self.actionSave_Table.triggered.connect(self.save_bd_table)

        self.actionExport_Figure_as_Tikzpicture.triggered.connect(
            self.plotPreview.export_plot_tikz)

        self.actionExport_TableWidget.triggered.connect(
            self.export_table_to_csv)

        self.actionSave_Data.triggered.connect(self.save_current_selection)

        self.action_About.triggered.connect(self.open_about_page)

        self.variableTreeModel = VariableTreeModel()
        self.variableTreeView.setModel(self.variableTreeModel)
        self.plotsettings.visibilityChanged.connect(
            self.plot_settings_visibility_changed)
        self.sequenceWidget.visibilityChanged.connect(
            self.sequence_widget_visibility_changed)
        self.statusWidget.visibilityChanged.connect(
            self.status_widget_visibility_changed)

        # Set recursive selection model for variable view
        self._variable_tree_selection_model = QRecursiveSelectionModel(
            self.variableTreeView.model())
        self.variableTreeView.setSelectionModel(
            self._variable_tree_selection_model)

        # set up combo boxes for rate/psnr and interpolation options
        self.combo_interp.addItems(["pchip", "pol"])
        self.combo_rate_psnr.addItems(["drate", "dsnr"])
        self.combo_ci.addItems(["average", "worst", "best"])
        self.combo_interp.currentIndexChanged.connect(self.on_combo_box)
        self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box)
        self.combo_ci.currentIndexChanged.connect(self.on_ci_combo_box)
        self.combo_ci.setEnabled(False)

        # set up bd plot checkbox
        self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot)

        self.curveWidget.hide()
        self.curveListModel = OrderedDictModel()
        self.curveListView.setModel(self.curveListModel)
        self.curveListSelectionModel = QItemSelectionModel(self.curveListModel)
        self.curveListView.setSelectionModel(self.curveListSelectionModel)
        self.curveListView.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.curveWidget.visibilityChanged.connect(
            self.curve_widget_visibility_changed)

        self.actionGenerate_curve.triggered.connect(self.generate_new_curve)
        self.actionRemove_items.triggered.connect(self.remove)
        self.actionReload_files.triggered.connect(self.reload_files)

        self.settings = QSettings()
        self.get_recent_files()
        self.simDataItemTreeView.itemsOpened.connect(self.add_recent_files)

        self.watcher = QFileSystemWatcher(self)
        self.watcher.fileChanged.connect(self.warning_file_change)
        self.watcher.directoryChanged.connect(self.warning_file_change)
        self.simDataItemTreeView.parserThread.newParsedData.connect(
            self.add_files_to_watcher)
        self.show_file_changed_message = True
        self.reset_timer = QTimer(self)
        self.reset_timer.setSingleShot(True)
        self.reset_timer.setInterval(15000)
        self.reset_timer.timeout.connect(self._reset_file_changed_message)

        self.simDataItemTreeView.customContextMenuRequested.connect(
            self.show_sequences_context_menu)
        # self.curveListView.actionCalculateBD.triggered.connect(self.bd_user_generated_curves)

        # Set status Widget invisible as default
        self.statusWidget.setVisible(False)