def build_nested_qmenu(widget, context_options, name=None): """ builds nested menu for context menus but can be used for other menu related things. References: http://pyqt.sourceforge.net/Docs/PyQt4/qkeysequence.html """ if name is None: menu = QtWidgets.QMenu(widget) else: menu = QtWidgets.QMenu(name, widget) action_list = [] for option_tup in context_options: if len(option_tup) == 2: opt, func = option_tup shortcut = QtGui.QKeySequence(0) elif len(option_tup) == 3: opt, shortcut_str, func = option_tup shortcut = QtGui.QKeySequence(shortcut_str) if func is None: action = menu.addAction(opt, lambda: None, shortcut) action_list.append(action) elif isinstance(func, list): # Recursive case sub_menu, sub_action_list = build_nested_qmenu(widget, func, opt) menu.addMenu(sub_menu) action_list.append((sub_menu, sub_action_list)) else: action = menu.addAction(opt, func, shortcut) action_list.append(action) return menu, action_list
def setupUi(self, editPrefSkel): editPrefSkel.setObjectName(_fromUtf8('editPrefSkel')) editPrefSkel.resize(668, 530) # Add Pane for TreeView self.verticalLayout = QtWidgets.QVBoxLayout(editPrefSkel) self.verticalLayout.setObjectName(_fromUtf8('verticalLayout')) # The TreeView for QtCore.QAbstractItemModel to attach to self.prefTreeView = QtWidgets.QTreeView(editPrefSkel) self.prefTreeView.setObjectName(_fromUtf8('prefTreeView')) self.verticalLayout.addWidget(self.prefTreeView) # Add Pane for buttons self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName(_fromUtf8('horizontalLayout')) # # self.redrawBUT = QtWidgets.QPushButton(editPrefSkel) # self.redrawBUT.setObjectName(_fromUtf8('redrawBUT')) # self.horizontalLayout.addWidget(self.redrawBUT) ## # self.unloadFeaturesAndModelsBUT = QtWidgets.QPushButton(editPrefSkel) # self.unloadFeaturesAndModelsBUT.setObjectName(_fromUtf8('unloadFeaturesAndModelsBUT')) # self.horizontalLayout.addWidget(self.unloadFeaturesAndModelsBUT) # self.defaultPrefsBUT = QtWidgets.QPushButton(editPrefSkel) self.defaultPrefsBUT.setObjectName(_fromUtf8('defaultPrefsBUT')) self.horizontalLayout.addWidget(self.defaultPrefsBUT) # Buttons are a child of the View self.verticalLayout.addLayout(self.horizontalLayout) self.retranslateUi(editPrefSkel) QtCore.QMetaObject.connectSlotsByName(editPrefSkel)
def paint(self, painter, option, index): version4 = GUITOOL_PYQT_VERSION == 4 # if GUITOOL_PYQT_VERSION: # return super(ConfigValueDelegate, self).paint(painter, option, index) # Get Item Data # value = index.data(QtCore.Qt.DisplayRole).toInt()[0] leafNode = index.internalPointer() # if (VERBOSE_CONFIG and False): # print('[DELEGATE] * painting editor for %s at %s' % (leafNode, qindexstr(index))) # leafNode.print_tree() # # print('[DELEGATE] * painting editor for %s at %s' % (leafNode, qindexstr(index))) if leafNode.is_combo: # print('[DELEGATE] * painting editor for %s at %s' % (leafNode, qindexstr(index))) # painter.save() curent_value = six.text_type(index.model().data(index)) style = QtWidgets.QApplication.style() opt = QtWidgets.QStyleOptionComboBox() opt.currentText = curent_value opt.rect = option.rect opt.editable = False opt.frame = True if leafNode.qt_is_editable(): opt.state |= style.State_On opt.state |= style.State_Enabled opt.state = style.State_Enabled | style.State_Active element = QtWidgets.QStyle.CE_ComboBoxLabel control = QtWidgets.QStyle.CC_ComboBox style.drawComplexControl(control, opt, painter) style.drawControl(element, opt, painter) elif (leafNode is not None and leafNode.is_spin) and version4: # fill style options with item data style = QtWidgets.QApplication.style() opt = QtWidgets.QStyleOptionSpinBox() # opt.currentText doesn't exist for SpinBox # opt.currentText = curent_value # opt.rect = option.rect # opt.editable = False if leafNode.qt_is_editable(): opt.state |= style.State_Enabled element = QtWidgets.QStyle.CE_ItemViewItem control = QtWidgets.QStyle.CC_SpinBox painter.save() style.drawComplexControl(control, opt, painter) style.drawControl(element, opt, painter) # self.drawDisplay(painter, opt, opt.rect, str(curent_value)) option.rect.setLeft(option.rect.left() + 3) curent_value = six.text_type(index.model().data(index)) painter.drawText(option.rect, Qt.AlignLeft, str(curent_value)) painter.restore() else: return super(ConfigValueDelegate, self).paint(painter, option, index)
def __init__( cltw, col_data_list=None, col_name_list=None, niceheader_list=None, col_type_list=None, col_edit_list=None, display_indices=False, col_sort_index=None, parent=None, ): super(ColumnListTableWidget, cltw).__init__(parent) # QtWidgets.QWidget.__init__(cltw, parent) # Create vertical layout for the table to go into cltw.vert_layout = QtWidgets.QVBoxLayout(cltw) # Instansiate the AbstractItemModel cltw.model = ColumnListItemModel(parent=cltw) # Create a ColumnListTableView for the AbstractItemModel cltw.view = ColumnListTableView(cltw) cltw.view.setModel(cltw.model) cltw.vert_layout.addWidget(cltw.view) # Make sure we don't call a childs method ColumnListTableWidget.change_data( cltw, col_data_list, col_name_list, niceheader_list, col_type_list, col_edit_list, display_indices, col_sort_index, )
def infer_delegates(view, **headers): """ Infers which columns should be given item delegates """ get_thumb_size = headers.get('get_thumb_size', None) col_type_list = headers.get('col_type_list', []) num_cols = view.model().columnCount() num_duplicates = int(num_cols / len(col_type_list)) col_type_list = col_type_list * num_duplicates view.has_thumbs = False for colx, coltype in enumerate(col_type_list): if coltype in qtype.QT_PIXMAP_TYPES: if VERBOSE: print('[view] colx=%r is a PIXMAP' % colx) thumb_delegate = api_thumb_delegate.APIThumbDelegate( view, get_thumb_size) view.setItemDelegateForColumn(colx, thumb_delegate) view.has_thumbs = True # HACK # verticalHeader = view.verticalHeader() # verticalHeader.setDefaultSectionSize(256) elif coltype in qtype.QT_BUTTON_TYPES: if VERBOSE: print('[view] colx=%r is a BUTTON' % colx) button_delegate = api_button_delegate.APIButtonDelegate(view) view.setItemDelegateForColumn(colx, button_delegate) elif isinstance(coltype, QtWidgets.QAbstractItemDelegate): if VERBOSE: print('[view] colx=%r is a CUSTOM DELEGATE' % colx) view.setItemDelegateForColumn(colx, coltype) else: if VERBOSE: print('[view] colx=%r does not have a delgate' % colx) # Effectively unsets any existing delegates default_delegate = QtWidgets.QStyledItemDelegate(view) view.setItemDelegateForColumn(colx, default_delegate)
def get_number_of_monitors(): gt.ensure_qtapp() desktop = QtWidgets.QDesktopWidget() if hasattr(desktop, 'numScreens'): n = desktop.numScreens() else: n = desktop.screenCount() return n
def get_monitor_geometries(): gt.ensure_qtapp() monitor_geometries = {} desktop = QtWidgets.QDesktopWidget() if hasattr(desktop, 'numScreens'): n = desktop.numScreens() else: n = desktop.screenCount() for screenx in range(n): rect = desktop.availableGeometry(screen=screenx) geom = (rect.x(), rect.y(), rect.width(), rect.height()) monitor_geometries[screenx] = geom return monitor_geometries
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 createEditor(self, parent, option, index): combo = QtWidgets.QComboBox(parent) combo.addItems(['option1', 'option2', 'option3']) # FIXME: Change to newstyle signal slot if GUITOOL_PYQT_VERSION == 5: self.connect(combo.currentIndexChanged, self.currentIndexChanged) else: # I believe this particular option is broken in pyqt4 self.connect( combo, QtCore.SIGNAL('currentIndexChanged(int)'), self, QtCore.SLOT('currentIndexChanged()'), ) return combo
def newFileDialog(directory_, other_sidebar_dpaths=[], use_sidebar_cwd=True, mode='open', exec_=False): r""" Args: directory_ (?): other_sidebar_dpaths (list): (default = []) use_sidebar_cwd (bool): (default = True) _dialog_class_ (wrappertype): (default = <class 'PyQt5.QtWidgets.QFileDialog'>) Returns: ?: qdlg CommandLine: python -m wbia.guitool.guitool_dialogs newFileDialog --show Example: >>> # DISABLE_DOCTEST >>> # GUI_DOCTEST >>> # xdoctest: +REQUIRES(--gui) >>> from wbia.guitool.guitool_dialogs import * # NOQA >>> import wbia.guitool >>> guitool.ensure_qtapp() >>> directory_ = '.' >>> _dialog_class_ = QtWidgets.QFileDialog >>> if ut.show_was_requested(): >>> files = newFileDialog(directory_, mode='save', exec_=True) >>> print('files = %r' % (files,)) >>> else: >>> dlg = newFileDialog(directory_, mode='save', exec_=True) """ qdlg = QtWidgets.QFileDialog() if mode == 'open': qdlg.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) elif mode == 'save': qdlg.setAcceptMode(QtWidgets.QFileDialog.AcceptSave) else: raise KeyError(mode) _set_dialog_sidebar(qdlg, directory_, use_sidebar_cwd, other_sidebar_dpaths) if exec_: qdlg.exec_() return qdlg.selectedFiles() else: return qdlg
def _newMsgBox( msg='', title='', parent=None, options=None, cache_reply=False, resizable=False ): if resizable: msgbox = ResizableMessageBox(parent) else: msgbox = QtWidgets.QMessageBox(parent) # msgbox.setAttribute(QtCore.Qt.WA_DeleteOnClose) # std_buts = QtWidgets.QMessageBox.Close # std_buts = QtWidgets.QMessageBox.NoButton std_buts = QtWidgets.QMessageBox.Cancel msgbox.setStandardButtons(std_buts) msgbox.setWindowTitle(title) msgbox.setText(msg) msgbox.setModal(parent is not None) return msgbox
def paint(self, painter, option, index): # This method will be called every time a particular cell is # in view and that view is changed in some way. We ask the # delegates parent (in this case a table view) if the index # in question (the table cell) already has a widget associated # with it. If not, create one with the text for this index and # connect its clicked signal to a slot in the parent view so # we are notified when its used and can do something. if not self.parent().indexWidget(index): self.parent().setIndexWidget( index, QtWidgets.QPushButton( index.data().toString(), self.parent(), clicked=self.parent().cellButtonClicked, ), )
def __init__(co_wgt, ibs, gid_list, parent=None, hack=False): logger.info('[co_gui] Initializing') logger.info('[co_gui] gid_list = %r' % (gid_list, )) QtWidgets.QWidget.__init__(co_wgt, parent=parent) co_wgt.fnum = pt.next_fnum() co_wgt.main_layout = QtWidgets.QVBoxLayout(co_wgt) co_wgt.text_layout = gt.newWidget(co_wgt, orientation=Qt.Vertical, verticalStretch=10) co_wgt.main_layout.addWidget(co_wgt.text_layout) co_wgt.control_layout = gt.newWidget( co_wgt, orientation=Qt.Vertical, verticalSizePolicy=QtWidgets.QSizePolicy.MinimumExpanding, ) co_wgt.main_layout.addWidget(co_wgt.control_layout) co_wgt.button_layout = gt.newWidget(co_wgt, orientation=Qt.Horizontal) co_wgt.control_layout.addWidget(co_wgt.button_layout) co_wgt.combo_layout = gt.newWidget(co_wgt, orientation=Qt.Horizontal) co_wgt.control_layout.addWidget(co_wgt.combo_layout) co_wgt.hack = hack # hack for edting the time of a single image co_wgt.imfig = None co_wgt.imax = None co_wgt.dtime = None co_wgt.ibs = ibs co_wgt.gid_list = gid_list co_wgt.current_gindex = 0 co_wgt.offset = 0 # Set image datetime with first image co_wgt.get_image_datetime_() co_wgt.add_label() co_wgt.add_combo_boxes() co_wgt.add_buttons() co_wgt.update_ui()
def __init__( widget, headers=None, parent=None, model_class=APIItemModel, view_class=APITableView, tblnice='APIItemWidget', doubleClicked=None, ): WIDGET_BASE.__init__(widget, parent) if isinstance(view_class, six.string_types): if view_class == 'tree': view_class = APITreeView elif view_class == 'table': view_class = APITableView else: raise ValueError('Unknown view_class=%r' % (view_class,)) # Create vertical layout for the table to go into widget.vert_layout = QtWidgets.QVBoxLayout(widget) # Create a ColumnListTableView for the AbstractItemModel widget.view = view_class(parent=widget) # Instantiate the AbstractItemModel # FIXME: It is very bad to give the model a view. # Only the view should have a model # Also the model should not be an attribute because # it might live in another thread widget._model = model_class(parent=widget.view) widget.view.setModel(widget._model) widget.vert_layout.addWidget(widget.view) widget.tblnice = tblnice if headers is not None: # Make sure we don't call a subclass method APIItemWidget.change_headers(widget, headers) widget.connect_signals() widget.api = None if doubleClicked: widget.view.doubleClicked.connect(doubleClicked)
def paint_button( painter, option, text='button', pressed=True, bgcolor=None, fgcolor=None, clicked=None, button=None, view=None, ): # http://www.qtcentre.org/archive/index.php/t-31029.html opt = QtWidgets.QStyleOptionButton() opt.text = text opt.rect = option.rect opt.palette = option.palette if pressed: opt.state = QtWidgets.QStyle.State_Enabled | QtWidgets.QStyle.State_Sunken else: opt.state = QtWidgets.QStyle.State_Enabled | QtWidgets.QStyle.State_Raised # style = QtGui.Q Application.style() style = button.style() style.drawControl(QtWidgets.QStyle.CE_PushButton, opt, painter, button)
def get_monitor_geom(monitor_num=0): r""" Args: monitor_num (int): (default = 0) Returns: tuple: geom CommandLine: python -m wbia.plottool.screeninfo get_monitor_geom --show Example: >>> # DISABLE_DOCTEST >>> from wbia.plottool.screeninfo import * # NOQA >>> monitor_num = 0 >>> geom = get_monitor_geom(monitor_num) >>> result = ('geom = %s' % (ut.repr2(geom),)) >>> print(result) """ gt.ensure_qtapp() desktop = QtWidgets.QDesktopWidget() rect = desktop.availableGeometry(screen=monitor_num) geom = (rect.x(), rect.y(), rect.width(), rect.height()) return geom
def user_option( parent=None, msg='msg', title='user_option', options=['Yes', 'No'], use_cache=False, default=None, detailed_msg=None, ): """ Prompts user with several options with ability to save decision Args: parent (None): msg (str): title (str): options (list): use_cache (bool): default (str): default option Returns: str: reply CommandLine: python -m wbia.guitool.guitool_dialogs --test-user_option Example: >>> # GUI_DOCTEST >>> # xdoctest: +REQUIRES(--gui) >>> from wbia.guitool.guitool_dialogs import * # NOQA >>> import wbia.guitool as gt >>> gt.ensure_qtapp() >>> parent = None >>> msg = 'msg' >>> title = 'user_option' >>> options = ['Yes', 'No'] >>> use_cache = False >>> default = 'Yes' >>> # execute function >>> detailed_msg = 'hi' >>> reply = user_option(parent, msg, title, options, use_cache, default, detailed_msg) >>> result = str(reply) >>> print(result) >>> # xdoctest: +REQUIRES(--show) >>> ut.quit_if_noshow() >>> #gt.guitool_main.qtapp_loop() """ if ut.VERBOSE: print('[gt] user_option:\n %r: %s' % (title, msg)) # Recall decision cache_id = title + msg if use_cache: reply = _guitool_cache_read(cache_id, default=None) if reply is not None: return reply # Create message box msgbox = _newMsgBox(msg, title, parent, resizable=detailed_msg is not None) # _addOptions(msgbox, options) # def _addOptions(msgbox, options): # msgbox.addButton(QtWidgets.QMessageBox.Close) options = list(options)[::-1] for opt in options: role = QtWidgets.QMessageBox.ApplyRole msgbox.addButton(QtWidgets.QPushButton(opt), role) # Set default button if default is not None: assert default in options, 'default=%r is not in options=%r' % (default, options,) for qbutton in msgbox.buttons(): if default == qbutton.text(): msgbox.setDefaultButton(qbutton) if use_cache: # Add a remember me option if caching is on dontPrompt = _cacheReply(msgbox) if detailed_msg is not None: msgbox.setDetailedText(detailed_msg) # Wait for output optx = msgbox.exec_() if optx == QtWidgets.QMessageBox.Cancel: # User Canceled return None try: # User Selected an option reply = options[optx] except KeyError as ex: # This should be unreachable code. print('[gt] USER OPTION EXCEPTION !') print('[gt] optx = %r' % optx) print('[gt] options = %r' % options) print('[gt] ex = %r' % ex) raise # Remember decision if caching is on if use_cache and dontPrompt.isChecked(): _guitool_cache_write(cache_id, reply) # Close the message box del msgbox return reply
def test_show_qimg(qimg): qpixmap = QtGui.QPixmap(qimg) lbl = QtWidgets.QLabel() lbl.setPixmap(qpixmap) lbl.show() # show label with qim image return lbl
def _cacheReply(msgbox): dontPrompt = QtWidgets.QCheckBox('dont ask me again', parent=msgbox) dontPrompt.blockSignals(True) msgbox.addButton(dontPrompt, QtWidgets.QMessageBox.ActionRole) return dontPrompt
def get_resolution_info(monitor_num=0): r""" Args: monitor_num (int): (default = 0) Returns: dict: info CommandLine: python -m wbia.plottool.screeninfo get_resolution_info --show xrandr | grep ' connected' grep "NVIDIA" /var/log/Xorg.0.log Example: >>> # DISABLE_DOCTEST >>> from wbia.plottool.screeninfo import * # NOQA >>> monitor_num = 1 >>> for monitor_num in range(get_number_of_monitors()): >>> info = get_resolution_info(monitor_num) >>> print('monitor(%d).info = %s' % (monitor_num, ut.repr3(info, precision=3))) """ import wbia.guitool as gt app = gt.ensure_qtapp()[0] # NOQA # screen_resolution = app.desktop().screenGeometry() # width, height = screen_resolution.width(), screen_resolution.height() # print('height = %r' % (height,)) # print('width = %r' % (width,)) desktop = QtWidgets.QDesktopWidget() screen = desktop.screen(monitor_num) ppi_x = screen.logicalDpiX() ppi_y = screen.logicalDpiY() dpi_x = screen.physicalDpiX() dpi_y = screen.physicalDpiY() # This call is not rotated correctly # rect = screen.screenGeometry() # This call has bad offsets rect = desktop.screenGeometry(screen=monitor_num) # This call subtracts offsets weirdly # desktop.availableGeometry(screen=monitor_num) pixels_w = rect.width() # for num in range(desktop.screenCount()): # pass pixels_h = rect.height() # + rect.y() """ I have two monitors (screens), after rotation effects they have the geometry: (for example) S1 = {x: 0, y=300, w: 1920, h:1080} S2 = {x=1920, y=0, w: 1080, h:1920} Here is a pictoral example G--------------------------------------C------------------- | | | A--------------------------------------| | | | | | | | | | | | S1 | | | | S2 | | | | | | | | | | |--------------------------------------B | | | | | | | ----------------------------------------------------------D Desired Info G = (0, 0) A = (S1.x, S1.y) B = (S1.x + S1.w, S1.y + S1.h) C = (S2.x, S2.y) D = (S2.x + S1.w, S2.y + S2.h) from PyQt4 import QtGui, QtCore app = QtCore.QCoreApplication.instance() if app is None: import sys app = QtGui.QApplication(sys.argv) desktop = QtGui.QDesktopWidget() rect1 = desktop.screenGeometry(screen=0) rect2 = desktop.screenGeometry(screen=1) """ # I want to get the relative positions of my monitors # pt = screen.pos() # pt = screen.mapToGlobal(pt) # pt = screen.mapToGlobal(screen.pos()) # Screen offsets seem bugged # off_x = pt.x() # off_y = pt.y() # print(pt.x()) # print(pt.y()) # pt = screen.mapToGlobal(QtCore.QPoint(0, 0)) # print(pt.x()) # print(pt.y()) off_x = rect.x() off_y = rect.y() # pt.x(), pt.y() inches_w = pixels_w / dpi_x inches_h = pixels_h / dpi_y inches_diag = (inches_w ** 2 + inches_h ** 2) ** 0.5 mm_w = inches_w * ut.MM_PER_INCH mm_h = inches_h * ut.MM_PER_INCH mm_diag = inches_diag * ut.MM_PER_INCH ratio = min(mm_w, mm_h) / max(mm_w, mm_h) # pixel_density = dpi_x / ppi_x info = ut.odict( [ ('monitor_num', monitor_num), ('off_x', off_x), ('off_y', off_y), ('ratio', ratio), ('ppi_x', ppi_x), ('ppi_y', ppi_y), ('dpi_x', dpi_x), ('dpi_y', dpi_y), # 'pixel_density', pixel_density), ('inches_w', inches_w), ('inches_h', inches_h), ('inches_diag', inches_diag), ('mm_w', mm_w), ('mm_h', mm_h), ('mm_diag', mm_diag), ('pixels_w', pixels_w), ('pixels_h', pixels_h), ] ) return info