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)
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()
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)