Пример #1
0
class GUILoggingSender(QtCore.QObject):
    write_ = QtCore.pyqtSignal(str)

    def __init__(self, write_slot):
        QtCore.QObject.__init__(self)
        self.write_.connect(write_slot)

    def write_gui(self, msg):
        self.write_.emit(str(msg))
class APIButtonDelegate(DELEGATE_BASE):
    button_clicked = QtCore.pyqtSignal(QtCore.QModelIndex)

    def __init__(dgt, parent=None):
        assert parent is not None, 'parent must be a view'
        DELEGATE_BASE.__init__(dgt, parent)
        # FIXME: I don't like this state in the delegate, as it renders all
        # buttons
        dgt._pressed = None
        dgt.button_clicked.connect(dgt.on_button_click)

    def get_index_butkw(dgt, qtindex):
        """
        The model data for a button should be a (text, callback) tuple.  OR
        it could be a function which accepts an qtindex and returns a button
        """
        data = qtindex.model().data(qtindex, QtCore.Qt.DisplayRole)
        # Get info
        if isinstance(data, tuple):
            buttontup = data
        elif utool.is_funclike(data):
            func = data
            buttontup = func(qtindex)
        else:
            raise AssertionError('bad type')
        text, callback = buttontup[0:2]
        butkw = {
            # 'parent': dgt.parent(),
            'text': text,
            'clicked': callback,
        }
        if len(buttontup) > 2:
            butkw['bgcolor'] = buttontup[2]
            butkw['fgcolor'] = (0, 0, 0)
        return butkw

    def paint(dgt, painter, option, qtindex):
        painter.save()
        butkw = dgt.get_index_butkw(qtindex)
        # FIXME: I don't want to create a widget each time just
        # so I can access the button's style
        button = guitool_components.newButton(**butkw)
        pressed = dgt.is_qtindex_pressed(qtindex)
        view = dgt.parent()
        paint_button(painter,
                     option,
                     button=button,
                     pressed=pressed,
                     view=view,
                     **butkw)
        painter.restore()

    def is_qtindex_pressed(dgt, qtindex):
        return dgt._pressed is not None and dgt._pressed == (
            qtindex.row(),
            qtindex.column(),
        )

    @QtCore.pyqtSlot(QtCore.QModelIndex)
    def on_button_click(dgt, qtindex):
        if utool.VERBOSE:
            print('pressed button')
        butkw = dgt.get_index_butkw(qtindex)
        callback = butkw['clicked']
        callback()

    def editorEvent(dgt, event, model, option, qtindex):
        # http://stackoverflow.com/questions/14585575/button-delegate-issue
        # print('editor event')
        event_type = event.type()
        if event_type == QtCore.QEvent.MouseButtonPress:
            # store the position that is clicked
            dgt._pressed = (qtindex.row(), qtindex.column())
            if utool.VERBOSE:
                print('store')
            return True
        elif event_type == QtCore.QEvent.MouseButtonRelease:
            if dgt.is_qtindex_pressed(qtindex):
                print('emit')
                dgt.button_clicked.emit(qtindex)
                pass
            elif dgt._pressed is not None:
                # different place.
                # force a repaint on the pressed cell by emitting a dataChanged
                # Note: This is probably not the best idea
                # but I've yet to find a better solution.
                print('repaint')
                oldIndex = qtindex.model().index(*dgt._pressed)
                dgt._pressed = None
                qtindex.model().dataChanged.emit(oldIndex, oldIndex)
                pass
            dgt._pressed = None
            # print('mouse release')
            return True
        elif event_type == QtCore.QEvent.Leave:
            print('leave')
        elif event_type == QtCore.QEvent.MouseButtonDblClick:
            print('doubleclick')
        else:
            print('event_type = %r' % event_type)
            return DELEGATE_BASE.editorEvent(dgt, event, model, option,
                                             qtindex)
Пример #3
0
class EditConfigWidget(QtWidgets.QWidget):
    """
    Widget to edit a dtool.Config object

    Args:
        config (dtool.Config):

    CommandLine:
        python -m wbia.guitool.PrefWidget2 EditConfigWidget --show
        python -m wbia.guitool.PrefWidget2 EditConfigWidget --show --verbconf

    Example:
        >>> # DISABLE_DOCTEST
        >>> from wbia.guitool.PrefWidget2 import *  # NOQA
        >>> import wbia.guitool
        >>> guitool.ensure_qtapp()
        >>> import dtool
        >>> def changed(key=None):
        >>>     print('config[key] = %r has changed' % (key,))
        >>> class ExampleConfig(dtool.Config):
        >>>     _param_info_list = [
        >>>         ut.ParamInfo('str_option', 'hello'),
        >>>         ut.ParamInfo('int_option', 42, none_ok=False),
        >>>         ut.ParamInfo('int_option2', None, type_=int, min_=-2),
        >>>         ut.ParamInfo('float_option', .42, max_=1.0, min_=0),
        >>>         ut.ParamInfo('none_option', None),
        >>>         ut.ParamInfo('none_combo_option', None, valid_values=[None, True, False]),
        >>>         ut.ParamInfo('combo_option', 'up', valid_values=['up', 'down', 'strange', 'charm', 'top', 'bottom']),
        >>>         ut.ParamInfo('bool_option', False),
        >>>         ut.ParamInfo('bool_option2', None, type_=bool),
        >>>         ut.ParamInfo('hidden_str', 'foobar', hideif=lambda cfg: not cfg['bool_option']),
        >>>         ut.ParamInfo('hidden_combo', 'one', valid_values=['oneA', 'twoB', 'threeC'], hideif=lambda cfg: not cfg['bool_option']),
        >>>     ]
        >>> config = ExampleConfig()
        >>> widget = EditConfigWidget(config=config, changed=changed)
        >>> widget.rootNode.print_tree()
        >>> from wbia.plottool import fig_presenter
        >>> fig_presenter.register_qt4_win(widget)
        >>> widget.show()
        >>> ut.quit_if_noshow()
        >>> widget.resize(400, 500)
        >>> guitool.qtapp_loop(qwin=widget, freq=10)
    """

    # data_changed(key)
    data_changed = QtCore.pyqtSignal(str)

    def __init__(
        self, parent=None, config=None, user_mode=False, with_buttons=True, changed=None
    ):
        super(EditConfigWidget, self).__init__(parent)
        rootNode = ConfigNodeWrapper('root', config)
        self.user_mode = user_mode
        self._with_buttons = with_buttons
        self.init_layout()
        self.rootNode = rootNode
        self.config_model = QConfigModel(self, rootNode=rootNode)
        self.init_mvc()
        if changed is not None:
            self.data_changed.connect(changed)

    def init_layout(self):
        import wbia.guitool as gt

        # Create the tree view and buttons
        self.tree_view = QtWidgets.QTreeView(self)
        self.delegate = ConfigValueDelegate(self.tree_view)
        self.tree_view.setItemDelegateForColumn(1, self.delegate)

        if self._with_buttons:
            buttons = []
            self.default_but = gt.newButton(
                self, 'Defaults', pressed=self.reset_to_default
            )
            buttons.append(self.default_but)

            self.orig_but = gt.newButton(self, 'Original', pressed=self.reset_to_original)
            buttons.append(self.orig_but)

            if not self.user_mode:
                self.print_internals = gt.newButton(
                    self, 'Print Internals', pressed=self.print_internals
                )
                buttons.append(self.print_internals)

            # Add compoments to the layout
            self.hbox = QtWidgets.QHBoxLayout()
            for button in buttons:
                self.hbox.addWidget(button)
                button.setSizePolicy(
                    QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum
                )

        self.vbox = QtWidgets.QVBoxLayout(self)
        self.vbox.addWidget(self.tree_view)
        if self._with_buttons:
            self.vbox.addLayout(self.hbox)
        self.setWindowTitle(_translate('self', 'Edit Config Widget', None))

    def init_mvc(self):
        import operator
        from six.moves import reduce

        edit_triggers = reduce(
            operator.__or__,
            [
                QtWidgets.QAbstractItemView.CurrentChanged,
                QtWidgets.QAbstractItemView.DoubleClicked,
                QtWidgets.QAbstractItemView.SelectedClicked,
                # QtWidgets.QAbstractItemView.EditKeyPressed,
                # QtWidgets.QAbstractItemView.AnyKeyPressed,
            ],
        )
        self.tree_view.setEditTriggers(edit_triggers)
        self.tree_view.setModel(self.config_model)
        view_header = self.tree_view.header()
        # import utool
        # utool.embed()
        # view_header.setDefaultSectionSize(250)
        # self.tree_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.tree_view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        # view_header.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
        #                          QtWidgets.QSizePolicy.MinimumExpanding)
        # view_header.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
        #                          QtWidgets.QSizePolicy.Preferred)
        self.tree_view.resizeColumnToContents(0)
        self.tree_view.resizeColumnToContents(1)
        # self.tree_view.setAnimated(True)
        # view_header.setStretchLastSection(True)
        try:
            view_header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
        except AttributeError:
            view_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)

        # view_header.setResizeMode(QtWidgets.QHeaderView.Interactive)
        # import utool
        # utool.embed()
        # self.tree_view.header().resizeSection(0, 250)
        # setDefaultSectionSize(

        # from wbia.guitool import api_item_view
        # api_item_view.set_column_persistant_editor(self.tree_view, 1)
        # # Persistant editors
        # num_rows = 4  # self.tree_view.model.rowCount()
        # print('view.set_persistant: %r rows' % num_rows)
        if False:
            view = self.tree_view
            model = self.config_model
            column = 1
            for row in range(model.rowCount()):
                index = model.index(row, column)
                view.openPersistentEditor(index)

        self.config_model.dataChanged.connect(self._on_change)

    def _on_change(self, top_left, bottom_right):
        if top_left is bottom_right:
            # we know what index changed
            qtindex = top_left
            model = qtindex.model()
            # Find index with config key
            key_index = model.index(qtindex.row(), 0, qtindex.parent())
            key = key_index.data()
        else:
            key = None
        self.data_changed.emit(key)

    def reset_to_default(self):
        print('Defaulting')
        self.rootNode._reset_to_default()
        self.refresh_layout()
        self.data_changed.emit('')

    def reset_to_original(self):
        print('Defaulting')
        self.rootNode._reset_to_original()
        self.refresh_layout()
        self.data_changed.emit('')

    def set_to_external(self, cfg):
        print('Setting to external')
        self.rootNode._set_to_external(cfg)
        self.refresh_layout()
        self.data_changed.emit('')

    def print_internals(self):
        print('Print Internals')
        self.rootNode.print_tree()

    def refresh_layout(self):
        self.config_model.layoutAboutToBeChanged.emit()
        self.config_model.layoutChanged.emit()
Пример #4
0
class MatplotlibWidget(gt.GuitoolWidget):
    """
    A qt widget that contains a matplotlib figure

    References:
        http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html
    """

    click_inside_signal = QtCore.pyqtSignal(MouseEvent, object)
    key_press_signal = QtCore.pyqtSignal(KeyEvent)
    pick_event_signal = QtCore.pyqtSignal(PickEvent)

    def initialize(self, pan_and_zoom=False):
        from wbia.plottool.interactions import zoom_factory, pan_factory
        from wbia.plottool import abstract_interaction

        # Create unmanaged figure and a canvas
        self.fig = mpl.figure.Figure()
        self.fig._no_raise_plottool = True
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self)
        self.addWidget(self.canvas)

        # Workaround key_press bug
        # References: https://github.com/matplotlib/matplotlib/issues/707
        self.canvas.setFocusPolicy(Qt.ClickFocus)

        self.reset_ax()

        # self.ax = self.fig.add_subplot(1, 1, 1)
        # pt.adjust_subplots(left=0, right=1, top=1, bottom=0, fig=self.fig)

        if pan_and_zoom or True:
            self.pan_events = pan_factory(self.ax)
            self.zoon_events = zoom_factory(self.ax)

        self.fig.canvas.mpl_connect('button_press_event', self._emit_button_press)
        self.fig.canvas.mpl_connect('key_press_event', self.key_press_signal.emit)
        self.fig.canvas.mpl_connect('pick_event', self.pick_event_signal.emit)

        self.MOUSE_BUTTONS = abstract_interaction.AbstractInteraction.MOUSE_BUTTONS
        self.setMinimumHeight(20)
        self.setMinimumWidth(20)

        self.installEventFilter(self.parent())

    def clf(self):
        self.fig.clf()
        self.reset_ax()

    def reset_ax(self):
        # from wbia.plottool.interactions import zoom_factory, pan_factory
        import wbia.plottool as pt

        self.ax = self.fig.add_subplot(1, 1, 1)
        pt.adjust_subplots(left=0, right=1, top=1, bottom=0, fig=self.fig)
        # self.pan_events = pan_factory(self.ax)
        # self.zoon_events = zoom_factory(self.ax)
        return self.ax

    def _emit_button_press(self, event):
        from wbia.plottool import interact_helpers as ih

        if ih.clicked_inside_axis(event):
            self.click_inside_signal.emit(event, event.inaxes)