コード例 #1
0
ファイル: menu_actions.py プロジェクト: saraemp/straditize
    def __init__(self, df, straditizer, fname=None, *args, **kwargs):
        """
        Parameters
        ----------
        df: pandas.DataFrame
            The DataFrame to be exported
        straditizer: straditize.straditizer.Straditizer
            The source straditizer
        fname: str
            The file name to export to
        """
        super().__init__(*args, **kwargs)
        self.df = df
        self.stradi = straditizer
        self.txt_fname = QLineEdit()
        self.bt_open_file = QToolButton()
        self.bt_open_file.setIcon(QIcon(get_icon('run_arrow.png')))
        self.bt_open_file.setToolTip('Select the export file on your drive')

        self.cb_include_meta = QCheckBox('Include meta data')
        self.cb_include_meta.setChecked(True)

        self.bbox = bbox = QDialogButtonBox(QDialogButtonBox.Ok
                                            | QDialogButtonBox.Cancel)

        # ---------------------------------------------------------------------
        # --------------------------- Layouts ---------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Export to:'))
        hbox.addWidget(self.txt_fname)
        hbox.addWidget(self.bt_open_file)
        vbox.addLayout(hbox)

        vbox.addWidget(self.cb_include_meta)

        vbox.addWidget(bbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # --------------------------- Connections -----------------------------
        # ---------------------------------------------------------------------
        bbox.accepted.connect(self._export)
        bbox.rejected.connect(self.reject)
        self.bt_open_file.clicked.connect(self.get_open_file_name)

        if fname is not None:
            self.txt_fname.setText(fname)
            self._export()
コード例 #2
0
    def __init__(self, straditizer_widgets, item=None, *args, **kwargs):
        """
        Parameters
        ----------
        %(StraditizerControlBase.init_straditizercontrol.parameters)s"""
        super(ImageRotator, self).__init__(*args, **kwargs)
        self.txt_rotate = QLineEdit()
        self.txt_rotate.setValidator(QDoubleValidator())

        self.btn_rotate_horizontal = QPushButton('Horizontal alignment')
        self.btn_rotate_horizontal.setToolTip(
            'Mark two points that should be on the same horizontal level '
            'and rotate the picture to achieve this.')

        self.btn_rotate_vertical = QPushButton('Vertical alignment')
        self.btn_rotate_vertical.setToolTip(
            'Mark two points that should be on the same vertical level '
            'and rotate the picture to achieve this.')

        self.init_straditizercontrol(straditizer_widgets, item)

        # ---------------------------------------------------------------------
        # --------------------------- Layouts ---------------------------------
        # ---------------------------------------------------------------------

        layout = QVBoxLayout()

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Rotate:'))
        hbox.addWidget(self.txt_rotate)
        layout.addLayout(hbox)

        layout.addWidget(self.btn_rotate_horizontal)
        layout.addWidget(self.btn_rotate_vertical)

        self.setLayout(layout)

        # ---------------------------------------------------------------------
        # --------------------------- Connections -----------------------------
        # ---------------------------------------------------------------------

        self.txt_rotate.textChanged.connect(self.start_rotation)
        self.btn_rotate_horizontal.clicked.connect(
            self.start_horizontal_alignment)
        self.btn_rotate_vertical.clicked.connect(
            self.start_vertical_alignment)

        self.widgets2disable = [self.txt_rotate, self.btn_rotate_horizontal,
                                self.btn_rotate_vertical]
コード例 #3
0
ファイル: colors.py プロジェクト: sourabhmet/psy-simple
    def __init__(self, parent, fmto, what=None, N=None, pctl_min=None,
                 pctl_max=None):
        QWidget.__init__(self, parent)

        hbox = QHBoxLayout()

        self.combo = QComboBox()
        self.combo.addItems(sorted(fmto.calc_funcs))
        hbox.addWidget(self.combo)

        self.sb_N = QSpinBox()
        hbox.addWidget(self.sb_N)

        self.txt_min_pctl = QLineEdit()
        self.txt_min_pctl.setValidator(QDoubleValidator(0., 100., 10))
        hbox.addWidget(QLabel('Percentiles:'))
        hbox.addWidget(QLabel('Min.:'))
        hbox.addWidget(self.txt_min_pctl)

        self.txt_max_pctl = QLineEdit()
        self.txt_max_pctl.setValidator(QDoubleValidator(0., 100., 10))
        hbox.addWidget(QLabel('Max.:'))
        hbox.addWidget(self.txt_max_pctl)

        if what is not None:
            self.combo.setCurrentText(what)
        if N is not None:
            self.sb_N.setValue(N)
        if pctl_min is not None:
            self.txt_min_pctl.setText('%1.6g' % pctl_min)
        if pctl_max is not None:
            self.txt_max_pctl.setText('%1.6g' % pctl_max)

        self.combo.currentTextChanged.connect(self.set_obj)
        self.sb_N.valueChanged.connect(self.set_obj)
        self.txt_min_pctl.textChanged.connect(self.set_obj)
        self.txt_max_pctl.textChanged.connect(self.set_obj)

        self.setLayout(hbox)
コード例 #4
0
ファイル: colors.py プロジェクト: sourabhmet/psy-simple
    def __init__(self, parent, norm):
        QWidget.__init__(self, parent)
        self.norm = norm

        validator = QDoubleValidator()
        self.txt_min = QLineEdit()
        self.txt_min.setValidator(validator)
        self.txt_max = QLineEdit()
        self.txt_max.setValidator(validator)

        self.lbl_linthresh = QLabel('linthresh:')
        self.txt_linthresh = QLineEdit()  # linthresh for SymLogNorm
        self.txt_linthresh.setValidator(validator)
        self.txt_linthresh.setToolTip(
            'The threshold for linear scaling. Within this distance from 0, '
            'the scaling will be linear, not logarithmic.')

        self.lbl_gamma = QLabel('gamma:')
        self.txt_gamma = QLineEdit()  # gamma for PowerNorm
        self.txt_gamma.setValidator(validator)
        self.txt_gamma.setToolTip('The power value for the PowerNorm')

        self.fill_from_norm()

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Min.:'))
        hbox.addWidget(self.txt_min)
        hbox.addWidget(QLabel('Max.:'))
        hbox.addWidget(self.txt_max)
        hbox.addWidget(self.lbl_linthresh)
        hbox.addWidget(self.txt_linthresh)
        hbox.addWidget(self.lbl_gamma)
        hbox.addWidget(self.txt_gamma)
        self.setLayout(hbox)

        self.txt_min.textChanged.connect(self.set_obj)
        self.txt_max.textChanged.connect(self.set_obj)
        self.txt_linthresh.textChanged.connect(self.set_obj)
        self.txt_gamma.textChanged.connect(self.set_obj)
コード例 #5
0
ファイル: colors.py プロジェクト: sourabhmet/psy-simple
    def __init__(self, parent, array=None):
        QWidget.__init__(self, parent)

        self.txt_min = QLineEdit()
        self.txt_min.setValidator(QDoubleValidator())
        self.txt_max = QLineEdit()
        self.txt_max.setValidator(QDoubleValidator())
        self.txt_step = QLineEdit()
        self.txt_step.setValidator(QDoubleValidator(1e-10, 1e10, 10))
        self.sb_nsteps = QSpinBox()
        self.step_inc_combo = combo = QComboBox()
        combo.addItems(['Step', '# Steps'])

        if array is not None:
            self.txt_min.setText('%1.4g' % array.min())
            self.txt_max.setText('%1.4g' % array.max())
            steps = np.diff(array)
            if len(steps) == 1 or np.diff(steps).max() < 1e-5:
                self.txt_step.setText('%1.4g' % steps[0])
                combo.setCurrentIndex(0)
            else:
                combo.setCurrentIndex(1)
            self.sb_nsteps.setValue(len(array))

        self.toggle_txt_step(combo.currentText())

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Min.'))
        hbox.addWidget(self.txt_min)
        hbox.addWidget(QLabel('Max.'))
        hbox.addWidget(self.txt_max)
        hbox.addWidget(combo)
        hbox.addWidget(self.txt_step)
        hbox.addWidget(self.sb_nsteps)
        self.setLayout(hbox)

        for w in [self.txt_min, self.txt_max, self.txt_step]:
            w.textChanged.connect(self.set_array)
        self.sb_nsteps.valueChanged.connect(self.set_array)

        combo.currentTextChanged.connect(self.toggle_txt_step)
コード例 #6
0
    def __init__(self, *args, **kwargs):
        super(DataFrameEditor, self).__init__(*args, **kwargs)
        self.error_msg = PyErrorMessage(self)

        # Label for displaying the DataFrame size
        self.lbl_size = QLabel()

        # A Checkbox for enabling and disabling the editability of the index
        self.cb_index_editable = QCheckBox('Index editable')

        # A checkbox for enabling and disabling the change of data types
        self.cb_dtypes_changeable = QCheckBox('Datatypes changeable')

        # A checkbox for enabling and disabling sorting
        self.cb_enable_sort = QCheckBox('Enable sorting')

        # A button to open a dataframe from the file
        self.btn_open_df = QToolButton(parent=self)
        self.btn_open_df.setIcon(QIcon(get_icon('run_arrow.png')))
        self.btn_open_df.setToolTip('Open a DataFrame from your disk')

        self.btn_from_console = LoadFromConsoleButton(pd.DataFrame)
        self.btn_from_console.setToolTip('Show a DataFrame from the console')

        # The table to display the DataFrame
        self.table = DataFrameView(pd.DataFrame(), self)

        # format line edit
        self.format_editor = QLineEdit()
        self.format_editor.setText(self.table.model()._format)

        # format update button
        self.btn_change_format = QPushButton('Update')
        self.btn_change_format.setEnabled(False)

        # table clearing button
        self.btn_clear = QPushButton('Clear')
        self.btn_clear.setToolTip(
            'Clear the table and disconnect from the DataFrame')

        # refresh button
        self.btn_refresh = QToolButton()
        self.btn_refresh.setIcon(QIcon(get_icon('refresh.png')))
        self.btn_refresh.setToolTip('Refresh the table')

        # close button
        self.btn_close = QPushButton('Close')
        self.btn_close.setToolTip('Close this widget permanentely')

        # ---------------------------------------------------------------------
        # ------------------------ layout --------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()
        self.top_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.cb_index_editable)
        hbox.addWidget(self.cb_dtypes_changeable)
        hbox.addWidget(self.cb_enable_sort)
        hbox.addWidget(self.lbl_size)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_open_df)
        hbox.addWidget(self.btn_from_console)
        vbox.addLayout(hbox)
        vbox.addWidget(self.table)
        self.bottom_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.format_editor)
        hbox.addWidget(self.btn_change_format)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_clear)
        hbox.addWidget(self.btn_close)
        hbox.addWidget(self.btn_refresh)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # ------------------------ Connections --------------------------------
        # ---------------------------------------------------------------------
        self.cb_dtypes_changeable.stateChanged.connect(
            self.set_dtypes_changeable)
        self.cb_index_editable.stateChanged.connect(self.set_index_editable)
        self.btn_from_console.object_loaded.connect(self._open_ds_from_console)
        self.rows_inserted.connect(lambda i, n: self.set_lbl_size_text())
        self.format_editor.textChanged.connect(self.toggle_fmt_button)
        self.btn_change_format.clicked.connect(self.update_format)
        self.btn_clear.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(lambda: self.close())
        self.btn_refresh.clicked.connect(self.table.reset_model)
        self.btn_open_df.clicked.connect(self._open_dataframe)
        self.table.set_index_action.triggered.connect(
            self.update_index_editable)
        self.table.append_index_action.triggered.connect(
            self.update_index_editable)
        self.cb_enable_sort.stateChanged.connect(
            self.table.setSortingEnabled)
コード例 #7
0
ファイル: menu_actions.py プロジェクト: saraemp/straditize
class ExportDfDialog(QDialog):
    """A QDialog to export a :class:`pandas.DataFrame` to Excel or CSV"""
    @docstrings.get_sectionsf('ExportDfDialog')
    def __init__(self, df, straditizer, fname=None, *args, **kwargs):
        """
        Parameters
        ----------
        df: pandas.DataFrame
            The DataFrame to be exported
        straditizer: straditize.straditizer.Straditizer
            The source straditizer
        fname: str
            The file name to export to
        """
        super().__init__(*args, **kwargs)
        self.df = df
        self.stradi = straditizer
        self.txt_fname = QLineEdit()
        self.bt_open_file = QToolButton()
        self.bt_open_file.setIcon(QIcon(get_icon('run_arrow.png')))
        self.bt_open_file.setToolTip('Select the export file on your drive')

        self.cb_include_meta = QCheckBox('Include meta data')
        self.cb_include_meta.setChecked(True)

        self.bbox = bbox = QDialogButtonBox(QDialogButtonBox.Ok
                                            | QDialogButtonBox.Cancel)

        # ---------------------------------------------------------------------
        # --------------------------- Layouts ---------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Export to:'))
        hbox.addWidget(self.txt_fname)
        hbox.addWidget(self.bt_open_file)
        vbox.addLayout(hbox)

        vbox.addWidget(self.cb_include_meta)

        vbox.addWidget(bbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # --------------------------- Connections -----------------------------
        # ---------------------------------------------------------------------
        bbox.accepted.connect(self._export)
        bbox.rejected.connect(self.reject)
        self.bt_open_file.clicked.connect(self.get_open_file_name)

        if fname is not None:
            self.txt_fname.setText(fname)
            self._export()

    def get_open_file_name(self):
        """Ask the user for a filename for saving the data frame"""
        def check_current():
            dirname = osp.dirname(current)
            if osp.exists(dirname) and osp.isdir(dirname):
                return dirname

        current = self.txt_fname.text().strip()
        start = None
        if current:
            start = check_current()
        if start is None:
            for attr in 'project_file', 'image_file':
                try:
                    current = self.stradi.get_attr(attr)
                except KeyError:
                    pass
                else:
                    start = check_current()
                    if start is not None:
                        break
        if start is None:
            start = os.getcwd()
        fname = QFileDialog.getSaveFileName(
            self, 'DataFrame file destination', start,
            'Excel files (*.xlsx *.xls);;'
            'csv files (*.csv);;'
            'All files (*)')
        if with_qt5:  # the filter is passed as well
            fname = fname[0]
        if not fname:
            return
        self.txt_fname.setText(fname)

    def _export(self):
        fname = self.txt_fname.text()
        ending = osp.splitext(fname)[1]
        self.stradi.set_attr('exported', str(dt.datetime.now()))
        meta = self.stradi.valid_attrs
        if ending in ['.xls', '.xlsx']:
            with pd.ExcelWriter(fname) as writer:
                self.df.to_excel(writer, 'Data')
                if self.cb_include_meta.isChecked() and len(meta):
                    meta.to_excel(writer, 'Metadata', header=False)
        else:
            with open(fname, 'w') as f:
                if self.cb_include_meta.isChecked():
                    for t in meta.iloc[:, 0].items():
                        f.write('# %s: %s\n' % t)
            self.df.to_csv(fname, mode='a')
        self.accept()

    def cancel(self):
        del self.stradi, self.df
        super().cancel()

    def accept(self):
        del self.stradi, self.df
        super().accept()

    @classmethod
    @docstrings.dedent
    def export_df(cls, parent, df, straditizer, fname=None, exec_=True):
        """Open a dialog for exporting a DataFrame

        Parameters
        ----------
        parent: QWidget
            The parent widget
        %(ExportDfDialog.parameters)s"""
        dialog = cls(df, straditizer, fname, parent=parent)
        if fname is None:
            available_width = QDesktopWidget().availableGeometry().width() / 3.
            width = dialog.sizeHint().width()
            height = dialog.sizeHint().height()
            # The plot creator window should cover at least one third of the
            # screen
            dialog.resize(max(available_width, width), height)
            if exec_:
                dialog.exec_()
            else:
                return dialog
コード例 #8
0
    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        help_explorer: psyplot_gui.help_explorer.HelpExplorer
            The help explorer to show the documentation of one formatoption
        shell: IPython.core.interactiveshell.InteractiveShell
            The shell that can be used to update the current subproject via::

                psy.gcp().update(**kwargs)

            where ``**kwargs`` is defined through the selected formatoption
            in the :attr:`fmt_combo` combobox and the value in the
            :attr:`line_edit` editor
        ``*args, **kwargs``
            Any other keyword for the QWidget class
        """
        help_explorer = kwargs.pop('help_explorer', None)
        shell = kwargs.pop('shell', None)
        super(FormatoptionWidget, self).__init__(*args, **kwargs)
        self.help_explorer = help_explorer
        self.shell = shell

        # ---------------------------------------------------------------------
        # -------------------------- Child widgets ----------------------------
        # ---------------------------------------------------------------------
        self.group_combo = QComboBox(parent=self)
        self.fmt_combo = QComboBox(parent=self)
        self.line_edit = QLineEdit(parent=self)
        self.run_button = QToolButton(parent=self)

        self.keys_button = QPushButton('Formatoption keys', parent=self)
        self.summaries_button = QPushButton('Summaries', parent=self)
        self.docs_button = QPushButton('Docs', parent=self)

        self.grouped_cb = QCheckBox('grouped', parent=self)
        self.all_groups_cb = QCheckBox('all groups', parent=self)
        self.include_links_cb = QCheckBox('include links', parent=self)

        # ---------------------------------------------------------------------
        # -------------------------- Descriptions -----------------------------
        # ---------------------------------------------------------------------

        self.group_combo.setToolTip('Select the formatoption group')
        self.fmt_combo.setToolTip('Select the formatoption to update')
        self.line_edit.setToolTip(
            'Insert the value which what you want to update the selected '
            'formatoption and hit right button. The code is executed in the '
            'main console.')
        self.run_button.setIcon(QIcon(get_icon('run_arrow.png')))
        self.run_button.setToolTip('Update the selected formatoption')
        self.keys_button.setToolTip(
            'Show the formatoption keys in this group (or in all '
            'groups) in the help explorer')
        self.summaries_button.setToolTip(
            'Show the formatoption summaries in this group (or in all '
            'groups) in the help explorer')
        self.docs_button.setToolTip(
            'Show the formatoption documentations in this group (or in all '
            'groups) in the help explorer')
        self.grouped_cb.setToolTip(
            'Group the formatoptions before displaying them in the help '
            'explorer')
        self.all_groups_cb.setToolTip('Use all groups when displaying the '
                                      'keys, docs or summaries')
        self.include_links_cb.setToolTip(
            'Include links to remote documentations when showing the '
            'keys, docs and summaries in the help explorer (requires '
            'intersphinx)')

        # ---------------------------------------------------------------------
        # -------------------------- Connections ------------------------------
        # ---------------------------------------------------------------------
        self.group_combo.currentIndexChanged[int].connect(self.fill_fmt_combo)
        self.fmt_combo.currentIndexChanged[int].connect(self.show_fmt_info)
        self.run_button.clicked.connect(self.run_code)
        self.line_edit.returnPressed.connect(self.run_button.click)
        self.keys_button.clicked.connect(
            partial(self.show_all_fmt_info, 'keys'))
        self.summaries_button.clicked.connect(
            partial(self.show_all_fmt_info, 'summaries'))
        self.docs_button.clicked.connect(
            partial(self.show_all_fmt_info, 'docs'))

        # ---------------------------------------------------------------------
        # ------------------------------ Layouts ------------------------------
        # ---------------------------------------------------------------------
        self.combos = QHBoxLayout()
        self.combos.addWidget(self.group_combo)
        self.combos.addWidget(self.fmt_combo)

        self.execs = QHBoxLayout()
        self.execs.addWidget(self.line_edit)
        self.execs.addWidget(self.run_button)

        self.info_box = QHBoxLayout()
        self.info_box.addStretch(0)
        for w in [
                self.keys_button, self.summaries_button, self.docs_button,
                self.all_groups_cb, self.grouped_cb, self.include_links_cb
        ]:
            self.info_box.addWidget(w)

        self.vbox = QVBoxLayout()
        self.vbox.addLayout(self.combos)
        self.vbox.addLayout(self.execs)
        self.vbox.addLayout(self.info_box)

        self.setLayout(self.vbox)

        # fill with content
        self.fill_combos_from_project(psy.gcp())
        psy.Project.oncpchange.connect(self.fill_combos_from_project)
コード例 #9
0
class DataFrameEditor(DockMixin, QWidget):
    """An editor for data frames"""

    dock_cls = DataFrameDock

    #: A signal that is emitted, if the table is cleared
    cleared = QtCore.pyqtSignal()

    #: A signal that is emitted when a cell has been changed. The argument
    #: is a tuple of two integers and one float:
    #: the row index, the column index and the new value
    cell_edited = QtCore.pyqtSignal(int, int, object, object)

    #: A signal that is emitted, if rows have been inserted into the dataframe.
    #: The first value is the integer of the (original) position of the row,
    #: the second one is the number of rows
    rows_inserted = QtCore.pyqtSignal(int, int)

    @property
    def hidden(self):
        return not self.table.filled

    def __init__(self, *args, **kwargs):
        super(DataFrameEditor, self).__init__(*args, **kwargs)
        self.error_msg = PyErrorMessage(self)

        # Label for displaying the DataFrame size
        self.lbl_size = QLabel()

        # A Checkbox for enabling and disabling the editability of the index
        self.cb_index_editable = QCheckBox('Index editable')

        # A checkbox for enabling and disabling the change of data types
        self.cb_dtypes_changeable = QCheckBox('Datatypes changeable')

        # A checkbox for enabling and disabling sorting
        self.cb_enable_sort = QCheckBox('Enable sorting')

        # A button to open a dataframe from the file
        self.btn_open_df = QToolButton(parent=self)
        self.btn_open_df.setIcon(QIcon(get_icon('run_arrow.png')))
        self.btn_open_df.setToolTip('Open a DataFrame from your disk')

        self.btn_from_console = LoadFromConsoleButton(pd.DataFrame)
        self.btn_from_console.setToolTip('Show a DataFrame from the console')

        # The table to display the DataFrame
        self.table = DataFrameView(pd.DataFrame(), self)

        # format line edit
        self.format_editor = QLineEdit()
        self.format_editor.setText(self.table.model()._format)

        # format update button
        self.btn_change_format = QPushButton('Update')
        self.btn_change_format.setEnabled(False)

        # table clearing button
        self.btn_clear = QPushButton('Clear')
        self.btn_clear.setToolTip(
            'Clear the table and disconnect from the DataFrame')

        # refresh button
        self.btn_refresh = QToolButton()
        self.btn_refresh.setIcon(QIcon(get_icon('refresh.png')))
        self.btn_refresh.setToolTip('Refresh the table')

        # close button
        self.btn_close = QPushButton('Close')
        self.btn_close.setToolTip('Close this widget permanentely')

        # ---------------------------------------------------------------------
        # ------------------------ layout --------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()
        self.top_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.cb_index_editable)
        hbox.addWidget(self.cb_dtypes_changeable)
        hbox.addWidget(self.cb_enable_sort)
        hbox.addWidget(self.lbl_size)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_open_df)
        hbox.addWidget(self.btn_from_console)
        vbox.addLayout(hbox)
        vbox.addWidget(self.table)
        self.bottom_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.format_editor)
        hbox.addWidget(self.btn_change_format)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_clear)
        hbox.addWidget(self.btn_close)
        hbox.addWidget(self.btn_refresh)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # ------------------------ Connections --------------------------------
        # ---------------------------------------------------------------------
        self.cb_dtypes_changeable.stateChanged.connect(
            self.set_dtypes_changeable)
        self.cb_index_editable.stateChanged.connect(self.set_index_editable)
        self.btn_from_console.object_loaded.connect(self._open_ds_from_console)
        self.rows_inserted.connect(lambda i, n: self.set_lbl_size_text())
        self.format_editor.textChanged.connect(self.toggle_fmt_button)
        self.btn_change_format.clicked.connect(self.update_format)
        self.btn_clear.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(lambda: self.close())
        self.btn_refresh.clicked.connect(self.table.reset_model)
        self.btn_open_df.clicked.connect(self._open_dataframe)
        self.table.set_index_action.triggered.connect(
            self.update_index_editable)
        self.table.append_index_action.triggered.connect(
            self.update_index_editable)
        self.cb_enable_sort.stateChanged.connect(
            self.table.setSortingEnabled)

    def update_index_editable(self):
        model = self.table.model()
        if len(model.df.index.names) > 1:
            model.index_editable = False
            self.cb_index_editable.setEnabled(False)
        self.cb_index_editable.setChecked(model.index_editable)

    def set_lbl_size_text(self, nrows=None, ncols=None):
        """Set the text of the :attr:`lbl_size` label to display the size"""
        model = self.table.model()
        nrows = nrows if nrows is not None else model.rowCount()
        ncols = ncols if ncols is not None else model.columnCount()
        if not nrows and not ncols:
            self.lbl_size.setText('')
        else:
            self.lbl_size.setText('Rows: %i, Columns: %i' % (nrows, ncols))

    def clear_table(self):
        """Clear the table and emit the :attr:`cleared` signal"""
        df = pd.DataFrame()
        self.set_df(df, show=False)

    def _open_ds_from_console(self, oname, df):
        self.set_df(df)

    @docstrings.dedent
    def set_df(self, df, *args, **kwargs):
        """
        Fill the table from a :class:`~pandas.DataFrame`

        Parameters
        ----------
        %(DataFrameModel.parameters.no_parent)s
        show: bool
            If True (default), show and raise_ the editor
        """
        show = kwargs.pop('show', True)
        self.table.set_df(df, *args, **kwargs)
        self.set_lbl_size_text(*df.shape)
        model = self.table.model()
        self.cb_dtypes_changeable.setChecked(model.dtypes_changeable)

        if len(model.df.index.names) > 1:
            model.index_editable = False
            self.cb_index_editable.setEnabled(False)
        else:
            self.cb_index_editable.setEnabled(True)
        self.cb_index_editable.setChecked(model.index_editable)
        self.cleared.emit()
        if show:
            self.show_plugin()
            self.dock.raise_()

    def set_index_editable(self, state):
        """Set the :attr:`DataFrameModel.index_editable` attribute"""
        self.table.model().index_editable = state == Qt.Checked

    def set_dtypes_changeable(self, state):
        """Set the :attr:`DataFrameModel.dtypes_changeable` attribute"""
        self.table.model().dtypes_changeable = state == Qt.Checked

    def toggle_fmt_button(self, text):
        try:
            text % 1.1
        except (TypeError, ValueError):
            self.btn_change_format.setEnabled(False)
        else:
            self.btn_change_format.setEnabled(
                text.strip() != self.table.model()._format)

    def update_format(self):
        """Update the format of the table"""
        self.table.model().set_format(self.format_editor.text().strip())

    def to_dock(self, main, *args, **kwargs):
        connect = self.dock is None
        super(DataFrameEditor, self).to_dock(main, *args, **kwargs)
        if connect:
            self.dock.toggleViewAction().triggered.connect(self.maybe_tabify)

    def maybe_tabify(self):
        main = self.dock.parent()
        if self.is_shown and main.dockWidgetArea(
                main.help_explorer.dock) == main.dockWidgetArea(self.dock):
            main.tabifyDockWidget(main.help_explorer.dock, self.dock)

    def _open_dataframe(self):
        self.open_dataframe()

    def open_dataframe(self, fname=None, *args, **kwargs):
        """Opens a file dialog and the dataset that has been inserted"""
        if fname is None:
            fname = QFileDialog.getOpenFileName(
                self, 'Open dataset', os.getcwd(),
                'Comma separated files (*.csv);;'
                'Excel files (*.xls *.xlsx);;'
                'JSON files (*.json);;'
                'All files (*)'
                )
            if with_qt5:  # the filter is passed as well
                fname = fname[0]
        if isinstance(fname, pd.DataFrame):
            self.set_df(fname)
        elif not fname:
            return
        else:
            ext = osp.splitext(fname)[1]
            open_funcs = {
                '.xls': pd.read_excel, '.xlsx': pd.read_excel,
                '.json': pd.read_json,
                '.tab': partial(pd.read_csv, delimiter='\t'),
                '.dat': partial(pd.read_csv, delim_whitespace=True),
                }
            open_func = open_funcs.get(ext, pd.read_csv)
            try:
                df = open_func(fname)
            except Exception:
                self.error_msg.showTraceback(
                    '<b>Could not open DataFrame %s with %s</b>' % (
                        fname, open_func))
                return
            self.set_df(df)

    def close(self, *args, **kwargs):
        if self.dock is not None:
            self.dock.close(*args, **kwargs)  # removes the dock window
            del self.dock
        return super(DataFrameEditor, self).close(*args, **kwargs)
コード例 #10
0
class MarkerControl(StraditizerControlBase, QWidget):
    """Widget to control the appearance of the marks

    This widget controls the appearance of the
    :class:`straditize.cross_mark.CrossMarks` instances in the
    :attr:`~straditize.straditizer.Straditizer.marks` attribute of the
    :attr:`~straditize.widgets.StraditizerControlBase.straditizer`"""

    _toolbar = None

    @property
    def marks(self):
        """The :class:`~straditize.cross_mark.CrossMarks` of the straditizer
        """
        return chain(self.straditizer.marks, self.straditizer.magni_marks)

    @docstrings.dedent
    def __init__(self, straditizer_widgets, item, *args, **kwargs):
        """
        Parameters
        ----------
        %(StraditizerControlBase.init_straditizercontrol.parameters)s
        """
        super(MarkerControl, self).__init__(*args, **kwargs)
        self.init_straditizercontrol(straditizer_widgets, item)
        vbox = QVBoxLayout()

        # auto hide button
        box_hide = QGridLayout()
        self.cb_auto_hide = QCheckBox('Auto-hide')
        self.cb_show_connected = QCheckBox('Show additionals')
        self.cb_drag_hline = QCheckBox('Drag in y-direction')
        self.cb_drag_vline = QCheckBox('Drag in x-direction')
        self.cb_selectable_hline = QCheckBox('Horizontal lines selectable')
        self.cb_selectable_vline = QCheckBox('Vertical lines selectable')
        self.cb_show_hlines = QCheckBox('Show horizontal lines')
        self.cb_show_vlines = QCheckBox('Show vertical lines')
        box_hide.addWidget(self.cb_auto_hide, 0, 0)
        box_hide.addWidget(self.cb_show_connected, 0, 1)
        box_hide.addWidget(self.cb_show_vlines, 1, 0)
        box_hide.addWidget(self.cb_show_hlines, 1, 1)
        box_hide.addWidget(self.cb_drag_hline, 2, 0)
        box_hide.addWidget(self.cb_drag_vline, 2, 1)
        box_hide.addWidget(self.cb_selectable_hline, 3, 0)
        box_hide.addWidget(self.cb_selectable_vline, 3, 1)
        vbox.addLayout(box_hide)

        style_box = QGridLayout()
        style_box.addWidget(QLabel('Unselected:'), 0, 1)
        selected_label = QLabel('Selected:')
        style_box.addWidget(selected_label, 0, 2)
        max_width = selected_label.sizeHint().width()

        # line color
        self.lbl_color_select = ColorLabel()
        self.lbl_color_unselect = ColorLabel()
        self.lbl_color_select.setMaximumWidth(max_width)
        self.lbl_color_unselect.setMaximumWidth(max_width)
        style_box.addWidget(QLabel('Line color:'), 1, 0)
        style_box.addWidget(self.lbl_color_unselect, 1, 1)
        style_box.addWidget(self.lbl_color_select, 1, 2)

        # line width
        self.txt_line_width = QLineEdit()
        self.txt_line_width_select = QLineEdit()

        validator = QDoubleValidator()
        validator.setBottom(0)
        self.txt_line_width.setValidator(validator)
        self.txt_line_width_select.setValidator(validator)

        style_box.addWidget(QLabel('Line width:'), 2, 0)
        style_box.addWidget(self.txt_line_width, 2, 1)
        style_box.addWidget(self.txt_line_width_select, 2, 2)

        vbox.addLayout(style_box)

        # line style
        hbox_line_style = QHBoxLayout()
        hbox_line_style.addWidget(QLabel('Line style'))
        self.combo_line_style = QComboBox()
        hbox_line_style.addWidget(self.combo_line_style)
        vbox.addLayout(hbox_line_style)
        self.fill_linestyles()
        self.combo_line_style.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        # marker style
        hbox_marker_style = QHBoxLayout()
        hbox_marker_style.addWidget(QLabel('Marker size'))
        self.txt_marker_size = QLineEdit()
        self.txt_marker_size.setMinimumWidth(40)
        self.txt_marker_size.setText(str(mpl.rcParams['lines.markersize']))
        validator = QDoubleValidator()
        validator.setBottom(0)
        self.txt_marker_size.setValidator(validator)
        hbox_marker_style.addWidget(self.txt_marker_size)
        hbox_marker_style.addWidget(QLabel('Marker style'))
        self.combo_marker_style = QComboBox()
        hbox_marker_style.addWidget(self.combo_marker_style)
        vbox.addLayout(hbox_marker_style)

        self.setLayout(vbox)

        self.widgets2disable = [
            self.lbl_color_select, self.lbl_color_unselect, self.cb_auto_hide,
            self.txt_line_width, self.txt_line_width_select,
            self.combo_line_style, self.cb_show_connected,
            self.txt_marker_size, self.combo_marker_style, self.cb_drag_vline,
            self.cb_drag_hline, self.cb_selectable_vline,
            self.cb_selectable_hline, self.cb_show_hlines, self.cb_show_vlines
        ]

        self.fill_markerstyles()

        self.tb_actions = []

        # ---------------------------------------------------------------------
        # ---------------------------- connections ----------------------------
        # ---------------------------------------------------------------------

        self.lbl_color_select.color_changed.connect(self.change_select_colors)
        self.lbl_color_unselect.color_changed.connect(
            self.change_unselect_colors)
        self.txt_line_width.textChanged.connect(self.change_line_widths)
        self.txt_line_width_select.textChanged.connect(
            self.change_selection_line_widths)
        self.cb_auto_hide.stateChanged.connect(self.change_auto_hide)
        self.combo_marker_style.currentIndexChanged.connect(
            self.change_marker_style)
        self.combo_line_style.currentIndexChanged.connect(
            self.change_line_style)
        self.txt_marker_size.textChanged.connect(self.change_marker_size)
        self.cb_show_connected.stateChanged.connect(
            self.change_show_connected_artists)
        self.cb_drag_hline.stateChanged.connect(self.change_hline_draggable)
        self.cb_drag_vline.stateChanged.connect(self.change_vline_draggable)
        self.cb_selectable_hline.stateChanged.connect(
            self.change_hline_selectable)
        self.cb_selectable_vline.stateChanged.connect(
            self.change_vline_selectable)
        self.cb_show_vlines.stateChanged.connect(self.change_show_vlines)
        self.cb_show_hlines.stateChanged.connect(self.change_show_hlines)

    def draw_figs(self):
        """Draw the figures of the :attr:`marks`"""
        for canvas in {m.fig.canvas for m in self.marks}:
            canvas.draw_idle()

    def fill_linestyles(self):
        """Fill the :attr:`combo_line_style` combobox"""
        self.line_styles = [('-', 'solid'), ('--', 'dashed'),
                            ('-.', 'dashdot'), (':', 'dotted'),
                            ('None', None, '', ' ')]
        self.combo_line_style.addItems([t[0] for t in self.line_styles])

    def fill_markerstyles(self):
        """Fill the :attr:`combo_marker_style` combobox"""
        self.marker_styles = [('point', (".", )), ('pixel', (",", )),
                              ('circle', ("o", )),
                              ('triangle down', ("v", "1")),
                              ('triangle up', ("^", "2")),
                              ('triangle left', ("<", "3")),
                              ('triangle right', (">", "4")),
                              ('octagon', ("8", )), ('square', ("s", )),
                              ('pentagon', ("p", )),
                              ('plus (filled)', ("P", )), ('star', ("*", )),
                              ('hexagon1', ("h", )), ('hexagon2', ("H", )),
                              ('plus', ("+", )), ('x', ("x", )),
                              ('x (filled)', ("X", )), ('diamond', ("D", )),
                              ('thin diamond', ("d", )), ('vline', ("|", )),
                              ('hline', ("_", )),
                              ('no marker', ('None', None, '', ' '))]
        self.combo_marker_style.addItems([
            '%s (%s)' % (key.capitalize(), ','.join(map(str, filter(None, t))))
            for key, t in self.marker_styles
        ])

    def set_marker_item(self, marker):
        """Switch the :attr:`combo_marker_style` to the given `marker`

        Parameters
        ----------
        marker: str
            A matplotlib marker string"""
        for i, (key, t) in enumerate(self.marker_styles):
            if marker in t:
                block = self.combo_marker_style.blockSignals(True)
                self.combo_marker_style.setCurrentIndex(i)
                self.combo_marker_style.blockSignals(block)
                break

    def set_line_style_item(self, ls):
        """Switch the :attr:`combo_line_style` to the given linestyle

        Parameters
        ----------
        ls: str
            The matplotlib linestyle string
        """
        for i, t in enumerate(self.line_styles):
            if ls in t:
                block = self.combo_line_style.blockSignals(True)
                self.combo_line_style.setCurrentIndex(i)
                self.combo_line_style.blockSignals(block)
                break

    def should_be_enabled(self, w):
        """Check if a widget `w` should be enabled or disabled

        Parameters
        ----------
        w: PyQt5.QtWidgets.QWidget
            The widget to (potentially) enable

        Returns
        -------
        bool
            True if the :attr:`straditizer` of this instance has marks.
            Otherwise False"""
        return self.straditizer is not None and bool(self.straditizer.marks)

    def change_select_colors(self, color):
        """Change the selection color of the marks

        Change the selection color of the marks to the given color.

        Parameters
        ----------
        color: PyQt5.QtGui.QColor or a matplotlib color
            The color to use
        """
        if isinstance(color, QtGui.QColor):
            color = np.array(color.getRgb()) / 255.
        for mark in self.marks:
            key = 'color' if 'color' in mark._select_props else 'c'
            mark._select_props[key] = color
            mark._unselect_props[key] = mark.hline.get_c()
        self.draw_figs()

    def change_unselect_colors(self, color):
        """Change the :attr:`straditize.cross_mark.CrossMark.cunselect` color

        Change the unselection color of the marks to the given color.

        Parameters
        ----------
        color: PyQt5.QtGui.QColor or a matplotlib color
            The color to use
        """
        if isinstance(color, QtGui.QColor):
            color = np.array(color.getRgb()) / 255.
        for mark in self.marks:
            key = 'color' if 'color' in mark._unselect_props else 'c'
            mark._unselect_props[key] = color
            for l in chain(mark.hlines, mark.vlines, mark.line_connections):
                l.set_color(color)
        self.draw_figs()

    def change_line_widths(self, lw):
        """Change the linewidth of the marks

        Parameters
        ----------
        lw: float
            The line width to use
        """
        lw = float(lw or 0)
        for mark in self.marks:
            key = 'lw' if 'lw' in mark._unselect_props else 'linewidth'
            mark._unselect_props[key] = lw
            if not mark.auto_hide:
                for l in chain(mark.hlines, mark.vlines,
                               mark.line_connections):
                    l.set_lw(lw)
        self.draw_figs()

    def change_selection_line_widths(self, lw):
        """Change the linewidth for selected marks

        Parameters
        ----------
        lw: float
            The linewidth for selected marks"""
        lw = float(lw or 0)
        for mark in self.marks:
            key = 'lw' if 'lw' in mark._select_props else 'linewidth'
            mark._select_props[key] = lw

    def change_auto_hide(self, auto_hide):
        """Toggle the :attr:`~straditize.cross_mark.CrossMark.auto_hide`

        This method disables or enables the
        :attr:`~straditize.cross_mark.CrossMark.auto_hide` of the marks

        Parameters
        ----------
        auto_hide: bool or PyQt5.QtGui.Qt.Checked or PyQt5.QtGui.Qt.Unchecked
            The value to use for the auto_hide. :data:`PyQt5.QtGui.Qt.Checked`
            is equivalent to ``True``
        """
        if auto_hide is Qt.Checked:
            auto_hide = True
        elif auto_hide is Qt.Unchecked:
            auto_hide = False
        for mark in self.marks:
            mark.auto_hide = auto_hide
            if auto_hide:
                for l in chain(mark.hlines, mark.vlines,
                               mark.line_connections):
                    l.set_lw(0)
            else:
                lw = mark._unselect_props.get(
                    'lw', mark._unselect_props.get('linewidth'))
                for l in chain(mark.hlines, mark.vlines,
                               mark.line_connections):
                    l.set_lw(lw)
        self.draw_figs()

    def change_show_connected_artists(self, show):
        """Change the visibility of connected artists

        Parameters
        ----------
        show: bool
            The visibility for the
            :meth:`straditize.cross_mark.CrossMarks.set_connected_artists_visible`
            method"""
        if show is Qt.Checked:
            show = True
        elif show is Qt.Unchecked:
            show = False
        for mark in self.marks:
            mark.set_connected_artists_visible(show)
        self.draw_figs()

    def change_line_style(self, i):
        """Change the line style of the marks

        Parameters
        ----------
        i: int
            The index of the line style in the :attr:`line_styles` attribute
            to use
        """
        ls = self.line_styles[i][0]
        for mark in self.marks:
            for l in chain(mark.hlines, mark.vlines, mark.line_connections):
                l.set_ls(ls)
        self.draw_figs()

    def change_marker_style(self, i):
        """Change the marker style of the marks

        Parameters
        ----------
        i: int
            The index of the marker style in the :attr:`marker_styles`
            attribute to use
        """
        marker = self.marker_styles[i][1][0]
        for mark in self.marks:
            for l in chain(mark.hlines, mark.vlines, mark.line_connections):
                l.set_marker(marker)
        self.draw_figs()

    def change_marker_size(self, markersize):
        """Change the size of the markers

        Parameters
        ----------
        markersize: float
            The size of the marker to use"""
        markersize = float(markersize or 0)
        for mark in self.marks:
            for l in chain(mark.hlines, mark.vlines, mark.line_connections):
                l.set_markersize(markersize)
        self.draw_figs()

    def fill_from_mark(self, mark):
        """Set the widgets of this :class:`MarkerControl` from a mark

        This method sets the color labels, combo boxes, check boxes and
        text edits to match the properties of the given `mark`

        Parameters
        ----------
        mark: straditize.cross_mark.CrossMark
            The mark to use the attributes from
        """
        line = mark.hline
        try:
            cselect = mark._select_props['c']
        except KeyError:
            try:
                cselect = mark._select_props['color']
            except KeyError:
                cselect = mark.hline.get_c()
        lw_key = 'lw' if 'lw' in mark._unselect_props else 'linewidth'
        self.set_line_style_item(line.get_linestyle())
        self.set_marker_item(line.get_marker())
        self.txt_line_width.setText(str(mark._unselect_props.get(lw_key, 0)))
        self.txt_line_width_select.setText(
            str(mark._select_props.get(lw_key, 0)))
        self.txt_marker_size.setText(str(line.get_markersize()))
        self.lbl_color_select._set_color(cselect)
        self.lbl_color_unselect._set_color(mark.hline.get_c())
        self.cb_auto_hide.setChecked(mark.auto_hide)
        self.cb_show_connected.setChecked(mark.show_connected_artists)
        try:
            mark.x
        except NotImplementedError:
            self.cb_drag_vline.setEnabled(False)
            self.cb_show_vlines.setEnabled(False)
            self.cb_selectable_vline.setEnabled(False)
        else:
            self.cb_drag_vline.setEnabled(True)
            self.cb_show_vlines.setEnabled(True)
            self.cb_selectable_vline.setEnabled(True)
        try:
            mark.y
        except NotImplementedError:
            self.cb_drag_hline.setEnabled(False)
            self.cb_show_hlines.setEnabled(False)
            self.cb_selectable_hline.setEnabled(False)
        else:
            self.cb_drag_hline.setEnabled(True)
            self.cb_show_hlines.setEnabled(True)
            self.cb_selectable_hline.setEnabled(True)
        self.cb_drag_hline.setChecked('h' in mark.draggable)
        self.cb_drag_vline.setChecked('v' in mark.draggable)
        self.cb_show_hlines.setChecked(not mark.hide_horizontal)
        self.cb_show_vlines.setChecked(not mark.hide_vertical)
        self.cb_selectable_hline.setChecked('h' in mark.selectable)
        self.cb_selectable_vline.setChecked('v' in mark.selectable)

    @property
    def line_props(self):
        """The properties of the lines as a :class:`dict`"""
        return {
            'ls':
            self.combo_line_style.currentText(),
            'marker':
            self.marker_styles[self.combo_marker_style.currentIndex()][1][0],
            'lw':
            float(self.txt_line_width.text().strip() or 0),
            'markersize':
            float(self.txt_marker_size.text().strip() or 0),
            'c':
            self.lbl_color_unselect.color.getRgbF(),
        }

    @property
    def select_props(self):
        """The properties of selected marks as a :class:`dict`"""
        return {
            'c': self.lbl_color_select.color.getRgbF(),
            'lw': float(self.txt_line_width_select.text().strip() or 0)
        }

    def update_mark(self, mark):
        """Update the properties of a mark to match the settings

        Parameters
        ----------
        mark: straditize.cross_mark.CrossMarks
            The mark to update
        """
        if len(self.straditizer.marks) < 2:
            return
        # line properties
        props = self.line_props
        mark._unselect_props.update(props)
        mark._select_props.update(self.select_props)

        # auto_hide
        auto_hide = self.cb_auto_hide.isChecked()
        mark.auto_hide = auto_hide
        if auto_hide:
            props['lw'] = 0

        # show_connected
        show_connected = self.cb_show_connected.isChecked()
        mark.set_connected_artists_visible(show_connected)

        # drag hline
        drag_hline = self.cb_drag_hline.isChecked()
        if drag_hline and 'h' not in mark.draggable:
            mark.draggable.append('h')
        elif not drag_hline and 'h' in mark.draggable:
            mark.draggable.remove('h')

        # drag vline
        drag_vline = self.cb_drag_vline.isChecked()
        if drag_vline and 'v' not in mark.draggable:
            mark.draggable.append('v')
        elif not drag_vline and 'v' in mark.draggable:
            mark.draggable.remove('v')

        # select hline
        select_hline = self.cb_selectable_hline.isChecked()
        if select_hline and 'h' not in mark.selectable:
            mark.selectable.append('h')
        elif not select_hline and 'h' in mark.selectable:
            mark.selectable.remove('h')

        # select hline
        select_vline = self.cb_selectable_vline.isChecked()
        if select_vline and 'v' not in mark.selectable:
            mark.selectable.append('v')
        elif not select_vline and 'v' in mark.selectable:
            mark.selectable.remove('v')

        show_horizontal = self.cb_show_hlines.isChecked()
        mark.hide_horizontal = ~show_horizontal
        show_vertical = self.cb_show_vlines.isChecked()
        mark.hide_vertical = ~show_vertical

        for l in chain(mark.hlines, mark.vlines):
            l.update(props)
        for l in mark.hlines:
            l.set_visible(show_horizontal)
        for l in mark.vlines:
            l.set_visible(show_vertical)

    def change_hline_draggable(self, state):
        """Enable or disable the dragging of horizontal lines

        Parameters
        ----------
        state: Qt.Checked or Qt.Unchecked
            If Qt.Checked, the horizontal lines can be dragged and dropped"""
        if state == Qt.Checked:
            for mark in self.marks:
                mark.draggable = np.unique(np.r_[['h'], mark.draggable])
        else:
            for mark in self.marks:
                mark.draggable = np.array(list(set(mark.draggable) - {'h'}))

    def change_hline_selectable(self, state):
        """Enable or disable the selection of horizontal lines

        Parameters
        ----------
        state: Qt.Checked or Qt.Unchecked
            If Qt.Checked, the horizontal lines can be selected"""
        if state == Qt.Checked:
            for mark in self.marks:
                mark.selectable = np.unique(np.r_[['h'], mark.selectable])
        else:
            for mark in self.marks:
                mark.selectable = np.array(list(set(mark.selectable) - {'h'}))

    def change_vline_draggable(self, state):
        """Enable or disable the dragging of vertical lines

        Parameters
        ----------
        state: Qt.Checked or Qt.Unchecked
            If Qt.Checked, the vertical lines can be dragged and dropped"""
        if state == Qt.Checked:
            for mark in self.marks:
                mark.draggable = np.unique(np.r_[['v'], mark.draggable])
        else:
            for mark in self.marks:
                mark.draggable = np.array(list(set(mark.draggable) - {'v'}))

    def change_vline_selectable(self, state):
        """Enable or disable the selection of vertical lines

        Parameters
        ----------
        state: Qt.Checked or Qt.Unchecked
            If Qt.Checked, the vertical lines can be selected"""
        if state == Qt.Checked:
            for mark in self.marks:
                mark.selectable = np.unique(np.r_[['v'], mark.selectable])
        else:
            for mark in self.marks:
                mark.selectable = np.array(list(set(mark.selectable) - {'v'}))

    def change_show_hlines(self, state):
        """Enable of disable the visibility of horizontal lines

        Parameters
        ----------
        state: Qt.Checked or Qt.Unchecked
            If Qt.Checked, all horizontal lines are hidden"""
        if state == Qt.Checked:
            for mark in self.marks:
                mark.hide_horizontal = False
                mark.set_visible(True)
        else:
            for mark in self.marks:
                mark.hide_horizontal = True
                mark.set_visible(True)
        self.draw_figs()

    def change_show_vlines(self, state):
        """Enable of disable the visibility of vertical lines

        Parameters
        ----------
        state: Qt.Checked or Qt.Unchecked
            If Qt.Checked, all vertical lines are hidden"""
        if state == Qt.Checked:
            for mark in self.marks:
                mark.hide_vertical = False
                mark.set_visible(True)
        else:
            for mark in self.marks:
                mark.hide_vertical = True
                mark.set_visible(True)
        self.draw_figs()

    def enable_or_disable_widgets(self, b):
        """Renabled to use the :meth:`refresh` method
        """
        self.refresh()

    def fill_after_adding(self, mark):
        if not self._filled:
            self.refresh()

    def go_to_right_mark(self):
        """Move the plot to the next right cross mark"""
        ax = self.straditizer.marks[0].ax
        if ax.xaxis_inverted():
            return self.go_to_smaller_x_mark(min(ax.get_xlim()))
        else:
            return self.go_to_greater_x_mark(max(ax.get_xlim()))

    def go_to_left_mark(self):
        """Move the plot to the previous left cross mark"""
        ax = self.straditizer.marks[0].ax
        if ax.xaxis_inverted():
            return self.go_to_greater_x_mark(max(ax.get_xlim()))
        else:
            return self.go_to_smaller_x_mark(min(ax.get_xlim()))

    def go_to_greater_x_mark(self, x):
        """Move the plot to the next mark with a x-position greater than `x`

        Parameters
        ----------
        x: float
            The reference x-position that shall be smaller than the new
            centered mark"""
        def is_visible(mark):
            return (np.searchsorted(np.sort(xlim),
                                    mark.xa) == 1).any() and (np.searchsorted(
                                        np.sort(ylim), mark.ya) == 1).any()

        ax = next(self.marks).ax
        xlim = np.asarray(ax.get_xlim())
        ylim = np.asarray(ax.get_ylim())
        marks = self.straditizer.marks
        if len(marks[0].xa) > 1:
            # get the mark in the center
            yc = ylim.mean()
            try:
                mark = min(filter(is_visible, marks),
                           key=lambda m: np.abs(m.ya - yc).min())
            except ValueError:  # empty sequence
                mark = min(marks, key=lambda m: np.abs(m.ya - yc).min())
            # if all edges are visible already, we return
            if (np.searchsorted(xlim, mark.xa) == 1).all():
                return
            mask = mark.xa > x
            if not mask.any():  # already on the right side
                return
            i = mark.xa[mask].argmin()
            x = mark.xa[mask][i]
            dx = np.diff(xlim) / 2.
            ax.set_xlim(x - dx, x + dx)
            ax.set_ylim(*ax.get_ylim())
        else:
            distances = ((mark.xa > x).any()
                         and (mark.xa[mark.xa > x] - x).min()
                         for mark in marks)
            try:
                dist, mark = min((t for t in zip(distances, marks) if t[0]),
                                 key=lambda t: t[0])
            except ValueError:  # empty sequence
                return
            mask = mark.xa > x
            i = mark.xa[mask].argmin()
            x = mark.xa[mask][i]
            j = np.abs(mark.ya - ylim.mean()).argmin()
            y = mark.ya[j]
            dx = np.diff(xlim) / 2.
            ax.set_xlim(x - dx, x + dx)
            dy = np.diff(ylim) / 2.
            ax.set_ylim(y - dy, y + dy)
        self.straditizer.draw_figure()

    def go_to_smaller_x_mark(self, x):
        """Move the plot to the next mark with a x-position smaller than `x`

        Parameters
        ----------
        x: float
            The reference x-position that shall be greater than the new
            centered mark"""
        def is_visible(mark):
            return (np.searchsorted(np.sort(xlim),
                                    mark.xa) == 1).any() and (np.searchsorted(
                                        np.sort(ylim), mark.ya) == 1).any()

        ax = next(self.marks).ax
        xlim = np.asarray(ax.get_xlim())
        ylim = np.asarray(ax.get_ylim())
        marks = self.straditizer.marks
        if len(marks[0].xa) > 1:
            # get the mark in the center
            yc = ylim.mean()
            try:
                mark = min(filter(is_visible, marks),
                           key=lambda m: np.abs(m.ya - yc).min())
            except ValueError:  # empty sequence
                mark = min(marks, key=lambda m: np.abs(m.ya - yc).min())
            # if all edges are visible already, we return
            if (np.searchsorted(xlim, mark.xa) == 1).all():
                return
            mask = mark.xa < x
            if not mask.any():  # already on the right side
                return
            i = mark.xa[mask].argmin()
            x = mark.xa[mask][i]
            dx = np.diff(xlim) / 2.
            ax.set_xlim(x - dx, x + dx)
            ax.set_ylim(*ax.get_ylim())
        else:
            distances = ((mark.xa < x).any()
                         and (mark.xa[mark.xa < x] - x).min()
                         for mark in marks)
            try:
                dist, mark = min((t for t in zip(distances, marks) if t[0]),
                                 key=lambda t: t[0])
            except ValueError:  # empty sequence
                return
            mask = mark.xa < x
            i = mark.xa[mask].argmin()
            x = mark.xa[mask][i]
            j = np.abs(mark.ya - ylim.mean()).argmin()
            y = mark.ya[j]
            dx = np.diff(xlim) / 2.
            ax.set_xlim(x - dx, x + dx)
            dy = np.diff(ylim) / 2.
            ax.set_ylim(y - dy, y + dy)
        self.straditizer.draw_figure()

    def go_to_upper_mark(self):
        """Go to the next mark above the current y-limits"""
        ax = self.straditizer.marks[0].ax
        if ax.xaxis_inverted():
            return self.go_to_greater_y_mark(max(ax.get_ylim()))
        else:
            return self.go_to_smaller_y_mark(min(ax.get_ylim()))

    def go_to_lower_mark(self):
        """Go to the next mark below the current y-limits"""
        ax = self.straditizer.marks[0].ax
        if ax.xaxis_inverted():
            return self.go_to_smaller_y_mark(min(ax.get_ylim()))
        else:
            return self.go_to_greater_y_mark(max(ax.get_ylim()))

    def go_to_greater_y_mark(self, y):
        """Move the plot to the next mark with a y-position greater than `y`

        Parameters
        ----------
        y: float
            The reference y-position that shall be smaller than the new
            centered mark"""
        def is_visible(mark):
            return (np.searchsorted(np.sort(xlim),
                                    mark.xa) == 1).any() and (np.searchsorted(
                                        np.sort(ylim), mark.ya) == 1).any()

        ax = next(self.marks).ax
        xlim = np.asarray(ax.get_xlim())
        ylim = np.asarray(ax.get_ylim())
        marks = self.straditizer.marks
        if len(marks[0].ya) > 1:
            # get the mark in the center
            xc = xlim.mean()
            try:
                mark = min(filter(is_visible, marks),
                           key=lambda m: np.abs(m.xa - xc).min())
            except ValueError:  # empty sequence
                mark = min(marks, key=lambda m: np.abs(m.xa - xc).min())
            # if all edges are visible already, we return
            if (np.searchsorted(ylim, mark.ya) == 1).all():
                return
            mask = mark.ya > y
            if not mask.any():  # already on the right side
                return
            i = mark.ya[mask].argmin()
            y = mark.ya[mask][i]
            dy = np.diff(ylim) / 2.
            ax.set_ylim(y - dy, y + dy)
        else:
            distances = ((mark.ya > y).any()
                         and (mark.xa[mark.ya > y] - y).min()
                         for mark in marks)
            try:
                dist, mark = min((t for t in zip(distances, marks) if t[0]),
                                 key=lambda t: t[0])
            except ValueError:  # empty sequence
                return
            mask = mark.ya > y
            i = mark.ya[mask].argmin()
            y = mark.ya[mask][i]
            j = np.abs(mark.xa - xlim.mean()).argmin()
            x = mark.xa[j]
            dx = np.diff(xlim) / 2.
            ax.set_xlim(x - dx, x + dx)
            dy = np.diff(ylim) / 2.
            ax.set_ylim(y - dy, y + dy)
        self.straditizer.draw_figure()

    def go_to_smaller_y_mark(self, y):
        """Move the plot to the next mark with a y-position smaller than `x`

        Parameters
        ----------
        y: float
            The reference y-position that shall be smaller than the new
            centered mark"""
        def is_visible(mark):
            return (np.searchsorted(np.sort(xlim),
                                    mark.xa) == 1).any() and (np.searchsorted(
                                        np.sort(ylim), mark.ya) == 1).any()

        ax = next(self.marks).ax
        xlim = np.asarray(ax.get_xlim())
        ylim = np.asarray(ax.get_ylim())
        marks = self.straditizer.marks
        if len(marks[0].ya) > 1:
            # get the mark in the center
            xc = xlim.mean()
            try:
                mark = min(filter(is_visible, marks),
                           key=lambda m: np.abs(m.xa - xc).min())
            except ValueError:  # empty sequence
                mark = min(marks, key=lambda m: np.abs(m.xa - xc).min())
            # if all edges are visible already, we return
            if (np.searchsorted(ylim, mark.ya) == 1).all():
                return
            mask = mark.ya < y
            if not mask.any():  # already on the right side
                return
            i = mark.ya[mask].argmin()
            y = mark.ya[mask][i]
            dy = np.diff(ylim) / 2.
            ax.set_ylim(y - dy, y + dy)
        else:
            distances = ((mark.ya < y).any()
                         and (mark.ya[mark.ya < y] - y).min()
                         for mark in marks)
            try:
                dist, mark = min((t for t in zip(distances, marks) if t[0]),
                                 key=lambda t: t[0])
            except ValueError:  # empty sequence
                return
            mask = mark.ya < y
            i = mark.ya[mask].argmin()
            y = mark.ya[mask][i]
            j = np.abs(mark.xa - xlim.mean()).argmin()
            x = mark.xa[j]
            dx = np.diff(xlim) / 2.
            ax.set_xlim(x - dx, x + dx)
            dy = np.diff(ylim) / 2.
            ax.set_ylim(y - dy, y + dy)
        self.straditizer.draw_figure()

    def add_toolbar_widgets(self, mark):
        """Add the navigation actions to the toolbar"""
        tb = self.straditizer.marks[0].ax.figure.canvas.toolbar
        if not isinstance(tb, QToolBar):
            return
        if self.tb_actions:
            self.remove_actions()

        self.tb_actions.append(tb.addSeparator())
        try:
            mark.x
        except NotImplementedError:
            add_right = False
        else:
            a = tb.addAction(QIcon(get_icon('left_mark.png')), 'left mark',
                             self.go_to_left_mark)
            a.setToolTip('Move to the next cross mark on the left')
            self.tb_actions.append(a)
            add_right = True

        try:
            mark.y
        except NotImplementedError:
            pass
        else:
            a = tb.addAction(QIcon(get_icon('upper_mark.png')), 'upper mark',
                             self.go_to_upper_mark)
            a.setToolTip('Move to the next cross mark above')
            self.tb_actions.append(a)

            a = tb.addAction(QIcon(get_icon('lower_mark.png')), 'lower mark',
                             self.go_to_lower_mark)
            a.setToolTip('Move to the next cross mark below')
            self.tb_actions.append(a)

        if add_right:
            a = tb.addAction(QIcon(get_icon('right_mark.png')), 'right mark',
                             self.go_to_right_mark)
            a.setToolTip('Move to the next cross mark on the right')
            self.tb_actions.append(a)
        self._toolbar = tb

    def remove_actions(self):
        """Remove the navigation actions from the toolbar"""
        if self._toolbar is None:
            return
        tb = self._toolbar
        for a in self.tb_actions:
            tb.removeAction(a)
        self.tb_actions.clear()

    def refresh(self):
        """Reimplemented to also set the properties of this widget
        """
        super(MarkerControl, self).refresh()
        if self.straditizer is not None and self.straditizer.marks:
            self._filled = True
            mark = self.straditizer.marks[0]
            self.fill_from_mark(mark)
            self.add_toolbar_widgets(mark)
        else:
            self.remove_actions()
            self._filled = False
        if self.straditizer is not None:
            self.straditizer.mark_added.connect(self.fill_after_adding)
            self.straditizer.mark_added.connect(self.update_mark)
コード例 #11
0
    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        help_explorer: psyplot_gui.help_explorer.HelpExplorer
            The help explorer to show the documentation of one formatoption
        console: psyplot_gui.console.ConsoleWidget
            The console that can be used to update the current subproject via::

                psy.gcp().update(**kwargs)

            where ``**kwargs`` is defined through the selected formatoption
            in the :attr:`fmt_combo` combobox and the value in the
            :attr:`line_edit` editor
        ``*args, **kwargs``
            Any other keyword for the QWidget class
        """
        help_explorer = kwargs.pop('help_explorer', None)
        console = kwargs.pop('console', None)
        super(FormatoptionWidget, self).__init__(*args, **kwargs)
        self.help_explorer = help_explorer
        self.console = console
        self.error_msg = PyErrorMessage(self)

        # ---------------------------------------------------------------------
        # -------------------------- Child widgets ----------------------------
        # ---------------------------------------------------------------------
        self.group_combo = QComboBox(parent=self)
        self.fmt_combo = QComboBox(parent=self)
        self.line_edit = QLineEdit(parent=self)
        self.text_edit = QTextEdit(parent=self)
        self.run_button = QToolButton(parent=self)

        # completer for the fmto widget
        self.fmt_combo.setEditable(True)
        self.fmt_combo.setInsertPolicy(QComboBox.NoInsert)
        self.fmto_completer = completer = QCompleter(
            ['time', 'lat', 'lon', 'lev'])
        completer.setCompletionMode(QCompleter.PopupCompletion)
        completer.activated[str].connect(self.set_fmto)
        if with_qt5:
            completer.setFilterMode(Qt.MatchContains)
        completer.setModel(QStandardItemModel())
        self.fmt_combo.setCompleter(completer)

        self.dim_widget = DimensionsWidget(parent=self)
        self.dim_widget.setVisible(False)

        self.multiline_button = QPushButton('Multiline', parent=self)
        self.multiline_button.setCheckable(True)

        self.yaml_cb = QCheckBox('Yaml syntax')
        self.yaml_cb.setChecked(True)

        self.keys_button = QPushButton('Keys', parent=self)
        self.summaries_button = QPushButton('Summaries', parent=self)
        self.docs_button = QPushButton('Docs', parent=self)

        self.grouped_cb = QCheckBox('grouped', parent=self)
        self.all_groups_cb = QCheckBox('all groups', parent=self)
        self.include_links_cb = QCheckBox('include links', parent=self)

        self.text_edit.setVisible(False)

        # ---------------------------------------------------------------------
        # -------------------------- Descriptions -----------------------------
        # ---------------------------------------------------------------------

        self.group_combo.setToolTip('Select the formatoption group')
        self.fmt_combo.setToolTip('Select the formatoption to update')
        self.line_edit.setToolTip(
            'Insert the value which what you want to update the selected '
            'formatoption and hit right button. The code is executed in the '
            'main console.')
        self.yaml_cb.setToolTip(
            "Use the yaml syntax for the values inserted in the above cell. "
            "Otherwise the content there is evaluated as a python expression "
            "in the terminal")
        self.text_edit.setToolTip(self.line_edit.toolTip())
        self.run_button.setIcon(QIcon(get_icon('run_arrow.png')))
        self.run_button.setToolTip('Update the selected formatoption')
        self.multiline_button.setToolTip(
            'Allow linebreaks in the text editor line above.')
        self.keys_button.setToolTip(
            'Show the formatoption keys in this group (or in all '
            'groups) in the help explorer')
        self.summaries_button.setToolTip(
            'Show the formatoption summaries in this group (or in all '
            'groups) in the help explorer')
        self.docs_button.setToolTip(
            'Show the formatoption documentations in this group (or in all '
            'groups) in the help explorer')
        self.grouped_cb.setToolTip(
            'Group the formatoptions before displaying them in the help '
            'explorer')
        self.all_groups_cb.setToolTip('Use all groups when displaying the '
                                      'keys, docs or summaries')
        self.include_links_cb.setToolTip(
            'Include links to remote documentations when showing the '
            'keys, docs and summaries in the help explorer (requires '
            'intersphinx)')

        # ---------------------------------------------------------------------
        # -------------------------- Connections ------------------------------
        # ---------------------------------------------------------------------
        self.group_combo.currentIndexChanged[int].connect(self.fill_fmt_combo)
        self.fmt_combo.currentIndexChanged[int].connect(self.show_fmt_info)
        self.fmt_combo.currentIndexChanged[int].connect(self.load_fmt_widget)
        self.fmt_combo.currentIndexChanged[int].connect(
            self.set_current_fmt_value)
        self.run_button.clicked.connect(self.run_code)
        self.line_edit.returnPressed.connect(self.run_button.click)
        self.multiline_button.clicked.connect(self.toggle_line_edit)
        self.keys_button.clicked.connect(
            partial(self.show_all_fmt_info, 'keys'))
        self.summaries_button.clicked.connect(
            partial(self.show_all_fmt_info, 'summaries'))
        self.docs_button.clicked.connect(
            partial(self.show_all_fmt_info, 'docs'))

        # ---------------------------------------------------------------------
        # ------------------------------ Layouts ------------------------------
        # ---------------------------------------------------------------------
        self.combos = QHBoxLayout()
        self.combos.addWidget(self.group_combo)
        self.combos.addWidget(self.fmt_combo)

        self.execs = QHBoxLayout()
        self.execs.addWidget(self.line_edit)
        self.execs.addWidget(self.text_edit)
        self.execs.addWidget(self.run_button)

        self.info_box = QHBoxLayout()
        self.info_box.addWidget(self.multiline_button)
        self.info_box.addWidget(self.yaml_cb)
        self.info_box.addStretch(0)
        for w in [
                self.keys_button, self.summaries_button, self.docs_button,
                self.all_groups_cb, self.grouped_cb, self.include_links_cb
        ]:
            self.info_box.addWidget(w)

        self.vbox = QVBoxLayout()
        self.vbox.addLayout(self.combos)
        self.vbox.addWidget(self.dim_widget)
        self.vbox.addLayout(self.execs)
        self.vbox.addLayout(self.info_box)

        self.vbox.setSpacing(0)

        self.setLayout(self.vbox)

        # fill with content
        self.fill_combos_from_project(psy.gcp())
        psy.Project.oncpchange.connect(self.fill_combos_from_project)
        rcParams.connect('fmt.sort_by_key', self.refill_from_rc)
コード例 #12
0
ファイル: samples_table.py プロジェクト: saraemp/straditize
class MultiCrossMarksEditor(DockMixin, QWidget):
    """An editor for cross marks in multiple axes"""

    #: The QDockWidget for the :class:`DataFrameEditor`
    dock_cls = DataFrameDock

    #: A :class:`weakref` to the
    #: :attr:`~straditize.widgets.StraditizerWidgets.straditizer`
    straditizer = None

    def __init__(self, straditizer, axes=None, *args, **kwargs):
        """
        Parameters
        ----------
        straditizer: weakref.ref
            The reference to the straditizer
        axes: matplotlib.axes.Axes
            The matplotlib axes corresponding to the marks
        """
        super(MultiCrossMarksEditor, self).__init__(*args, **kwargs)
        self.straditizer = straditizer
        straditizer = straditizer()
        self.error_msg = PyErrorMessage(self)

        #: Plot the reconstructed data
        self.cb_plot_lines = QCheckBox('Plot reconstruction')
        self.cb_plot_lines.setChecked(True)

        # A Checkbox to automatically zoom to the selection
        self.cb_zoom_to_selection = QCheckBox('Zoom to selection')

        # A Checkbox to automaticall hide the other marks
        self.cb_selection_only = QCheckBox('Selection only')

        # A Checkbox to automatically fit the selected cells to the selected
        # data
        self.cb_fit2selection = QCheckBox(
            'Fit selected cells to selected data')
        self.cb_fit2selection.setToolTip(
            'If checked, select cells from the table and click on one of the '
            'plots to update the table with the data at the selected position.'
        )

        # The table to display the DataFrame
        self.table = self.create_view(axes=axes)

        # format line edit
        self.format_editor = QLineEdit()
        self.format_editor.setText(self.table.model()._format)

        # format update button
        self.btn_change_format = QPushButton('Update')
        self.btn_change_format.setEnabled(False)

        self.btn_save = QPushButton('Save')
        self.btn_save.setToolTip('Save the samples and continue editing')

        # ---------------------------------------------------------------------
        # ------------------------ layout --------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()
        self.top_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.cb_zoom_to_selection)
        hbox.addWidget(self.cb_selection_only)
        hbox.addWidget(self.cb_fit2selection)
        hbox.addWidget(self.cb_plot_lines)
        hbox.addStretch(0)
        vbox.addLayout(hbox)
        vbox.addWidget(self.table)
        self.bottom_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.format_editor)
        hbox.addWidget(self.btn_change_format)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_save)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # ------------------------ Connections --------------------------------
        # ---------------------------------------------------------------------
        self.format_editor.textChanged.connect(self.toggle_fmt_button)
        self.btn_change_format.clicked.connect(self.update_format)
        self.btn_save.clicked.connect(self.save_samples)
        straditizer.mark_added.connect(self.table.model().load_new_marks)
        straditizer.mark_removed.connect(self.table.model().remove_mark)
        self.table.selectionModel().selectionChanged.connect(
            self.maybe_zoom_to_selection)
        self.table.frozen_table_view.selectionModel().selectionChanged.connect(
            self.maybe_zoom_to_selection)
        self.table.selectionModel().selectionChanged.connect(
            self.maybe_show_selection_only)
        self.table.frozen_table_view.selectionModel().selectionChanged.connect(
            self.maybe_show_selection_only)

        self.cb_zoom_to_selection.stateChanged.connect(
            self.toggle_cb_zoom_to_selection)
        self.cb_selection_only.stateChanged.connect(
            self.toggle_cb_selection_only)
        self.cb_fit2selection.stateChanged.connect(self.toggle_fit2selection)
        self.cb_plot_lines.stateChanged.connect(self.toggle_plot_lines)

        self.toggle_plot_lines()

    def create_view(self, axes=None):
        """Create the :class:`MultiCrossMarksView` of the editor

        Parameters
        ----------
        axes: list of :class:`matplotlib.axes.Axes`
            The matplotlib axes for the marks"""
        stradi = self.straditizer()
        reader = stradi.data_reader
        df = getattr(stradi, '_plotted_full_df', reader._full_df).copy()
        df.columns = [
            str(i) if str(i) == colname else '%s (%i)' % (colname, i)
            for i, colname in enumerate(stradi.colnames_reader.column_names +
                                        ['nextrema'])
        ]
        return MultiCrossMarksView(stradi.marks,
                                   df,
                                   df.columns,
                                   self.straditizer,
                                   axes=axes,
                                   occurences_value=reader.occurences_value)

    def save_samples(self):
        """Save the samples to the :attr:`straditizer` without removing them"""
        self.straditizer().update_samples_sep(remove=False)

    def maybe_zoom_to_selection(self):
        if self.cb_zoom_to_selection.isChecked():
            self.table.zoom_to_selection()

    def maybe_show_selection_only(self):
        if self.cb_selection_only.isChecked():
            self.table.show_selected_marks_only()

    def toggle_cb_zoom_to_selection(self):
        if self.cb_zoom_to_selection.isChecked():
            self.table.zoom_to_selection()

    def toggle_cb_selection_only(self):
        if self.cb_selection_only.isChecked():
            self.table.show_selected_marks_only()
        else:
            self.table.show_all_marks()

    def toggle_fit2selection(self):
        """Enable the fitting so selected digitized data"""
        model = self.table.model()
        fig = model.fig
        if self.cb_fit2selection.isChecked():
            self._fit2selection_cid = fig.canvas.mpl_connect(
                'button_press_event', self._fit2selection)
        elif self._fit2selection_cid is not None:
            fig.canvas.mpl_disconnect(self._fit2selection_cid)
            del self._fit2selection_cid

    def _fit2selection(self, event):
        model = self.table.model()
        if (not event.inaxes or event.button != 1
                or model.fig.canvas.manager.toolbar.mode != ''):
            return
        y = int(np.round(event.ydata))
        data = self.table.full_df.loc[y]
        indexes = list(self.table.selectedIndexes())
        mark = None
        for index in indexes:
            row = index.row()
            col = index.column()
            if col == 0:  # index column
                continue
            mark = model.get_cell_mark(row, col)
            old_pos = mark.pos
            xa = data[col - 1]
            if np.isnan(xa):
                xa = 0
            mark.set_pos((xa, mark.ya))
            mark.moved.emit(old_pos, mark)
        if mark is not None:
            mark.fig.canvas.draw()

    def toggle_fmt_button(self, text):
        try:
            text % 1.1
        except (TypeError, ValueError):
            self.btn_change_format.setEnabled(False)
        else:
            self.btn_change_format.setEnabled(
                text.strip() != self.table.model()._format)

    def toggle_plot_lines(self):
        model = self.table.model()
        if self.cb_plot_lines.isChecked():
            model.plot_lines()
        else:
            model.remove_lines()

    def update_format(self):
        """Update the format of the table"""
        self.table.model().set_format(self.format_editor.text().strip())

    def to_dock(self,
                main,
                title=None,
                position=None,
                docktype='df',
                *args,
                **kwargs):
        if position is None:
            if main.centralWidget() is not main.help_explorer:
                position = main.dockWidgetArea(main.help_explorer.dock)
            else:
                position = Qt.RightDockWidgetArea
        connect = self.dock is None
        ret = super(MultiCrossMarksEditor, self).to_dock(main,
                                                         title,
                                                         position,
                                                         docktype=docktype,
                                                         *args,
                                                         **kwargs)
        if connect:
            self.dock.toggleViewAction().triggered.connect(self.maybe_tabify)
        return ret

    def maybe_tabify(self):
        main = self.dock.parent()
        if self.is_shown and main.dockWidgetArea(
                main.help_explorer.dock) == main.dockWidgetArea(self.dock):
            main.tabifyDockWidget(main.help_explorer.dock, self.dock)
コード例 #13
0
class DataFrameEditor(DockMixin, QWidget):
    """An editor for data frames"""

    dock_cls = DataFrameDock

    #: A signal that is emitted, if the table is cleared
    cleared = QtCore.pyqtSignal()

    #: A signal that is emitted when a cell has been changed. The argument
    #: is a tuple of two integers and one float:
    #: the row index, the column index and the new value
    cell_edited = QtCore.pyqtSignal(int, int, object, object)

    #: A signal that is emitted, if rows have been inserted into the dataframe.
    #: The first value is the integer of the (original) position of the row,
    #: the second one is the number of rows
    rows_inserted = QtCore.pyqtSignal(int, int)

    @property
    def hidden(self):
        return not self.table.filled

    def __init__(self, *args, **kwargs):
        super(DataFrameEditor, self).__init__(*args, **kwargs)
        self.error_msg = PyErrorMessage(self)

        # Label for displaying the DataFrame size
        self.lbl_size = QLabel()

        # A Checkbox for enabling and disabling the editability of the index
        self.cb_index_editable = QCheckBox('Index editable')

        # A checkbox for enabling and disabling the change of data types
        self.cb_dtypes_changeable = QCheckBox('Datatypes changeable')

        # A checkbox for enabling and disabling sorting
        self.cb_enable_sort = QCheckBox('Enable sorting')

        # A button to open a dataframe from the file
        self.btn_open_df = QToolButton(parent=self)
        self.btn_open_df.setIcon(QIcon(get_icon('run_arrow.png')))
        self.btn_open_df.setToolTip('Open a DataFrame from your disk')

        self.btn_from_console = LoadFromConsoleButton(pd.DataFrame)
        self.btn_from_console.setToolTip('Show a DataFrame from the console')

        # The table to display the DataFrame
        self.table = DataFrameView(pd.DataFrame(), self)

        # format line edit
        self.format_editor = QLineEdit()
        self.format_editor.setText(self.table.model()._format)

        # format update button
        self.btn_change_format = QPushButton('Update')
        self.btn_change_format.setEnabled(False)

        # table clearing button
        self.btn_clear = QPushButton('Clear')
        self.btn_clear.setToolTip(
            'Clear the table and disconnect from the DataFrame')

        # refresh button
        self.btn_refresh = QToolButton()
        self.btn_refresh.setIcon(QIcon(get_icon('refresh.png')))
        self.btn_refresh.setToolTip('Refresh the table')

        # close button
        self.btn_close = QPushButton('Close')
        self.btn_close.setToolTip('Close this widget permanentely')

        # ---------------------------------------------------------------------
        # ------------------------ layout --------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()
        self.top_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.cb_index_editable)
        hbox.addWidget(self.cb_dtypes_changeable)
        hbox.addWidget(self.cb_enable_sort)
        hbox.addWidget(self.lbl_size)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_open_df)
        hbox.addWidget(self.btn_from_console)
        vbox.addLayout(hbox)
        vbox.addWidget(self.table)
        self.bottom_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.format_editor)
        hbox.addWidget(self.btn_change_format)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_clear)
        hbox.addWidget(self.btn_close)
        hbox.addWidget(self.btn_refresh)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # ------------------------ Connections --------------------------------
        # ---------------------------------------------------------------------
        self.cb_dtypes_changeable.stateChanged.connect(
            self.set_dtypes_changeable)
        self.cb_index_editable.stateChanged.connect(self.set_index_editable)
        self.btn_from_console.object_loaded.connect(self._open_ds_from_console)
        self.rows_inserted.connect(lambda i, n: self.set_lbl_size_text())
        self.format_editor.textChanged.connect(self.toggle_fmt_button)
        self.btn_change_format.clicked.connect(self.update_format)
        self.btn_clear.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(lambda: self.close())
        self.btn_refresh.clicked.connect(self.table.reset_model)
        self.btn_open_df.clicked.connect(self._open_dataframe)
        self.table.set_index_action.triggered.connect(
            self.update_index_editable)
        self.table.append_index_action.triggered.connect(
            self.update_index_editable)
        self.cb_enable_sort.stateChanged.connect(
            self.table.setSortingEnabled)

    def update_index_editable(self):
        model = self.table.model()
        if len(model.df.index.names) > 1:
            model.index_editable = False
            self.cb_index_editable.setEnabled(False)
        self.cb_index_editable.setChecked(model.index_editable)

    def set_lbl_size_text(self, nrows=None, ncols=None):
        """Set the text of the :attr:`lbl_size` label to display the size"""
        model = self.table.model()
        nrows = nrows if nrows is not None else model.rowCount()
        ncols = ncols if ncols is not None else model.columnCount()
        if not nrows and not ncols:
            self.lbl_size.setText('')
        else:
            self.lbl_size.setText('Rows: %i, Columns: %i' % (nrows, ncols))

    def clear_table(self):
        """Clear the table and emit the :attr:`cleared` signal"""
        df = pd.DataFrame()
        self.set_df(df, show=False)

    def _open_ds_from_console(self, oname, df):
        self.set_df(df)

    @docstrings.dedent
    def set_df(self, df, *args, **kwargs):
        """
        Fill the table from a :class:`~pandas.DataFrame`

        Parameters
        ----------
        %(DataFrameModel.parameters.no_parent)s
        show: bool
            If True (default), show and raise_ the editor
        """
        show = kwargs.pop('show', True)
        self.table.set_df(df, *args, **kwargs)
        self.set_lbl_size_text(*df.shape)
        model = self.table.model()
        self.cb_dtypes_changeable.setChecked(model.dtypes_changeable)

        if len(model.df.index.names) > 1:
            model.index_editable = False
            self.cb_index_editable.setEnabled(False)
        else:
            self.cb_index_editable.setEnabled(True)
        self.cb_index_editable.setChecked(model.index_editable)
        self.cleared.emit()
        if show:
            self.show_plugin()
            self.dock.raise_()

    def set_index_editable(self, state):
        """Set the :attr:`DataFrameModel.index_editable` attribute"""
        self.table.model().index_editable = state == Qt.Checked

    def set_dtypes_changeable(self, state):
        """Set the :attr:`DataFrameModel.dtypes_changeable` attribute"""
        self.table.model().dtypes_changeable = state == Qt.Checked

    def toggle_fmt_button(self, text):
        try:
            text % 1.1
        except (TypeError, ValueError):
            self.btn_change_format.setEnabled(False)
        else:
            self.btn_change_format.setEnabled(
                text.strip() != self.table.model()._format)

    def update_format(self):
        """Update the format of the table"""
        self.table.model().set_format(self.format_editor.text().strip())

    def to_dock(self, main, *args, **kwargs):
        connect = self.dock is None
        super(DataFrameEditor, self).to_dock(main, *args, **kwargs)
        if connect:
            self.dock.toggleViewAction().triggered.connect(self.maybe_tabify)

    def maybe_tabify(self):
        main = self.dock.parent()
        if self.is_shown and main.dockWidgetArea(
                main.help_explorer.dock) == main.dockWidgetArea(self.dock):
            main.tabifyDockWidget(main.help_explorer.dock, self.dock)

    def _open_dataframe(self):
        self.open_dataframe()

    def open_dataframe(self, fname=None, *args, **kwargs):
        """Opens a file dialog and the dataset that has been inserted"""
        if fname is None:
            fname = QFileDialog.getOpenFileName(
                self, 'Open dataset', os.getcwd(),
                'Comma separated files (*.csv);;'
                'Excel files (*.xls *.xlsx);;'
                'JSON files (*.json);;'
                'All files (*)'
                )
            if with_qt5:  # the filter is passed as well
                fname = fname[0]
        if isinstance(fname, pd.DataFrame):
            self.set_df(fname)
        elif not fname:
            return
        else:
            ext = osp.splitext(fname)[1]
            open_funcs = {
                '.xls': pd.read_excel, '.xlsx': pd.read_excel,
                '.json': pd.read_json,
                '.tab': partial(pd.read_csv, delimiter='\t'),
                '.dat': partial(pd.read_csv, delim_whitespace=True),
                }
            open_func = open_funcs.get(ext, pd.read_csv)
            try:
                df = open_func(fname)
            except Exception:
                self.error_msg.showTraceback(
                    '<b>Could not open DataFrame %s with %s</b>' % (
                        fname, open_func))
                return
            self.set_df(df)

    def close(self, *args, **kwargs):
        if self.dock is not None:
            self.dock.close(*args, **kwargs)  # removes the dock window
            del self.dock
        return super(DataFrameEditor, self).close(*args, **kwargs)
コード例 #14
0
ファイル: colors.py プロジェクト: sourabhmet/psy-simple
class ArrayFmtWidget(QWidget):
    """Fmt widget for :class:`psy_simple.plotters.DataTicksCalculator`

    This formatoption widgets contains 3 line edits, one for the minimum, one
    for the maximum and one for the step size. And a spin box for the number
    of increments"""

    def __init__(self, parent, array=None):
        QWidget.__init__(self, parent)

        self.txt_min = QLineEdit()
        self.txt_min.setValidator(QDoubleValidator())
        self.txt_max = QLineEdit()
        self.txt_max.setValidator(QDoubleValidator())
        self.txt_step = QLineEdit()
        self.txt_step.setValidator(QDoubleValidator(1e-10, 1e10, 10))
        self.sb_nsteps = QSpinBox()
        self.step_inc_combo = combo = QComboBox()
        combo.addItems(['Step', '# Steps'])

        if array is not None:
            self.txt_min.setText('%1.4g' % array.min())
            self.txt_max.setText('%1.4g' % array.max())
            steps = np.diff(array)
            if len(steps) == 1 or np.diff(steps).max() < 1e-5:
                self.txt_step.setText('%1.4g' % steps[0])
                combo.setCurrentIndex(0)
            else:
                combo.setCurrentIndex(1)
            self.sb_nsteps.setValue(len(array))

        self.toggle_txt_step(combo.currentText())

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Min.'))
        hbox.addWidget(self.txt_min)
        hbox.addWidget(QLabel('Max.'))
        hbox.addWidget(self.txt_max)
        hbox.addWidget(combo)
        hbox.addWidget(self.txt_step)
        hbox.addWidget(self.sb_nsteps)
        self.setLayout(hbox)

        for w in [self.txt_min, self.txt_max, self.txt_step]:
            w.textChanged.connect(self.set_array)
        self.sb_nsteps.valueChanged.connect(self.set_array)

        combo.currentTextChanged.connect(self.toggle_txt_step)

    def toggle_txt_step(self, s):
        show_step = s == 'Step'
        self.txt_step.setVisible(show_step)
        self.sb_nsteps.setVisible(not show_step)
        self.txt_step.setEnabled(show_step)
        self.sb_nsteps.setEnabled(not show_step)

    def set_array(self, *args, **kwargs):
        try:
            vmin = float(self.txt_min.text())
        except (ValueError, TypeError):
            return
        try:
            vmax = float(self.txt_max.text())
        except (ValueError, TypeError):
            return
        if self.txt_step.isEnabled():
            try:
                step = float(self.txt_step.text().strip())
            except (ValueError, TypeError):
                return
            arr = np.arange(vmin, vmax + 0.05 * step, step)
        else:
            arr = np.linspace(vmin, vmax, self.sb_nsteps.value())
        self.parent().set_obj(np.round(arr, 4).tolist())
コード例 #15
0
ファイル: fmt_widget.py プロジェクト: Chilipp/psyplot_gui
class FormatoptionWidget(QWidget, DockMixin):
    """
    Widget to update the formatoptions of the current project

    This widget, mainly made out of a combobox for the formatoption group,
    a combobox for the formatoption, and a text editor, is designed
    for updating the selected formatoptions for the current subproject.

    The widget is connected to the :attr:`psyplot.project.Project.oncpchange`
    signal and refills the comboboxes if the current subproject changes.

    The text editor either accepts python code that will be executed by the
    given `console`, or yaml code.
    """

    no_fmtos_update = _temp_bool_prop(
        'no_fmtos_update', """update the fmto combo box or not""")

    #: The combobox for the formatoption groups
    group_combo = None

    #: The combobox for the formatoptions
    fmt_combo = None

    #: The help_explorer to display the documentation of the formatoptions
    help_explorer = None

    #: The formatoption specific widget that is loaded from the formatoption
    fmt_widget = None

    #: A line edit for updating the formatoptions
    line_edit = None

    #: A multiline text editor for updating the formatoptions
    text_edit = None

    #: A button to switch between :attr:`line_edit` and :attr:`text_edit`
    multiline_button = None

    @property
    def shell(self):
        """The shell to execute the update of the formatoptions in the current
        project"""
        return self.console.kernel_manager.kernel.shell

    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        help_explorer: psyplot_gui.help_explorer.HelpExplorer
            The help explorer to show the documentation of one formatoption
        console: psyplot_gui.console.ConsoleWidget
            The console that can be used to update the current subproject via::

                psy.gcp().update(**kwargs)

            where ``**kwargs`` is defined through the selected formatoption
            in the :attr:`fmt_combo` combobox and the value in the
            :attr:`line_edit` editor
        ``*args, **kwargs``
            Any other keyword for the QWidget class
        """
        help_explorer = kwargs.pop('help_explorer', None)
        console = kwargs.pop('console', None)
        super(FormatoptionWidget, self).__init__(*args, **kwargs)
        self.help_explorer = help_explorer
        self.console = console
        self.error_msg = PyErrorMessage(self)

        # ---------------------------------------------------------------------
        # -------------------------- Child widgets ----------------------------
        # ---------------------------------------------------------------------
        self.group_combo = QComboBox(parent=self)
        self.fmt_combo = QComboBox(parent=self)
        self.line_edit = QLineEdit(parent=self)
        self.text_edit = QTextEdit(parent=self)
        self.run_button = QToolButton(parent=self)

        # completer for the fmto widget
        self.fmt_combo.setEditable(True)
        self.fmt_combo.setInsertPolicy(QComboBox.NoInsert)
        self.fmto_completer = completer = QCompleter(
            ['time', 'lat', 'lon', 'lev'])
        completer.setCompletionMode(
            QCompleter.PopupCompletion)
        completer.activated[str].connect(self.set_fmto)
        if with_qt5:
            completer.setFilterMode(Qt.MatchContains)
        completer.setModel(QStandardItemModel())
        self.fmt_combo.setCompleter(completer)

        self.dim_widget = DimensionsWidget(parent=self)
        self.dim_widget.setVisible(False)

        self.multiline_button = QPushButton('Multiline', parent=self)
        self.multiline_button.setCheckable(True)

        self.yaml_cb = QCheckBox('Yaml syntax')
        self.yaml_cb.setChecked(True)

        self.keys_button = QPushButton('Keys', parent=self)
        self.summaries_button = QPushButton('Summaries', parent=self)
        self.docs_button = QPushButton('Docs', parent=self)

        self.grouped_cb = QCheckBox('grouped', parent=self)
        self.all_groups_cb = QCheckBox('all groups', parent=self)
        self.include_links_cb = QCheckBox('include links', parent=self)

        self.text_edit.setVisible(False)

        # ---------------------------------------------------------------------
        # -------------------------- Descriptions -----------------------------
        # ---------------------------------------------------------------------

        self.group_combo.setToolTip('Select the formatoption group')
        self.fmt_combo.setToolTip('Select the formatoption to update')
        self.line_edit.setToolTip(
            'Insert the value which what you want to update the selected '
            'formatoption and hit right button. The code is executed in the '
            'main console.')
        self.yaml_cb.setToolTip(
            "Use the yaml syntax for the values inserted in the above cell. "
            "Otherwise the content there is evaluated as a python expression "
            "in the terminal")
        self.text_edit.setToolTip(self.line_edit.toolTip())
        self.run_button.setIcon(QIcon(get_icon('run_arrow.png')))
        self.run_button.setToolTip('Update the selected formatoption')
        self.multiline_button.setToolTip(
            'Allow linebreaks in the text editor line above.')
        self.keys_button.setToolTip(
            'Show the formatoption keys in this group (or in all '
            'groups) in the help explorer')
        self.summaries_button.setToolTip(
            'Show the formatoption summaries in this group (or in all '
            'groups) in the help explorer')
        self.docs_button.setToolTip(
            'Show the formatoption documentations in this group (or in all '
            'groups) in the help explorer')
        self.grouped_cb.setToolTip(
            'Group the formatoptions before displaying them in the help '
            'explorer')
        self.all_groups_cb.setToolTip('Use all groups when displaying the '
                                      'keys, docs or summaries')
        self.include_links_cb.setToolTip(
            'Include links to remote documentations when showing the '
            'keys, docs and summaries in the help explorer (requires '
            'intersphinx)')

        # ---------------------------------------------------------------------
        # -------------------------- Connections ------------------------------
        # ---------------------------------------------------------------------
        self.group_combo.currentIndexChanged[int].connect(self.fill_fmt_combo)
        self.fmt_combo.currentIndexChanged[int].connect(self.show_fmt_info)
        self.fmt_combo.currentIndexChanged[int].connect(self.load_fmt_widget)
        self.fmt_combo.currentIndexChanged[int].connect(
            self.set_current_fmt_value)
        self.run_button.clicked.connect(self.run_code)
        self.line_edit.returnPressed.connect(self.run_button.click)
        self.multiline_button.clicked.connect(self.toggle_line_edit)
        self.keys_button.clicked.connect(
            partial(self.show_all_fmt_info, 'keys'))
        self.summaries_button.clicked.connect(
            partial(self.show_all_fmt_info, 'summaries'))
        self.docs_button.clicked.connect(
            partial(self.show_all_fmt_info, 'docs'))

        # ---------------------------------------------------------------------
        # ------------------------------ Layouts ------------------------------
        # ---------------------------------------------------------------------
        self.combos = QHBoxLayout()
        self.combos.addWidget(self.group_combo)
        self.combos.addWidget(self.fmt_combo)

        self.execs = QHBoxLayout()
        self.execs.addWidget(self.line_edit)
        self.execs.addWidget(self.text_edit)
        self.execs.addWidget(self.run_button)

        self.info_box = QHBoxLayout()
        self.info_box.addWidget(self.multiline_button)
        self.info_box.addWidget(self.yaml_cb)
        self.info_box.addStretch(0)
        for w in [self.keys_button, self.summaries_button, self.docs_button,
                  self.all_groups_cb, self.grouped_cb, self.include_links_cb]:
            self.info_box.addWidget(w)

        self.vbox = QVBoxLayout()
        self.vbox.addLayout(self.combos)
        self.vbox.addWidget(self.dim_widget)
        self.vbox.addLayout(self.execs)
        self.vbox.addLayout(self.info_box)

        self.vbox.setSpacing(0)

        self.setLayout(self.vbox)

        # fill with content
        self.fill_combos_from_project(psy.gcp())
        psy.Project.oncpchange.connect(self.fill_combos_from_project)
        rcParams.connect('fmt.sort_by_key', self.refill_from_rc)

    def refill_from_rc(self, sort_by_key):
        from psyplot.project import gcp
        self.fill_combos_from_project(gcp())

    def fill_combos_from_project(self, project):
        """Fill :attr:`group_combo` and :attr:`fmt_combo` from a project

        Parameters
        ----------
        project: psyplot.project.Project
            The project to use"""
        if rcParams['fmt.sort_by_key']:
            def sorter(fmto):
                return fmto.key
        else:
            sorter = self.get_name

        current_text = self.group_combo.currentText()
        with self.no_fmtos_update:
            self.group_combo.clear()
            if project is None or project.is_main or not len(project):
                self.fmt_combo.clear()
                self.groups = []
                self.fmtos = []
                self.line_edit.setEnabled(False)
                return
            self.line_edit.setEnabled(True)
            # get dimensions
            it_vars = chain.from_iterable(
                arr.psy.iter_base_variables for arr in project.arrays)
            dims = next(it_vars).dims
            sdims = set(dims)
            for var in it_vars:
                sdims.intersection_update(var.dims)
            coords = [d for d in dims if d in sdims]
            coords_name = [COORDSGROUP] if coords else []
            coords_verbose = ['Dimensions'] if coords else []
            coords = [coords] if coords else []

            if len(project.plotters):
                # get formatoptions and group them alphabetically
                grouped_fmts = defaultdict(list)
                for fmto in project._fmtos:
                    grouped_fmts[fmto.group].append(fmto)
                for val in six.itervalues(grouped_fmts):
                    val.sort(key=sorter)
                grouped_fmts = OrderedDict(
                    sorted(six.iteritems(grouped_fmts),
                           key=lambda t: psyp.groups.get(t[0], t[0])))
                fmt_groups = list(grouped_fmts.keys())
                # save original names
                self.groups = coords_name + [ALLGROUP] + fmt_groups
                # save verbose group names (which are used in the combo box)
                self.groupnames = (
                    coords_verbose + ['All formatoptions'] + list(
                        map(lambda s: psyp.groups.get(s, s), fmt_groups)))
                # save formatoptions
                fmtos = list(grouped_fmts.values())
                self.fmtos = coords + [sorted(
                    chain(*fmtos), key=sorter)] + fmtos
            else:
                self.groups = coords_name
                self.groupnames = coords_verbose
                self.fmtos = coords
            self.group_combo.addItems(self.groupnames)
            ind = self.group_combo.findText(current_text)
            self.group_combo.setCurrentIndex(ind if ind >= 0 else 0)
        self.fill_fmt_combo(self.group_combo.currentIndex())

    def get_name(self, fmto):
        """Get the name of a :class:`psyplot.plotter.Formatoption` instance"""
        if isinstance(fmto, six.string_types):
            return fmto
        return '%s (%s)' % (fmto.name, fmto.key) if fmto.name else fmto.key

    @property
    def fmto(self):
        return self.fmtos[self.group_combo.currentIndex()][
            self.fmt_combo.currentIndex()]

    @fmto.setter
    def fmto(self, value):
        name = self.get_name(value)
        for i, fmtos in enumerate(self.fmtos):
            if i == 1:  # all formatoptions
                continue
            if name in map(self.get_name, fmtos):
                with self.no_fmtos_update:
                    self.group_combo.setCurrentIndex(i)
                self.fill_fmt_combo(i, name)
                return

    def toggle_line_edit(self):
        """Switch between the :attr:`line_edit` and :attr:`text_edit`

        This method is called when the :attr:`multiline_button` is clicked
        and switches between the single line :attr:``line_edit` and the
        multiline :attr:`text_edit`
        """
        # switch to multiline text edit
        if (self.multiline_button.isChecked() and
                not self.text_edit.isVisible()):
            self.line_edit.setVisible(False)
            self.text_edit.setVisible(True)
            self.text_edit.setPlainText(self.line_edit.text())
        elif (not self.multiline_button.isChecked() and
              not self.line_edit.isVisible()):
            self.line_edit.setVisible(True)
            self.text_edit.setVisible(False)
            self.line_edit.setText(self.text_edit.toPlainText())

    def fill_fmt_combo(self, i, current_text=None):
        """Fill the :attr:`fmt_combo` combobox based on the current group name
        """
        if not self.no_fmtos_update:
            with self.no_fmtos_update:
                if current_text is None:
                    current_text = self.fmt_combo.currentText()
                self.fmt_combo.clear()
                self.fmt_combo.addItems(
                    list(map(self.get_name, self.fmtos[i])))
                ind = self.fmt_combo.findText(current_text)
                self.fmt_combo.setCurrentIndex(ind if ind >= 0 else 0)
                # update completer model
                self.setup_fmt_completion_model()
            idx = self.fmt_combo.currentIndex()
            self.show_fmt_info(idx)
            self.load_fmt_widget(idx)
            self.set_current_fmt_value(idx)

    def set_fmto(self, name):
        self.fmto = name

    def setup_fmt_completion_model(self):
        fmtos = list(unique_everseen(map(
            self.get_name, chain.from_iterable(self.fmtos))))
        model = self.fmto_completer.model()
        model.setRowCount(len(fmtos))
        for i, name in enumerate(fmtos):
            model.setItem(i, QStandardItem(name))

    def load_fmt_widget(self, i):
        """Load the formatoption specific widget

        This method loads the formatoption specific widget from the
        :meth:`psyplot.plotter.Formatoption.get_fmt_widget` method and
        displays it above the :attr:`line_edit`

        Parameters
        ----------
        i: int
            The index of the current formatoption"""
        self.remove_fmt_widget()
        group_ind = self.group_combo.currentIndex()
        if not self.no_fmtos_update:
            from psyplot.project import gcp
            if self.groups[group_ind] == COORDSGROUP:
                dim = self.fmtos[group_ind][i]
                self.fmt_widget = self.dim_widget
                self.dim_widget.set_dim(dim)
                self.dim_widget.set_single_selection(
                    dim not in gcp()[0].dims)
                self.dim_widget.setVisible(True)
            else:
                fmto = self.fmtos[group_ind][i]
                self.fmt_widget = fmto.get_fmt_widget(self, gcp())
                if self.fmt_widget is not None:
                    self.vbox.insertWidget(2, self.fmt_widget)

    def reset_fmt_widget(self):
        idx = self.fmt_combo.currentIndex()
        self.load_fmt_widget(idx)
        self.set_current_fmt_value(idx)

    def remove_fmt_widget(self):
        if self.fmt_widget is not None:
            self.fmt_widget.hide()
            if self.fmt_widget is self.dim_widget:
                self.fmt_widget.reset_combobox()
            else:
                self.vbox.removeWidget(self.fmt_widget)
                self.fmt_widget.close()
            del self.fmt_widget

    def set_current_fmt_value(self, i):
        """Add the value of the current formatoption to the line text"""
        group_ind = self.group_combo.currentIndex()
        if not self.no_fmtos_update:
            if self.groups[group_ind] == COORDSGROUP:
                from psyplot.project import gcp
                dim = self.fmtos[group_ind][i]
                self.set_obj(gcp().arrays[0].psy.idims[dim])
            else:
                fmto = self.fmtos[group_ind][i]
                self.set_obj(fmto.value)

    def show_fmt_info(self, i):
        """Show the documentation of the formatoption in the help explorer
        """
        group_ind = self.group_combo.currentIndex()
        if (not self.no_fmtos_update and
                self.groups[group_ind] != COORDSGROUP):
            fmto = self.fmtos[self.group_combo.currentIndex()][i]
            fmto.plotter.show_docs(
                fmto.key, include_links=self.include_links_cb.isChecked())

    def run_code(self):
        """Run the update of the project inside the :attr:`shell`"""
        if self.line_edit.isVisible():
            text = str(self.line_edit.text())
        else:
            text = str(self.text_edit.toPlainText())
        if not text or not self.fmtos:
            return
        group_ind = self.group_combo.currentIndex()
        if self.groups[group_ind] == COORDSGROUP:
            key = self.fmtos[group_ind][self.fmt_combo.currentIndex()]
            param = 'dims'
        else:
            key = self.fmtos[group_ind][self.fmt_combo.currentIndex()].key
            param = 'fmt'
        if self.yaml_cb.isChecked():
            import psyplot.project as psy
            psy.gcp().update(**{key: yaml.load(text)})
        else:
            code = "psy.gcp().update(%s={'%s': %s})" % (param, key, text)
            if ExecutionInfo is not None:
                info = ExecutionInfo(raw_cell=code, store_history=False,
                                     silent=True, shell_futures=False)
                e = ExecutionResult(info)
            else:
                e = ExecutionResult()
            self.console.run_command_in_shell(code, e)
            try:
                e.raise_error()
            except Exception:  # reset the console and clear the error message
                raise
            finally:
                self.console.reset()

    def get_text(self):
        """Get the current update text"""
        if self.line_edit.isVisible():
            return self.line_edit.text()
        else:
            return self.text_edit.toPlainText()

    def get_obj(self):
        """Get the current update text"""
        if self.line_edit.isVisible():
            txt = self.line_edit.text()
        else:
            txt = self.text_edit.toPlainText()
        try:
            obj = yaml.load(txt)
        except Exception:
            self.error_msg.showTraceback("Could not load %s" % txt)
        else:
            return obj

    def insert_obj(self, obj):
        """Add a string to the formatoption widget"""
        current = self.get_text()
        use_yaml = self.yaml_cb.isChecked()
        use_line_edit = self.line_edit.isVisible()
        # strings are treated separately such that we consider quotation marks
        # at the borders
        if isstring(obj) and current:
            if use_line_edit:
                pos = self.line_edit.cursorPosition()
            else:
                pos = self.text_edit.textCursor().position()
            if pos not in [0, len(current)]:
                s = obj
            else:
                if current[0] in ['"', "'"]:
                    current = current[1:-1]
                self.clear_text()
                if pos == 0:
                    s = '"' + obj + current + '"'
                else:
                    s = '"' + current + obj + '"'
                current = ''
        elif isstring(obj):  # add quotation marks
            s = '"' + obj + '"'
        elif not use_yaml:
            s = repr(obj)
        else:
            s = yaml.dump(obj).strip()
            if s.endswith('\n...'):
                s = s[:-4]
        if use_line_edit:
            self.line_edit.insert(s)
        else:
            self.text_edit.insertPlainText(s)

    def clear_text(self):
        if self.line_edit.isVisible():
            self.line_edit.clear()
        else:
            self.text_edit.clear()

    def set_obj(self, obj):
        self.clear_text()
        self.insert_obj(obj)

    def show_all_fmt_info(self, what):
        """Show the keys, summaries or docs of the formatoptions

        Calling this function let's the help browser show the documentation
        etc. of all docs or only the selected group determined by the state of
        the :attr:`grouped_cb` and :attr:`all_groups_cb` checkboxes

        Parameters
        ----------
        what: {'keys', 'summaries', 'docs'}
            Determines what to show"""
        if not self.fmtos:
            return
        if (self.all_groups_cb.isChecked() or
                self.group_combo.currentIndex() < 2):
            fmtos = list(chain.from_iterable(
                fmto_group for i, fmto_group in enumerate(self.fmtos)
                if self.groups[i] not in [ALLGROUP, COORDSGROUP]))
        else:
            fmtos = self.fmtos[self.group_combo.currentIndex()]
        plotter = fmtos[0].plotter
        getattr(plotter, 'show_' + what)(
            [fmto.key for fmto in fmtos], grouped=self.grouped_cb.isChecked(),
            include_links=self.include_links_cb.isChecked())
コード例 #16
0
ファイル: colors.py プロジェクト: sourabhmet/psy-simple
class DataTicksCalculatorFmtWidget(QWidget):
    """Fmt widget for :class:`psy_simple.plotters.DataTicksCalculator`

    This widget contains a combo box with the different options from the
    :attr:`psy_simple.plotters.DataTicksCalculator.calc_funcs`, a spin box
    for the number of increments and two text widgets for minimum and maximum
    percentile"""

    def __init__(self, parent, fmto, what=None, N=None, pctl_min=None,
                 pctl_max=None):
        QWidget.__init__(self, parent)

        hbox = QHBoxLayout()

        self.combo = QComboBox()
        self.combo.addItems(sorted(fmto.calc_funcs))
        hbox.addWidget(self.combo)

        self.sb_N = QSpinBox()
        hbox.addWidget(self.sb_N)

        self.txt_min_pctl = QLineEdit()
        self.txt_min_pctl.setValidator(QDoubleValidator(0., 100., 10))
        hbox.addWidget(QLabel('Percentiles:'))
        hbox.addWidget(QLabel('Min.:'))
        hbox.addWidget(self.txt_min_pctl)

        self.txt_max_pctl = QLineEdit()
        self.txt_max_pctl.setValidator(QDoubleValidator(0., 100., 10))
        hbox.addWidget(QLabel('Max.:'))
        hbox.addWidget(self.txt_max_pctl)

        if what is not None:
            self.combo.setCurrentText(what)
        if N is not None:
            self.sb_N.setValue(N)
        if pctl_min is not None:
            self.txt_min_pctl.setText('%1.6g' % pctl_min)
        if pctl_max is not None:
            self.txt_max_pctl.setText('%1.6g' % pctl_max)

        self.combo.currentTextChanged.connect(self.set_obj)
        self.sb_N.valueChanged.connect(self.set_obj)
        self.txt_min_pctl.textChanged.connect(self.set_obj)
        self.txt_max_pctl.textChanged.connect(self.set_obj)

        self.setLayout(hbox)

    def set_obj(self):
        obj = [self.combo.currentText(),
               self.sb_N.value()]
        if (self.txt_min_pctl.text().strip() or
                self.txt_max_pctl.text().strip()):
            obj.append(float(self.txt_min_pctl.text().strip() or 0))
            if self.txt_max_pctl.text().strip():
                obj.append(float(self.txt_max_pctl.text().strip()))
        self.parent().set_obj(obj)
コード例 #17
0
ファイル: fmt_widget.py プロジェクト: Chilipp/psyplot_gui
    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        help_explorer: psyplot_gui.help_explorer.HelpExplorer
            The help explorer to show the documentation of one formatoption
        console: psyplot_gui.console.ConsoleWidget
            The console that can be used to update the current subproject via::

                psy.gcp().update(**kwargs)

            where ``**kwargs`` is defined through the selected formatoption
            in the :attr:`fmt_combo` combobox and the value in the
            :attr:`line_edit` editor
        ``*args, **kwargs``
            Any other keyword for the QWidget class
        """
        help_explorer = kwargs.pop('help_explorer', None)
        console = kwargs.pop('console', None)
        super(FormatoptionWidget, self).__init__(*args, **kwargs)
        self.help_explorer = help_explorer
        self.console = console
        self.error_msg = PyErrorMessage(self)

        # ---------------------------------------------------------------------
        # -------------------------- Child widgets ----------------------------
        # ---------------------------------------------------------------------
        self.group_combo = QComboBox(parent=self)
        self.fmt_combo = QComboBox(parent=self)
        self.line_edit = QLineEdit(parent=self)
        self.text_edit = QTextEdit(parent=self)
        self.run_button = QToolButton(parent=self)

        # completer for the fmto widget
        self.fmt_combo.setEditable(True)
        self.fmt_combo.setInsertPolicy(QComboBox.NoInsert)
        self.fmto_completer = completer = QCompleter(
            ['time', 'lat', 'lon', 'lev'])
        completer.setCompletionMode(
            QCompleter.PopupCompletion)
        completer.activated[str].connect(self.set_fmto)
        if with_qt5:
            completer.setFilterMode(Qt.MatchContains)
        completer.setModel(QStandardItemModel())
        self.fmt_combo.setCompleter(completer)

        self.dim_widget = DimensionsWidget(parent=self)
        self.dim_widget.setVisible(False)

        self.multiline_button = QPushButton('Multiline', parent=self)
        self.multiline_button.setCheckable(True)

        self.yaml_cb = QCheckBox('Yaml syntax')
        self.yaml_cb.setChecked(True)

        self.keys_button = QPushButton('Keys', parent=self)
        self.summaries_button = QPushButton('Summaries', parent=self)
        self.docs_button = QPushButton('Docs', parent=self)

        self.grouped_cb = QCheckBox('grouped', parent=self)
        self.all_groups_cb = QCheckBox('all groups', parent=self)
        self.include_links_cb = QCheckBox('include links', parent=self)

        self.text_edit.setVisible(False)

        # ---------------------------------------------------------------------
        # -------------------------- Descriptions -----------------------------
        # ---------------------------------------------------------------------

        self.group_combo.setToolTip('Select the formatoption group')
        self.fmt_combo.setToolTip('Select the formatoption to update')
        self.line_edit.setToolTip(
            'Insert the value which what you want to update the selected '
            'formatoption and hit right button. The code is executed in the '
            'main console.')
        self.yaml_cb.setToolTip(
            "Use the yaml syntax for the values inserted in the above cell. "
            "Otherwise the content there is evaluated as a python expression "
            "in the terminal")
        self.text_edit.setToolTip(self.line_edit.toolTip())
        self.run_button.setIcon(QIcon(get_icon('run_arrow.png')))
        self.run_button.setToolTip('Update the selected formatoption')
        self.multiline_button.setToolTip(
            'Allow linebreaks in the text editor line above.')
        self.keys_button.setToolTip(
            'Show the formatoption keys in this group (or in all '
            'groups) in the help explorer')
        self.summaries_button.setToolTip(
            'Show the formatoption summaries in this group (or in all '
            'groups) in the help explorer')
        self.docs_button.setToolTip(
            'Show the formatoption documentations in this group (or in all '
            'groups) in the help explorer')
        self.grouped_cb.setToolTip(
            'Group the formatoptions before displaying them in the help '
            'explorer')
        self.all_groups_cb.setToolTip('Use all groups when displaying the '
                                      'keys, docs or summaries')
        self.include_links_cb.setToolTip(
            'Include links to remote documentations when showing the '
            'keys, docs and summaries in the help explorer (requires '
            'intersphinx)')

        # ---------------------------------------------------------------------
        # -------------------------- Connections ------------------------------
        # ---------------------------------------------------------------------
        self.group_combo.currentIndexChanged[int].connect(self.fill_fmt_combo)
        self.fmt_combo.currentIndexChanged[int].connect(self.show_fmt_info)
        self.fmt_combo.currentIndexChanged[int].connect(self.load_fmt_widget)
        self.fmt_combo.currentIndexChanged[int].connect(
            self.set_current_fmt_value)
        self.run_button.clicked.connect(self.run_code)
        self.line_edit.returnPressed.connect(self.run_button.click)
        self.multiline_button.clicked.connect(self.toggle_line_edit)
        self.keys_button.clicked.connect(
            partial(self.show_all_fmt_info, 'keys'))
        self.summaries_button.clicked.connect(
            partial(self.show_all_fmt_info, 'summaries'))
        self.docs_button.clicked.connect(
            partial(self.show_all_fmt_info, 'docs'))

        # ---------------------------------------------------------------------
        # ------------------------------ Layouts ------------------------------
        # ---------------------------------------------------------------------
        self.combos = QHBoxLayout()
        self.combos.addWidget(self.group_combo)
        self.combos.addWidget(self.fmt_combo)

        self.execs = QHBoxLayout()
        self.execs.addWidget(self.line_edit)
        self.execs.addWidget(self.text_edit)
        self.execs.addWidget(self.run_button)

        self.info_box = QHBoxLayout()
        self.info_box.addWidget(self.multiline_button)
        self.info_box.addWidget(self.yaml_cb)
        self.info_box.addStretch(0)
        for w in [self.keys_button, self.summaries_button, self.docs_button,
                  self.all_groups_cb, self.grouped_cb, self.include_links_cb]:
            self.info_box.addWidget(w)

        self.vbox = QVBoxLayout()
        self.vbox.addLayout(self.combos)
        self.vbox.addWidget(self.dim_widget)
        self.vbox.addLayout(self.execs)
        self.vbox.addLayout(self.info_box)

        self.vbox.setSpacing(0)

        self.setLayout(self.vbox)

        # fill with content
        self.fill_combos_from_project(psy.gcp())
        psy.Project.oncpchange.connect(self.fill_combos_from_project)
        rcParams.connect('fmt.sort_by_key', self.refill_from_rc)
コード例 #18
0
class ImageRotator(StraditizerControlBase, QWidget):
    """Widget to rotate the image

    This control mainly adds a QLineEdit :attr:`txt_rotate` to the
    :class:`straditize.widgets.StraditizerWidgets` to rotate the image.
    It also enables the user to specify the rotation angle using two
    connected :class:`~straditize.cross_mark.CrossMarks`. Here the user can
    decide between a horizontal alignment (:attr:`btn_rotate_horizontal`) or
    a vertical alignment (:attr:`btn_rotate_vertical`)"""

    _rotating = False
    _ha = False
    _va = False

    #: A QPushButton for horizontal alignment
    btn_rotate_horizontal = None

    #: A QPushButton for vertical alignment
    btn_rotate_vertical = None

    #: A QLineEdit to display the rotation angle
    txt_rotate = None

    @docstrings.dedent
    def __init__(self, straditizer_widgets, item=None, *args, **kwargs):
        """
        Parameters
        ----------
        %(StraditizerControlBase.init_straditizercontrol.parameters)s"""
        super(ImageRotator, self).__init__(*args, **kwargs)
        self.txt_rotate = QLineEdit()
        self.txt_rotate.setValidator(QDoubleValidator())

        self.btn_rotate_horizontal = QPushButton('Horizontal alignment')
        self.btn_rotate_horizontal.setToolTip(
            'Mark two points that should be on the same horizontal level '
            'and rotate the picture to achieve this.')

        self.btn_rotate_vertical = QPushButton('Vertical alignment')
        self.btn_rotate_vertical.setToolTip(
            'Mark two points that should be on the same vertical level '
            'and rotate the picture to achieve this.')

        self.init_straditizercontrol(straditizer_widgets, item)

        # ---------------------------------------------------------------------
        # --------------------------- Layouts ---------------------------------
        # ---------------------------------------------------------------------

        layout = QVBoxLayout()

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Rotate:'))
        hbox.addWidget(self.txt_rotate)
        layout.addLayout(hbox)

        layout.addWidget(self.btn_rotate_horizontal)
        layout.addWidget(self.btn_rotate_vertical)

        self.setLayout(layout)

        # ---------------------------------------------------------------------
        # --------------------------- Connections -----------------------------
        # ---------------------------------------------------------------------

        self.txt_rotate.textChanged.connect(self.start_rotation)
        self.btn_rotate_horizontal.clicked.connect(
            self.start_horizontal_alignment)
        self.btn_rotate_vertical.clicked.connect(
            self.start_vertical_alignment)

        self.widgets2disable = [self.txt_rotate, self.btn_rotate_horizontal,
                                self.btn_rotate_vertical]

    def should_be_enabled(self, w):
        if not self._rotating:
            return self.straditizer is not None
        elif w is self.txt_rotate:
            return True
        return False

    def start_rotation(self):
        """Start the rotation (if not already started)"""
        if not self._rotating:
            self._rotating = True
            self.connect2apply(self.rotate_image)
            self.connect2cancel(self.remove_marks,
                                self.straditizer.draw_figure)
            self.straditizer_widgets.apply_button.setText('Rotate')

    def draw_figure(self):
        self.straditizer.draw_figure()

    def start_horizontal_alignment(self):
        """Start the horizontal alignment"""
        self.start_rotation()
        self._ha = True
        self._start_alignment()

    def start_vertical_alignment(self):
        """Start the vertical alignment"""
        self.start_rotation()
        self._va = True
        self._start_alignment()

    def _start_alignment(self):
        def new_mark(pos):
            if len(self.straditizer.marks) == 2:
                return
            mark = cm.CrossMarks(pos, ax=stradi.ax, c='b')
            if self.straditizer.marks:
                marks = [mark] + self.straditizer.marks
                mark.connect_marks(marks, visible=True)
                self.update_txt_rotate(marks=marks)
            mark.moved.connect(self.update_txt_rotate)
            return [mark]

        stradi = self.straditizer
        stradi.marks = []

        stradi.mark_cids.add(stradi.fig.canvas.mpl_connect(
            'button_press_event', stradi._add_mark_event(new_mark)))
        stradi.mark_cids.add(stradi.fig.canvas.mpl_connect(
            'button_press_event', stradi._remove_mark_event))

    def update_txt_rotate(self, *args, marks=[], **kwargs):
        """Update the :attr:`txt_rotate` from the displayed cross marks"""
        stradi = self.straditizer
        marks = marks or stradi.marks
        if len(marks) != 2:
            return
        x0, y0 = marks[0].pos
        x1, y1 = marks[1].pos
        dx = x0 - x1
        dy = y0 - y1

        if self._ha:
            angle = np.arctan(dy / dx) if dx else np.sign(dy) * np.pi / 2.
            self.txt_rotate.setText('%1.3g' % np.rad2deg(angle))
        elif self._va:
            angle = np.arctan(dx / dy) if dy else np.sign(dx) * np.pi / 2.
            self.txt_rotate.setText('%1.3g' % -np.rad2deg(angle))

    def enable_or_disable_widgets(self, b):
        super(ImageRotator, self).enable_or_disable_widgets(b)
        if self._rotating:
            self.txt_rotate.setEnabled(self.should_be_enabled(self.txt_rotate))

    @property
    def angle(self):
        """The rotation angle from :attr:`txt_rotate` as a float"""
        angle = self.txt_rotate.text()
        if not angle.strip():
            return
        return float(angle.strip())

    def rotate_image(self):
        """Rotate the image based on the specified :attr:`angle`"""
        angle = self.angle
        if angle is None:
            return
        sw = self.straditizer_widgets
        answer = QMessageBox.Yes if sw.always_yes else QMessageBox.question(
            self, 'Restart project?',
            'This will close the straditizer and create new figures. '
            'Are you sure, you want to continue?')
        if answer == QMessageBox.Yes:
            image = self.straditizer.image.rotate(angle, expand=True)
            attrs = self.straditizer.attrs
            self.straditizer_widgets.close_straditizer()
            self.straditizer_widgets.menu_actions.open_straditizer(
                image, attrs=attrs)

        self._rotating = self._ha = self._va = False

    def remove_marks(self):
        """Remove the cross marks used for the rotation angle"""
        self._rotating = self._ha = self._va = False
        self.straditizer.remove_marks()
        self.straditizer_widgets.apply_button.setText('Apply')
コード例 #19
0
ファイル: samples_table.py プロジェクト: saraemp/straditize
    def __init__(self, straditizer, axes=None, *args, **kwargs):
        """
        Parameters
        ----------
        straditizer: weakref.ref
            The reference to the straditizer
        axes: matplotlib.axes.Axes
            The matplotlib axes corresponding to the marks
        """
        super(MultiCrossMarksEditor, self).__init__(*args, **kwargs)
        self.straditizer = straditizer
        straditizer = straditizer()
        self.error_msg = PyErrorMessage(self)

        #: Plot the reconstructed data
        self.cb_plot_lines = QCheckBox('Plot reconstruction')
        self.cb_plot_lines.setChecked(True)

        # A Checkbox to automatically zoom to the selection
        self.cb_zoom_to_selection = QCheckBox('Zoom to selection')

        # A Checkbox to automaticall hide the other marks
        self.cb_selection_only = QCheckBox('Selection only')

        # A Checkbox to automatically fit the selected cells to the selected
        # data
        self.cb_fit2selection = QCheckBox(
            'Fit selected cells to selected data')
        self.cb_fit2selection.setToolTip(
            'If checked, select cells from the table and click on one of the '
            'plots to update the table with the data at the selected position.'
        )

        # The table to display the DataFrame
        self.table = self.create_view(axes=axes)

        # format line edit
        self.format_editor = QLineEdit()
        self.format_editor.setText(self.table.model()._format)

        # format update button
        self.btn_change_format = QPushButton('Update')
        self.btn_change_format.setEnabled(False)

        self.btn_save = QPushButton('Save')
        self.btn_save.setToolTip('Save the samples and continue editing')

        # ---------------------------------------------------------------------
        # ------------------------ layout --------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()
        self.top_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.cb_zoom_to_selection)
        hbox.addWidget(self.cb_selection_only)
        hbox.addWidget(self.cb_fit2selection)
        hbox.addWidget(self.cb_plot_lines)
        hbox.addStretch(0)
        vbox.addLayout(hbox)
        vbox.addWidget(self.table)
        self.bottom_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.format_editor)
        hbox.addWidget(self.btn_change_format)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_save)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # ------------------------ Connections --------------------------------
        # ---------------------------------------------------------------------
        self.format_editor.textChanged.connect(self.toggle_fmt_button)
        self.btn_change_format.clicked.connect(self.update_format)
        self.btn_save.clicked.connect(self.save_samples)
        straditizer.mark_added.connect(self.table.model().load_new_marks)
        straditizer.mark_removed.connect(self.table.model().remove_mark)
        self.table.selectionModel().selectionChanged.connect(
            self.maybe_zoom_to_selection)
        self.table.frozen_table_view.selectionModel().selectionChanged.connect(
            self.maybe_zoom_to_selection)
        self.table.selectionModel().selectionChanged.connect(
            self.maybe_show_selection_only)
        self.table.frozen_table_view.selectionModel().selectionChanged.connect(
            self.maybe_show_selection_only)

        self.cb_zoom_to_selection.stateChanged.connect(
            self.toggle_cb_zoom_to_selection)
        self.cb_selection_only.stateChanged.connect(
            self.toggle_cb_selection_only)
        self.cb_fit2selection.stateChanged.connect(self.toggle_fit2selection)
        self.cb_plot_lines.stateChanged.connect(self.toggle_plot_lines)

        self.toggle_plot_lines()
コード例 #20
0
ファイル: colors.py プロジェクト: sourabhmet/psy-simple
class NormalizationWidget(QWidget):
    """A simple widget representing a boundary norm"""

    def __init__(self, parent, norm):
        QWidget.__init__(self, parent)
        self.norm = norm

        validator = QDoubleValidator()
        self.txt_min = QLineEdit()
        self.txt_min.setValidator(validator)
        self.txt_max = QLineEdit()
        self.txt_max.setValidator(validator)

        self.lbl_linthresh = QLabel('linthresh:')
        self.txt_linthresh = QLineEdit()  # linthresh for SymLogNorm
        self.txt_linthresh.setValidator(validator)
        self.txt_linthresh.setToolTip(
            'The threshold for linear scaling. Within this distance from 0, '
            'the scaling will be linear, not logarithmic.')

        self.lbl_gamma = QLabel('gamma:')
        self.txt_gamma = QLineEdit()  # gamma for PowerNorm
        self.txt_gamma.setValidator(validator)
        self.txt_gamma.setToolTip('The power value for the PowerNorm')

        self.fill_from_norm()

        hbox = QHBoxLayout()
        hbox.addWidget(QLabel('Min.:'))
        hbox.addWidget(self.txt_min)
        hbox.addWidget(QLabel('Max.:'))
        hbox.addWidget(self.txt_max)
        hbox.addWidget(self.lbl_linthresh)
        hbox.addWidget(self.txt_linthresh)
        hbox.addWidget(self.lbl_gamma)
        hbox.addWidget(self.txt_gamma)
        self.setLayout(hbox)

        self.txt_min.textChanged.connect(self.set_obj)
        self.txt_max.textChanged.connect(self.set_obj)
        self.txt_linthresh.textChanged.connect(self.set_obj)
        self.txt_gamma.textChanged.connect(self.set_obj)

    def fill_from_norm(self):
        norm = self.norm
        if norm.vmin is not None:
            self.txt_min.setText('%1.6g' % norm.vmin)
        if norm.vmax is not None:
            self.txt_max.setText('%1.6g' % norm.vmax)
        if isinstance(self.norm, mcol.SymLogNorm):
            self.txt_linthresh.setVisible(True)
            self.txt_linthresh.setEnabled(True)
            self.lbl_linthresh.setVisible(True)
            self.txt_linthresh.setText('%1.6g' % norm.linthresh)
        else:
            self.txt_linthresh.setVisible(False)
            self.txt_linthresh.setEnabled(False)
            self.lbl_linthresh.setVisible(False)
        if isinstance(norm, mcol.PowerNorm):
            self.txt_gamma.setVisible(True)
            self.txt_gamma.setEnabled(True)
            self.lbl_gamma.setVisible(True)
            self.txt_gamma.setText('%1.6g' % norm.gamma)
        else:
            self.txt_gamma.setVisible(False)
            self.txt_gamma.setEnabled(False)
            self.lbl_gamma.setVisible(False)

    def set_obj(self):
        cls = self.norm.__class__
        if issubclass(cls, mcol.PowerNorm):
            args = [float(self.txt_gamma.text().strip() or 1.0)]
        elif issubclass(cls, mcol.SymLogNorm):
            args = [float(self.txt_linthresh.text().strip() or 1e-3)]
        else:
            args = []
        vmin = vmax = None
        if self.txt_min.text().strip():
            vmin = float(self.txt_min.text().strip())
        if self.txt_max.text().strip():
            vmax = float(self.txt_max.text().strip())
        try:
            norm = cls(*args, vmin=vmin, vmax=vmax)
        except Exception:
            pass
        else:
            self.parent().set_obj(norm)
コード例 #21
0
    def __init__(self, *args, **kwargs):
        super(DataFrameEditor, self).__init__(*args, **kwargs)
        self.error_msg = PyErrorMessage(self)

        # Label for displaying the DataFrame size
        self.lbl_size = QLabel()

        # A Checkbox for enabling and disabling the editability of the index
        self.cb_index_editable = QCheckBox('Index editable')

        # A checkbox for enabling and disabling the change of data types
        self.cb_dtypes_changeable = QCheckBox('Datatypes changeable')

        # A checkbox for enabling and disabling sorting
        self.cb_enable_sort = QCheckBox('Enable sorting')

        # A button to open a dataframe from the file
        self.btn_open_df = QToolButton(parent=self)
        self.btn_open_df.setIcon(QIcon(get_icon('run_arrow.png')))
        self.btn_open_df.setToolTip('Open a DataFrame from your disk')

        self.btn_from_console = LoadFromConsoleButton(pd.DataFrame)
        self.btn_from_console.setToolTip('Show a DataFrame from the console')

        # The table to display the DataFrame
        self.table = DataFrameView(pd.DataFrame(), self)

        # format line edit
        self.format_editor = QLineEdit()
        self.format_editor.setText(self.table.model()._format)

        # format update button
        self.btn_change_format = QPushButton('Update')
        self.btn_change_format.setEnabled(False)

        # table clearing button
        self.btn_clear = QPushButton('Clear')
        self.btn_clear.setToolTip(
            'Clear the table and disconnect from the DataFrame')

        # refresh button
        self.btn_refresh = QToolButton()
        self.btn_refresh.setIcon(QIcon(get_icon('refresh.png')))
        self.btn_refresh.setToolTip('Refresh the table')

        # close button
        self.btn_close = QPushButton('Close')
        self.btn_close.setToolTip('Close this widget permanentely')

        # ---------------------------------------------------------------------
        # ------------------------ layout --------------------------------
        # ---------------------------------------------------------------------
        vbox = QVBoxLayout()
        self.top_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.cb_index_editable)
        hbox.addWidget(self.cb_dtypes_changeable)
        hbox.addWidget(self.cb_enable_sort)
        hbox.addWidget(self.lbl_size)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_open_df)
        hbox.addWidget(self.btn_from_console)
        vbox.addLayout(hbox)
        vbox.addWidget(self.table)
        self.bottom_hbox = hbox = QHBoxLayout()
        hbox.addWidget(self.format_editor)
        hbox.addWidget(self.btn_change_format)
        hbox.addStretch(0)
        hbox.addWidget(self.btn_clear)
        hbox.addWidget(self.btn_close)
        hbox.addWidget(self.btn_refresh)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # ---------------------------------------------------------------------
        # ------------------------ Connections --------------------------------
        # ---------------------------------------------------------------------
        self.cb_dtypes_changeable.stateChanged.connect(
            self.set_dtypes_changeable)
        self.cb_index_editable.stateChanged.connect(self.set_index_editable)
        self.btn_from_console.object_loaded.connect(self._open_ds_from_console)
        self.rows_inserted.connect(lambda i, n: self.set_lbl_size_text())
        self.format_editor.textChanged.connect(self.toggle_fmt_button)
        self.btn_change_format.clicked.connect(self.update_format)
        self.btn_clear.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(self.clear_table)
        self.btn_close.clicked.connect(lambda: self.close())
        self.btn_refresh.clicked.connect(self.table.reset_model)
        self.btn_open_df.clicked.connect(self._open_dataframe)
        self.table.set_index_action.triggered.connect(
            self.update_index_editable)
        self.table.append_index_action.triggered.connect(
            self.update_index_editable)
        self.cb_enable_sort.stateChanged.connect(
            self.table.setSortingEnabled)
コード例 #22
0
class FormatoptionWidget(QWidget, DockMixin):
    """
    Widget to update the formatoptions of the current project

    This widget, mainly made out of a combobox for the formatoption group,
    a combobox for the formatoption, and a one-line text editor, is designed
    for updating the selected formatoptions for the current subproject.

    The widget is connected to the :attr:`psyplot.project.Project.oncpchange`
    signal and refills the comboboxes if the current subproject changes.

    The one-line text editor accepts python code that will be executed in side
    the given `shell`.
    """

    no_fmtos_update = _temp_bool_prop('no_fmtos_update',
                                      """update the fmto combo box or not""")

    #: The combobox for the formatoption groups
    group_combo = None

    #: The combobox for the formatoptions
    fmt_combo = None

    #: The help_explorer to display the documentation of the formatoptions
    help_explorer = None

    #: The shell to execute the update of the formatoptions in the current
    #: project
    shell = None

    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        help_explorer: psyplot_gui.help_explorer.HelpExplorer
            The help explorer to show the documentation of one formatoption
        shell: IPython.core.interactiveshell.InteractiveShell
            The shell that can be used to update the current subproject via::

                psy.gcp().update(**kwargs)

            where ``**kwargs`` is defined through the selected formatoption
            in the :attr:`fmt_combo` combobox and the value in the
            :attr:`line_edit` editor
        ``*args, **kwargs``
            Any other keyword for the QWidget class
        """
        help_explorer = kwargs.pop('help_explorer', None)
        shell = kwargs.pop('shell', None)
        super(FormatoptionWidget, self).__init__(*args, **kwargs)
        self.help_explorer = help_explorer
        self.shell = shell

        # ---------------------------------------------------------------------
        # -------------------------- Child widgets ----------------------------
        # ---------------------------------------------------------------------
        self.group_combo = QComboBox(parent=self)
        self.fmt_combo = QComboBox(parent=self)
        self.line_edit = QLineEdit(parent=self)
        self.run_button = QToolButton(parent=self)

        self.keys_button = QPushButton('Formatoption keys', parent=self)
        self.summaries_button = QPushButton('Summaries', parent=self)
        self.docs_button = QPushButton('Docs', parent=self)

        self.grouped_cb = QCheckBox('grouped', parent=self)
        self.all_groups_cb = QCheckBox('all groups', parent=self)
        self.include_links_cb = QCheckBox('include links', parent=self)

        # ---------------------------------------------------------------------
        # -------------------------- Descriptions -----------------------------
        # ---------------------------------------------------------------------

        self.group_combo.setToolTip('Select the formatoption group')
        self.fmt_combo.setToolTip('Select the formatoption to update')
        self.line_edit.setToolTip(
            'Insert the value which what you want to update the selected '
            'formatoption and hit right button. The code is executed in the '
            'main console.')
        self.run_button.setIcon(QIcon(get_icon('run_arrow.png')))
        self.run_button.setToolTip('Update the selected formatoption')
        self.keys_button.setToolTip(
            'Show the formatoption keys in this group (or in all '
            'groups) in the help explorer')
        self.summaries_button.setToolTip(
            'Show the formatoption summaries in this group (or in all '
            'groups) in the help explorer')
        self.docs_button.setToolTip(
            'Show the formatoption documentations in this group (or in all '
            'groups) in the help explorer')
        self.grouped_cb.setToolTip(
            'Group the formatoptions before displaying them in the help '
            'explorer')
        self.all_groups_cb.setToolTip('Use all groups when displaying the '
                                      'keys, docs or summaries')
        self.include_links_cb.setToolTip(
            'Include links to remote documentations when showing the '
            'keys, docs and summaries in the help explorer (requires '
            'intersphinx)')

        # ---------------------------------------------------------------------
        # -------------------------- Connections ------------------------------
        # ---------------------------------------------------------------------
        self.group_combo.currentIndexChanged[int].connect(self.fill_fmt_combo)
        self.fmt_combo.currentIndexChanged[int].connect(self.show_fmt_info)
        self.run_button.clicked.connect(self.run_code)
        self.line_edit.returnPressed.connect(self.run_button.click)
        self.keys_button.clicked.connect(
            partial(self.show_all_fmt_info, 'keys'))
        self.summaries_button.clicked.connect(
            partial(self.show_all_fmt_info, 'summaries'))
        self.docs_button.clicked.connect(
            partial(self.show_all_fmt_info, 'docs'))

        # ---------------------------------------------------------------------
        # ------------------------------ Layouts ------------------------------
        # ---------------------------------------------------------------------
        self.combos = QHBoxLayout()
        self.combos.addWidget(self.group_combo)
        self.combos.addWidget(self.fmt_combo)

        self.execs = QHBoxLayout()
        self.execs.addWidget(self.line_edit)
        self.execs.addWidget(self.run_button)

        self.info_box = QHBoxLayout()
        self.info_box.addStretch(0)
        for w in [
                self.keys_button, self.summaries_button, self.docs_button,
                self.all_groups_cb, self.grouped_cb, self.include_links_cb
        ]:
            self.info_box.addWidget(w)

        self.vbox = QVBoxLayout()
        self.vbox.addLayout(self.combos)
        self.vbox.addLayout(self.execs)
        self.vbox.addLayout(self.info_box)

        self.setLayout(self.vbox)

        # fill with content
        self.fill_combos_from_project(psy.gcp())
        psy.Project.oncpchange.connect(self.fill_combos_from_project)

    def fill_combos_from_project(self, project):
        """Fill :attr:`group_combo` and :attr:`fmt_combo` from a project

        Parameters
        ----------
        project: psyplot.project.Project
            The project to use"""
        current_text = self.group_combo.currentText()
        with self.no_fmtos_update:
            self.group_combo.clear()
            if project is None or project.is_main or not len(project.plotters):
                self.fmt_combo.clear()
                self.groups = []
                self.fmtos = []
                self.line_edit.setEnabled(False)
                return
            self.line_edit.setEnabled(True)
            # get dimensions
            coords = sorted(project.coords_intersect)
            coords_name = [COORDSGROUP] if coords else []
            coords_verbose = ['Dimensions'] if coords else []
            coords = [coords] if coords else []

            # get formatoptions and group them alphabetically
            grouped_fmts = defaultdict(list)
            for fmto in project._fmtos:
                grouped_fmts[fmto.group].append(fmto)
            for val in six.itervalues(grouped_fmts):
                val.sort(key=self.get_name)
            grouped_fmts = OrderedDict(
                sorted(six.iteritems(grouped_fmts),
                       key=lambda t: psyp.groups.get(t[0], t[0])))
            fmt_groups = list(grouped_fmts.keys())
            # save original names
            self.groups = coords_name + [ALLGROUP] + fmt_groups
            # save verbose group names (which are used in the combo box)
            self.groupnames = coords_verbose + ['All formatoptions'] + list(
                map(lambda s: psyp.groups.get(s, s), fmt_groups))
            # save formatoptions
            fmtos = list(grouped_fmts.values())
            self.fmtos = coords + [sorted(chain(*fmtos), key=self.get_name)
                                   ] + fmtos
            self.group_combo.addItems(self.groupnames)
            ind = self.group_combo.findText(current_text)
            self.group_combo.setCurrentIndex(ind if ind >= 0 else 0)
        self.fill_fmt_combo(self.group_combo.currentIndex())

    def get_name(self, fmto):
        """Get the name of a :class:`psyplot.plotter.Formatoption` instance"""
        if isinstance(fmto, six.string_types):
            return fmto
        return '%s (%s)' % (fmto.name, fmto.key) if fmto.name else fmto.key

    def fill_fmt_combo(self, i):
        """Fill the :attr:`fmt_combo` combobox based on the current group name
        """
        if not self.no_fmtos_update:
            with self.no_fmtos_update:
                current_text = self.fmt_combo.currentText()
                self.fmt_combo.clear()
                self.fmt_combo.addItems(list(map(self.get_name,
                                                 self.fmtos[i])))
                ind = self.fmt_combo.findText(current_text)
                self.fmt_combo.setCurrentIndex(ind if ind >= 0 else 0)
            self.show_fmt_info(self.fmt_combo.currentIndex())

    def show_fmt_info(self, i):
        """Show the documentation of the formatoption in the help explorer
        """
        group_ind = self.group_combo.currentIndex()
        if (not self.no_fmtos_update
                and self.groups[group_ind] != COORDSGROUP):
            fmto = self.fmtos[self.group_combo.currentIndex()][i]
            fmto.plotter.show_docs(
                fmto.key, include_links=self.include_links_cb.isChecked())

    def run_code(self):
        """Run the update of the project inside the :attr:`shell`"""
        text = str(self.line_edit.text())
        if not text or not self.fmtos:
            return
        group_ind = self.group_combo.currentIndex()
        if self.groups[group_ind] == COORDSGROUP:
            key = self.fmtos[group_ind][self.fmt_combo.currentIndex()]
            param = 'dims'
        else:
            key = self.fmtos[group_ind][self.fmt_combo.currentIndex()].key
            param = 'fmt'
        e = ExecutionResult()
        self.shell.run_code(
            "psy.gcp().update(%s={'%s': %s})" % (param, key, text), e)
        e.raise_error()

    def show_all_fmt_info(self, what):
        """Show the keys, summaries or docs of the formatoptions

        Calling this function let's the help browser show the documentation
        etc. of all docs or only the selected group determined by the state of
        the :attr:`grouped_cb` and :attr:`all_groups_cb` checkboxes

        Parameters
        ----------
        what: {'keys', 'summaries', 'docs'}
            Determines what to show"""
        if not self.fmtos:
            return
        if self.all_groups_cb.isChecked():
            fmtos = list(
                chain(*(fmto_group for i, fmto_group in enumerate(self.fmtos)
                        if not self.groups[i] in [ALLGROUP, COORDSGROUP])))
        else:
            if self.groups[self.group_combo.currentIndex()] == COORDSGROUP:
                return
            fmtos = self.fmtos[self.group_combo.currentIndex()]
        plotter = fmtos[0].plotter
        getattr(plotter, 'show_' +
                what)([fmto.key for fmto in fmtos],
                      grouped=self.grouped_cb.isChecked(),
                      include_links=self.include_links_cb.isChecked())
コード例 #23
0
    def __init__(self, straditizer_widgets, item, *args, **kwargs):
        """
        Parameters
        ----------
        %(StraditizerControlBase.init_straditizercontrol.parameters)s
        """
        super(MarkerControl, self).__init__(*args, **kwargs)
        self.init_straditizercontrol(straditizer_widgets, item)
        vbox = QVBoxLayout()

        # auto hide button
        box_hide = QGridLayout()
        self.cb_auto_hide = QCheckBox('Auto-hide')
        self.cb_show_connected = QCheckBox('Show additionals')
        self.cb_drag_hline = QCheckBox('Drag in y-direction')
        self.cb_drag_vline = QCheckBox('Drag in x-direction')
        self.cb_selectable_hline = QCheckBox('Horizontal lines selectable')
        self.cb_selectable_vline = QCheckBox('Vertical lines selectable')
        self.cb_show_hlines = QCheckBox('Show horizontal lines')
        self.cb_show_vlines = QCheckBox('Show vertical lines')
        box_hide.addWidget(self.cb_auto_hide, 0, 0)
        box_hide.addWidget(self.cb_show_connected, 0, 1)
        box_hide.addWidget(self.cb_show_vlines, 1, 0)
        box_hide.addWidget(self.cb_show_hlines, 1, 1)
        box_hide.addWidget(self.cb_drag_hline, 2, 0)
        box_hide.addWidget(self.cb_drag_vline, 2, 1)
        box_hide.addWidget(self.cb_selectable_hline, 3, 0)
        box_hide.addWidget(self.cb_selectable_vline, 3, 1)
        vbox.addLayout(box_hide)

        style_box = QGridLayout()
        style_box.addWidget(QLabel('Unselected:'), 0, 1)
        selected_label = QLabel('Selected:')
        style_box.addWidget(selected_label, 0, 2)
        max_width = selected_label.sizeHint().width()

        # line color
        self.lbl_color_select = ColorLabel()
        self.lbl_color_unselect = ColorLabel()
        self.lbl_color_select.setMaximumWidth(max_width)
        self.lbl_color_unselect.setMaximumWidth(max_width)
        style_box.addWidget(QLabel('Line color:'), 1, 0)
        style_box.addWidget(self.lbl_color_unselect, 1, 1)
        style_box.addWidget(self.lbl_color_select, 1, 2)

        # line width
        self.txt_line_width = QLineEdit()
        self.txt_line_width_select = QLineEdit()

        validator = QDoubleValidator()
        validator.setBottom(0)
        self.txt_line_width.setValidator(validator)
        self.txt_line_width_select.setValidator(validator)

        style_box.addWidget(QLabel('Line width:'), 2, 0)
        style_box.addWidget(self.txt_line_width, 2, 1)
        style_box.addWidget(self.txt_line_width_select, 2, 2)

        vbox.addLayout(style_box)

        # line style
        hbox_line_style = QHBoxLayout()
        hbox_line_style.addWidget(QLabel('Line style'))
        self.combo_line_style = QComboBox()
        hbox_line_style.addWidget(self.combo_line_style)
        vbox.addLayout(hbox_line_style)
        self.fill_linestyles()
        self.combo_line_style.setSizeAdjustPolicy(QComboBox.AdjustToContents)

        # marker style
        hbox_marker_style = QHBoxLayout()
        hbox_marker_style.addWidget(QLabel('Marker size'))
        self.txt_marker_size = QLineEdit()
        self.txt_marker_size.setMinimumWidth(40)
        self.txt_marker_size.setText(str(mpl.rcParams['lines.markersize']))
        validator = QDoubleValidator()
        validator.setBottom(0)
        self.txt_marker_size.setValidator(validator)
        hbox_marker_style.addWidget(self.txt_marker_size)
        hbox_marker_style.addWidget(QLabel('Marker style'))
        self.combo_marker_style = QComboBox()
        hbox_marker_style.addWidget(self.combo_marker_style)
        vbox.addLayout(hbox_marker_style)

        self.setLayout(vbox)

        self.widgets2disable = [
            self.lbl_color_select, self.lbl_color_unselect, self.cb_auto_hide,
            self.txt_line_width, self.txt_line_width_select,
            self.combo_line_style, self.cb_show_connected,
            self.txt_marker_size, self.combo_marker_style, self.cb_drag_vline,
            self.cb_drag_hline, self.cb_selectable_vline,
            self.cb_selectable_hline, self.cb_show_hlines, self.cb_show_vlines
        ]

        self.fill_markerstyles()

        self.tb_actions = []

        # ---------------------------------------------------------------------
        # ---------------------------- connections ----------------------------
        # ---------------------------------------------------------------------

        self.lbl_color_select.color_changed.connect(self.change_select_colors)
        self.lbl_color_unselect.color_changed.connect(
            self.change_unselect_colors)
        self.txt_line_width.textChanged.connect(self.change_line_widths)
        self.txt_line_width_select.textChanged.connect(
            self.change_selection_line_widths)
        self.cb_auto_hide.stateChanged.connect(self.change_auto_hide)
        self.combo_marker_style.currentIndexChanged.connect(
            self.change_marker_style)
        self.combo_line_style.currentIndexChanged.connect(
            self.change_line_style)
        self.txt_marker_size.textChanged.connect(self.change_marker_size)
        self.cb_show_connected.stateChanged.connect(
            self.change_show_connected_artists)
        self.cb_drag_hline.stateChanged.connect(self.change_hline_draggable)
        self.cb_drag_vline.stateChanged.connect(self.change_vline_draggable)
        self.cb_selectable_hline.stateChanged.connect(
            self.change_hline_selectable)
        self.cb_selectable_vline.stateChanged.connect(
            self.change_vline_selectable)
        self.cb_show_vlines.stateChanged.connect(self.change_show_vlines)
        self.cb_show_hlines.stateChanged.connect(self.change_show_hlines)
コード例 #24
0
class FormatoptionWidget(QWidget, DockMixin):
    """
    Widget to update the formatoptions of the current project

    This widget, mainly made out of a combobox for the formatoption group,
    a combobox for the formatoption, and a text editor, is designed
    for updating the selected formatoptions for the current subproject.

    The widget is connected to the :attr:`psyplot.project.Project.oncpchange`
    signal and refills the comboboxes if the current subproject changes.

    The text editor either accepts python code that will be executed by the
    given `console`, or yaml code.
    """

    no_fmtos_update = _temp_bool_prop('no_fmtos_update',
                                      """update the fmto combo box or not""")

    #: The combobox for the formatoption groups
    group_combo = None

    #: The combobox for the formatoptions
    fmt_combo = None

    #: The help_explorer to display the documentation of the formatoptions
    help_explorer = None

    #: The formatoption specific widget that is loaded from the formatoption
    fmt_widget = None

    #: A line edit for updating the formatoptions
    line_edit = None

    #: A multiline text editor for updating the formatoptions
    text_edit = None

    #: A button to switch between :attr:`line_edit` and :attr:`text_edit`
    multiline_button = None

    @property
    def shell(self):
        """The shell to execute the update of the formatoptions in the current
        project"""
        return self.console.kernel_manager.kernel.shell

    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        help_explorer: psyplot_gui.help_explorer.HelpExplorer
            The help explorer to show the documentation of one formatoption
        console: psyplot_gui.console.ConsoleWidget
            The console that can be used to update the current subproject via::

                psy.gcp().update(**kwargs)

            where ``**kwargs`` is defined through the selected formatoption
            in the :attr:`fmt_combo` combobox and the value in the
            :attr:`line_edit` editor
        ``*args, **kwargs``
            Any other keyword for the QWidget class
        """
        help_explorer = kwargs.pop('help_explorer', None)
        console = kwargs.pop('console', None)
        super(FormatoptionWidget, self).__init__(*args, **kwargs)
        self.help_explorer = help_explorer
        self.console = console
        self.error_msg = PyErrorMessage(self)

        # ---------------------------------------------------------------------
        # -------------------------- Child widgets ----------------------------
        # ---------------------------------------------------------------------
        self.group_combo = QComboBox(parent=self)
        self.fmt_combo = QComboBox(parent=self)
        self.line_edit = QLineEdit(parent=self)
        self.text_edit = QTextEdit(parent=self)
        self.run_button = QToolButton(parent=self)

        # completer for the fmto widget
        self.fmt_combo.setEditable(True)
        self.fmt_combo.setInsertPolicy(QComboBox.NoInsert)
        self.fmto_completer = completer = QCompleter(
            ['time', 'lat', 'lon', 'lev'])
        completer.setCompletionMode(QCompleter.PopupCompletion)
        completer.activated[str].connect(self.set_fmto)
        if with_qt5:
            completer.setFilterMode(Qt.MatchContains)
        completer.setModel(QStandardItemModel())
        self.fmt_combo.setCompleter(completer)

        self.dim_widget = DimensionsWidget(parent=self)
        self.dim_widget.setVisible(False)

        self.multiline_button = QPushButton('Multiline', parent=self)
        self.multiline_button.setCheckable(True)

        self.yaml_cb = QCheckBox('Yaml syntax')
        self.yaml_cb.setChecked(True)

        self.keys_button = QPushButton('Keys', parent=self)
        self.summaries_button = QPushButton('Summaries', parent=self)
        self.docs_button = QPushButton('Docs', parent=self)

        self.grouped_cb = QCheckBox('grouped', parent=self)
        self.all_groups_cb = QCheckBox('all groups', parent=self)
        self.include_links_cb = QCheckBox('include links', parent=self)

        self.text_edit.setVisible(False)

        # ---------------------------------------------------------------------
        # -------------------------- Descriptions -----------------------------
        # ---------------------------------------------------------------------

        self.group_combo.setToolTip('Select the formatoption group')
        self.fmt_combo.setToolTip('Select the formatoption to update')
        self.line_edit.setToolTip(
            'Insert the value which what you want to update the selected '
            'formatoption and hit right button. The code is executed in the '
            'main console.')
        self.yaml_cb.setToolTip(
            "Use the yaml syntax for the values inserted in the above cell. "
            "Otherwise the content there is evaluated as a python expression "
            "in the terminal")
        self.text_edit.setToolTip(self.line_edit.toolTip())
        self.run_button.setIcon(QIcon(get_icon('run_arrow.png')))
        self.run_button.setToolTip('Update the selected formatoption')
        self.multiline_button.setToolTip(
            'Allow linebreaks in the text editor line above.')
        self.keys_button.setToolTip(
            'Show the formatoption keys in this group (or in all '
            'groups) in the help explorer')
        self.summaries_button.setToolTip(
            'Show the formatoption summaries in this group (or in all '
            'groups) in the help explorer')
        self.docs_button.setToolTip(
            'Show the formatoption documentations in this group (or in all '
            'groups) in the help explorer')
        self.grouped_cb.setToolTip(
            'Group the formatoptions before displaying them in the help '
            'explorer')
        self.all_groups_cb.setToolTip('Use all groups when displaying the '
                                      'keys, docs or summaries')
        self.include_links_cb.setToolTip(
            'Include links to remote documentations when showing the '
            'keys, docs and summaries in the help explorer (requires '
            'intersphinx)')

        # ---------------------------------------------------------------------
        # -------------------------- Connections ------------------------------
        # ---------------------------------------------------------------------
        self.group_combo.currentIndexChanged[int].connect(self.fill_fmt_combo)
        self.fmt_combo.currentIndexChanged[int].connect(self.show_fmt_info)
        self.fmt_combo.currentIndexChanged[int].connect(self.load_fmt_widget)
        self.fmt_combo.currentIndexChanged[int].connect(
            self.set_current_fmt_value)
        self.run_button.clicked.connect(self.run_code)
        self.line_edit.returnPressed.connect(self.run_button.click)
        self.multiline_button.clicked.connect(self.toggle_line_edit)
        self.keys_button.clicked.connect(
            partial(self.show_all_fmt_info, 'keys'))
        self.summaries_button.clicked.connect(
            partial(self.show_all_fmt_info, 'summaries'))
        self.docs_button.clicked.connect(
            partial(self.show_all_fmt_info, 'docs'))

        # ---------------------------------------------------------------------
        # ------------------------------ Layouts ------------------------------
        # ---------------------------------------------------------------------
        self.combos = QHBoxLayout()
        self.combos.addWidget(self.group_combo)
        self.combos.addWidget(self.fmt_combo)

        self.execs = QHBoxLayout()
        self.execs.addWidget(self.line_edit)
        self.execs.addWidget(self.text_edit)
        self.execs.addWidget(self.run_button)

        self.info_box = QHBoxLayout()
        self.info_box.addWidget(self.multiline_button)
        self.info_box.addWidget(self.yaml_cb)
        self.info_box.addStretch(0)
        for w in [
                self.keys_button, self.summaries_button, self.docs_button,
                self.all_groups_cb, self.grouped_cb, self.include_links_cb
        ]:
            self.info_box.addWidget(w)

        self.vbox = QVBoxLayout()
        self.vbox.addLayout(self.combos)
        self.vbox.addWidget(self.dim_widget)
        self.vbox.addLayout(self.execs)
        self.vbox.addLayout(self.info_box)

        self.vbox.setSpacing(0)

        self.setLayout(self.vbox)

        # fill with content
        self.fill_combos_from_project(psy.gcp())
        psy.Project.oncpchange.connect(self.fill_combos_from_project)
        rcParams.connect('fmt.sort_by_key', self.refill_from_rc)

    def refill_from_rc(self, sort_by_key):
        from psyplot.project import gcp
        self.fill_combos_from_project(gcp())

    def fill_combos_from_project(self, project):
        """Fill :attr:`group_combo` and :attr:`fmt_combo` from a project

        Parameters
        ----------
        project: psyplot.project.Project
            The project to use"""
        if rcParams['fmt.sort_by_key']:

            def sorter(fmto):
                return fmto.key
        else:
            sorter = self.get_name

        current_text = self.group_combo.currentText()
        with self.no_fmtos_update:
            self.group_combo.clear()
            if project is None or project.is_main or not len(project):
                self.fmt_combo.clear()
                self.groups = []
                self.fmtos = []
                self.line_edit.setEnabled(False)
                return
            self.line_edit.setEnabled(True)
            # get dimensions
            it_vars = chain.from_iterable(arr.psy.iter_base_variables
                                          for arr in project.arrays)
            dims = next(it_vars).dims
            sdims = set(dims)
            for var in it_vars:
                sdims.intersection_update(var.dims)
            coords = [d for d in dims if d in sdims]
            coords_name = [COORDSGROUP] if coords else []
            coords_verbose = ['Dimensions'] if coords else []
            coords = [coords] if coords else []

            if len(project.plotters):
                # get formatoptions and group them alphabetically
                grouped_fmts = defaultdict(list)
                for fmto in project._fmtos:
                    grouped_fmts[fmto.group].append(fmto)
                for val in six.itervalues(grouped_fmts):
                    val.sort(key=sorter)
                grouped_fmts = OrderedDict(
                    sorted(six.iteritems(grouped_fmts),
                           key=lambda t: psyp.groups.get(t[0], t[0])))
                fmt_groups = list(grouped_fmts.keys())
                # save original names
                self.groups = coords_name + [ALLGROUP] + fmt_groups
                # save verbose group names (which are used in the combo box)
                self.groupnames = (
                    coords_verbose + ['All formatoptions'] +
                    list(map(lambda s: psyp.groups.get(s, s), fmt_groups)))
                # save formatoptions
                fmtos = list(grouped_fmts.values())
                self.fmtos = coords + [sorted(chain(*fmtos), key=sorter)
                                       ] + fmtos
            else:
                self.groups = coords_name
                self.groupnames = coords_verbose
                self.fmtos = coords
            self.group_combo.addItems(self.groupnames)
            ind = self.group_combo.findText(current_text)
            self.group_combo.setCurrentIndex(ind if ind >= 0 else 0)
        self.fill_fmt_combo(self.group_combo.currentIndex())

    def get_name(self, fmto):
        """Get the name of a :class:`psyplot.plotter.Formatoption` instance"""
        if isinstance(fmto, six.string_types):
            return fmto
        return '%s (%s)' % (fmto.name, fmto.key) if fmto.name else fmto.key

    @property
    def fmto(self):
        return self.fmtos[self.group_combo.currentIndex()][
            self.fmt_combo.currentIndex()]

    @fmto.setter
    def fmto(self, value):
        name = self.get_name(value)
        for i, fmtos in enumerate(self.fmtos):
            if i == 1:  # all formatoptions
                continue
            if name in map(self.get_name, fmtos):
                with self.no_fmtos_update:
                    self.group_combo.setCurrentIndex(i)
                self.fill_fmt_combo(i, name)
                return

    def toggle_line_edit(self):
        """Switch between the :attr:`line_edit` and :attr:`text_edit`

        This method is called when the :attr:`multiline_button` is clicked
        and switches between the single line :attr:``line_edit` and the
        multiline :attr:`text_edit`
        """
        # switch to multiline text edit
        if (self.multiline_button.isChecked()
                and not self.text_edit.isVisible()):
            self.line_edit.setVisible(False)
            self.text_edit.setVisible(True)
            self.text_edit.setPlainText(self.line_edit.text())
        elif (not self.multiline_button.isChecked()
              and not self.line_edit.isVisible()):
            self.line_edit.setVisible(True)
            self.text_edit.setVisible(False)
            self.line_edit.setText(self.text_edit.toPlainText())

    def fill_fmt_combo(self, i, current_text=None):
        """Fill the :attr:`fmt_combo` combobox based on the current group name
        """
        if not self.no_fmtos_update:
            with self.no_fmtos_update:
                if current_text is None:
                    current_text = self.fmt_combo.currentText()
                self.fmt_combo.clear()
                self.fmt_combo.addItems(list(map(self.get_name,
                                                 self.fmtos[i])))
                ind = self.fmt_combo.findText(current_text)
                self.fmt_combo.setCurrentIndex(ind if ind >= 0 else 0)
                # update completer model
                self.setup_fmt_completion_model()
            idx = self.fmt_combo.currentIndex()
            self.show_fmt_info(idx)
            self.load_fmt_widget(idx)
            self.set_current_fmt_value(idx)

    def set_fmto(self, name):
        self.fmto = name

    def setup_fmt_completion_model(self):
        fmtos = list(
            unique_everseen(map(self.get_name,
                                chain.from_iterable(self.fmtos))))
        model = self.fmto_completer.model()
        model.setRowCount(len(fmtos))
        for i, name in enumerate(fmtos):
            model.setItem(i, QStandardItem(name))

    def load_fmt_widget(self, i):
        """Load the formatoption specific widget

        This method loads the formatoption specific widget from the
        :meth:`psyplot.plotter.Formatoption.get_fmt_widget` method and
        displays it above the :attr:`line_edit`

        Parameters
        ----------
        i: int
            The index of the current formatoption"""
        self.remove_fmt_widget()
        group_ind = self.group_combo.currentIndex()
        if not self.no_fmtos_update:
            from psyplot.project import gcp
            if self.groups[group_ind] == COORDSGROUP:
                dim = self.fmtos[group_ind][i]
                self.fmt_widget = self.dim_widget
                self.dim_widget.set_dim(dim)
                self.dim_widget.set_single_selection(dim not in gcp()[0].dims)
                self.dim_widget.setVisible(True)
            else:
                fmto = self.fmtos[group_ind][i]
                self.fmt_widget = fmto.get_fmt_widget(self, gcp())
                if self.fmt_widget is not None:
                    self.vbox.insertWidget(2, self.fmt_widget)

    def reset_fmt_widget(self):
        idx = self.fmt_combo.currentIndex()
        self.load_fmt_widget(idx)
        self.set_current_fmt_value(idx)

    def remove_fmt_widget(self):
        if self.fmt_widget is not None:
            self.fmt_widget.hide()
            if self.fmt_widget is self.dim_widget:
                self.fmt_widget.reset_combobox()
            else:
                self.vbox.removeWidget(self.fmt_widget)
                self.fmt_widget.close()
            del self.fmt_widget

    def set_current_fmt_value(self, i):
        """Add the value of the current formatoption to the line text"""
        group_ind = self.group_combo.currentIndex()
        if not self.no_fmtos_update:
            if self.groups[group_ind] == COORDSGROUP:
                from psyplot.project import gcp
                dim = self.fmtos[group_ind][i]
                self.set_obj(gcp().arrays[0].psy.idims[dim])
            else:
                fmto = self.fmtos[group_ind][i]
                self.set_obj(fmto.value)

    def show_fmt_info(self, i):
        """Show the documentation of the formatoption in the help explorer
        """
        group_ind = self.group_combo.currentIndex()
        if (not self.no_fmtos_update
                and self.groups[group_ind] != COORDSGROUP):
            fmto = self.fmtos[self.group_combo.currentIndex()][i]
            fmto.plotter.show_docs(
                fmto.key, include_links=self.include_links_cb.isChecked())

    def run_code(self):
        """Run the update of the project inside the :attr:`shell`"""
        if self.line_edit.isVisible():
            text = str(self.line_edit.text())
        else:
            text = str(self.text_edit.toPlainText())
        if not text or not self.fmtos:
            return
        group_ind = self.group_combo.currentIndex()
        if self.groups[group_ind] == COORDSGROUP:
            key = self.fmtos[group_ind][self.fmt_combo.currentIndex()]
            param = 'dims'
        else:
            key = self.fmtos[group_ind][self.fmt_combo.currentIndex()].key
            param = 'fmt'
        if self.yaml_cb.isChecked():
            import psyplot.project as psy
            psy.gcp().update(**{key: yaml.load(text, Loader=yaml.Loader)})
        else:
            code = "psy.gcp().update(%s={'%s': %s})" % (param, key, text)
            if ExecutionInfo is not None:
                info = ExecutionInfo(raw_cell=code,
                                     store_history=False,
                                     silent=True,
                                     shell_futures=False)
                e = ExecutionResult(info)
            else:
                e = ExecutionResult()
            self.console.run_command_in_shell(code, e)
            try:
                e.raise_error()
            except Exception:  # reset the console and clear the error message
                raise
            finally:
                self.console.reset()

    def get_text(self):
        """Get the current update text"""
        if self.line_edit.isVisible():
            return self.line_edit.text()
        else:
            return self.text_edit.toPlainText()

    def get_obj(self):
        """Get the current update text"""
        if self.line_edit.isVisible():
            txt = self.line_edit.text()
        else:
            txt = self.text_edit.toPlainText()
        try:
            obj = yaml.load(txt, Loader=yaml.Loader)
        except Exception:
            self.error_msg.showTraceback("Could not load %s" % txt)
        else:
            return obj

    def insert_obj(self, obj):
        """Add a string to the formatoption widget"""
        current = self.get_text()
        use_yaml = self.yaml_cb.isChecked()
        use_line_edit = self.line_edit.isVisible()
        # strings are treated separately such that we consider quotation marks
        # at the borders
        if isstring(obj) and current:
            if use_line_edit:
                pos = self.line_edit.cursorPosition()
            else:
                pos = self.text_edit.textCursor().position()
            if pos not in [0, len(current)]:
                s = obj
            else:
                if current[0] in ['"', "'"]:
                    current = current[1:-1]
                self.clear_text()
                if pos == 0:
                    s = '"' + obj + current + '"'
                else:
                    s = '"' + current + obj + '"'
                current = ''
        elif isstring(obj):  # add quotation marks
            s = '"' + obj + '"'
        elif not use_yaml:
            s = repr(obj)
        else:
            s = yaml.dump(obj, default_flow_style=True).strip()
            if s.endswith('\n...'):
                s = s[:-4]
        if use_line_edit:
            self.line_edit.insert(s)
        else:
            self.text_edit.insertPlainText(s)

    def clear_text(self):
        if self.line_edit.isVisible():
            self.line_edit.clear()
        else:
            self.text_edit.clear()

    def set_obj(self, obj):
        self.clear_text()
        self.insert_obj(obj)

    def show_all_fmt_info(self, what):
        """Show the keys, summaries or docs of the formatoptions

        Calling this function let's the help browser show the documentation
        etc. of all docs or only the selected group determined by the state of
        the :attr:`grouped_cb` and :attr:`all_groups_cb` checkboxes

        Parameters
        ----------
        what: {'keys', 'summaries', 'docs'}
            Determines what to show"""
        if not self.fmtos:
            return
        if (self.all_groups_cb.isChecked()
                or self.group_combo.currentIndex() < 2):
            fmtos = list(
                chain.from_iterable(
                    fmto_group for i, fmto_group in enumerate(self.fmtos)
                    if self.groups[i] not in [ALLGROUP, COORDSGROUP]))
        else:
            fmtos = self.fmtos[self.group_combo.currentIndex()]
        plotter = fmtos[0].plotter
        getattr(plotter, 'show_' +
                what)([fmto.key for fmto in fmtos],
                      grouped=self.grouped_cb.isChecked(),
                      include_links=self.include_links_cb.isChecked())