def __init__(self, parent=None): super(XZoomSlider, self).__init__(parent) # define the interface in_icon = projexui.resources.find('img/zoom_in.png') out_icon = projexui.resources.find('img/zoom_out.png') self._zoomInButton = QToolButton(self) self._zoomInButton.setAutoRaise(True) self._zoomInButton.setToolTip('Zoom In') self._zoomInButton.setIcon(QIcon(in_icon)) self._zoomOutButton = QToolButton(self) self._zoomOutButton.setAutoRaise(True) self._zoomOutButton.setToolTip('Zoom Out') self._zoomOutButton.setIcon(QIcon(out_icon)) self._zoomSlider = QSlider(Qt.Horizontal, self) self._zoomSlider.setRange(10, 100) self._zoomSlider.setValue(100) # define the layout layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._zoomOutButton) layout.addWidget(self._zoomSlider) layout.addWidget(self._zoomInButton) self.setLayout(layout) # create connections self._zoomSlider.valueChanged.connect(self.emitZoomAmountChanged) self._zoomInButton.clicked.connect(self.zoomIn) self._zoomOutButton.clicked.connect(self.zoomOut)
def __init__(self, parent): super(XUrlWidget, self).__init__(parent) # define the interface self._urlEdit = XLineEdit(self) self._urlButton = QToolButton(self) self._urlButton.setAutoRaise(True) self._urlButton.setIcon(QIcon(resources.find('img/web.png'))) self._urlButton.setToolTip('Browse Link') self._urlButton.setFocusPolicy(Qt.NoFocus) self._urlEdit.setHint('http://') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._urlEdit) layout.addWidget(self._urlButton) self.setLayout(layout) self.setFocusPolicy(Qt.StrongFocus) # create connections self._urlEdit.textChanged.connect(self.urlChanged) self._urlEdit.textEdited.connect(self.urlEdited) self._urlButton.clicked.connect(self.browse)
def __init__( self, orientation, parent ): super(XSplitterHandle, self).__init__( orientation, parent ) # create a layout for the different buttons self._collapsed = False self._storedSizes = None self._collapseBefore = QToolButton(self) self._resizeGrip = QLabel(self) self._collapseAfter = QToolButton(self) self._collapseBefore.setAutoRaise(True) self._collapseAfter.setAutoRaise(True) self._collapseBefore.setCursor(Qt.ArrowCursor) self._collapseAfter.setCursor(Qt.ArrowCursor) # define the layout layout = QBoxLayout(QBoxLayout.LeftToRight, self) layout.setContentsMargins(0, 0, 0, 0) layout.addStretch(1) layout.addWidget(self._collapseBefore) layout.addWidget(self._resizeGrip) layout.addWidget(self._collapseAfter) layout.addStretch(1) self.setLayout(layout) # set the orientation to start with self.setOrientation(orientation) # create connections self._collapseAfter.clicked.connect( self.toggleCollapseAfter ) self._collapseBefore.clicked.connect( self.toggleCollapseBefore )
def __init__(self, parent=None): super(XViewProfileManager, self).__init__(parent) # define custom properties self._profiles = [] self._optionsMenuPolicy = Qt.DefaultContextMenu self._viewWidget = None # define the interface self._profileCombo = QComboBox(self) self._optionsButton = QToolButton(self) self._optionsButton.setAutoRaise(True) self._optionsButton.setToolTip('Advanced Options') self._optionsButton.setIcon(QIcon(resources.find('img/advanced.png'))) layout = QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._profileCombo) layout.addWidget(self._optionsButton) self.setLayout(layout) # create connections self._profileCombo.currentIndexChanged.connect( self.handleProfileChange) self._optionsButton.clicked.connect(self.showOptionsMenu)
def __init__(self, orientation, parent): super(XSplitterHandle, self).__init__(orientation, parent) # create a layout for the different buttons self._collapsed = False self._storedSizes = None self._collapseBefore = QToolButton(self) self._resizeGrip = QLabel(self) self._collapseAfter = QToolButton(self) self._collapseBefore.setAutoRaise(True) self._collapseAfter.setAutoRaise(True) self._collapseBefore.setCursor(Qt.ArrowCursor) self._collapseAfter.setCursor(Qt.ArrowCursor) # define the layout layout = QBoxLayout(QBoxLayout.LeftToRight, self) layout.setContentsMargins(0, 0, 0, 0) layout.addStretch(1) layout.addWidget(self._collapseBefore) layout.addWidget(self._resizeGrip) layout.addWidget(self._collapseAfter) layout.addStretch(1) self.setLayout(layout) # set the orientation to start with self.setOrientation(orientation) # create connections self._collapseAfter.clicked.connect(self.toggleCollapseAfter) self._collapseBefore.clicked.connect(self.toggleCollapseBefore)
def __init__(self, parent): super(XLocationWidget, self).__init__(parent) # define the interface self._locationEdit = XLineEdit(self) self._locationButton = QToolButton(self) self._urlTemplate = 'http://maps.google.com/maps?%(params)s' self._urlQueryKey = 'q' self._locationButton.setAutoRaise(True) self._locationButton.setIcon(QIcon(resources.find('img/map.png'))) self._locationEdit.setHint('no location set') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._locationEdit) layout.addWidget(self._locationButton) self.setLayout(layout) # create connections self._locationEdit.textChanged.connect(self.locationChanged) self._locationEdit.textEdited.connect(self.locationEdited) self._locationButton.clicked.connect(self.browseMaps)
def __init__(self, parent=None): super(XCommentEdit, self).__init__(parent) # define custom properties self._attachments = {} self._showAttachments = True # create toolbar self._toolbar = QToolBar(self) self._toolbar.setMovable(False) self._toolbar.setFixedHeight(30) self._toolbar.setAutoFillBackground(True) self._toolbar.setFocusProxy(self) self._toolbar.hide() # create toolbar buttons self._attachButton = QToolButton(self) self._attachButton.setIcon(QIcon(resources.find('img/attach.png'))) self._attachButton.setToolTip('Add Attachment') self._attachButton.setAutoRaise(True) self._attachButton.setIconSize(QSize(24, 24)) self._attachButton.setFixedSize(26, 26) self._submitButton = QPushButton(self) self._submitButton.setText('Submit') self._submitButton.setFocusProxy(self) # create attachments widget self._attachmentsEdit = XMultiTagEdit(self) self._attachmentsEdit.setAutoResizeToContents(True) self._attachmentsEdit.setFrameShape(XMultiTagEdit.NoFrame) self._attachmentsEdit.setViewMode(XMultiTagEdit.ListMode) self._attachmentsEdit.setEditable(False) self._attachmentsEdit.setFocusProxy(self) self._attachmentsEdit.hide() # define toolbar layout spacer = QWidget(self) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._attachAction = self._toolbar.addWidget(self._attachButton) self._toolbar.addWidget(spacer) self._toolbar.addWidget(self._submitButton) # set standard properties self.setAutoResizeToContents(True) self.setHint('add comment') self.setFocusPolicy(Qt.StrongFocus) self.setRequireShiftForNewLine(True) # create connections self._attachButton.clicked.connect(self.attachmentRequested) self._submitButton.clicked.connect(self.acceptText) self._attachmentsEdit.tagRemoved.connect(self.removeAttachment) self.focusChanged.connect(self.setToolbarVisible)
def __init__(self, parent=None): super(XPagesWidget, self).__init__(parent) # define custom properties self._currentPage = 1 self._pageCount = 10 self._itemCount = 0 self._pageSize = 50 self._itemsTitle = 'items' self._pagesSpinner = QSpinBox() self._pagesSpinner.setMinimum(1) self._pagesSpinner.setMaximum(10) self._pageSizeCombo = XComboBox(self) self._pageSizeCombo.setHint('all') self._pageSizeCombo.addItems(['', '25', '50', '75', '100']) self._pageSizeCombo.setCurrentIndex(2) self._nextButton = QToolButton(self) self._nextButton.setAutoRaise(True) self._nextButton.setArrowType(Qt.RightArrow) self._nextButton.setFixedWidth(16) self._prevButton = QToolButton(self) self._prevButton.setAutoRaise(True) self._prevButton.setArrowType(Qt.LeftArrow) self._prevButton.setFixedWidth(16) self._prevButton.setEnabled(False) self._pagesLabel = QLabel('of 10 for ', self) self._itemsLabel = QLabel(' items per page', self) # define the interface layout = QHBoxLayout() layout.addWidget(QLabel('Page', self)) layout.addWidget(self._prevButton) layout.addWidget(self._pagesSpinner) layout.addWidget(self._nextButton) layout.addWidget(self._pagesLabel) layout.addWidget(self._pageSizeCombo) layout.addWidget(self._itemsLabel) layout.addStretch(1) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) # create connections self._pageSizeCombo.currentIndexChanged.connect(self.pageSizePicked) self._nextButton.clicked.connect(self.gotoNext) self._prevButton.clicked.connect(self.gotoPrevious) self._pagesSpinner.editingFinished.connect(self.assignCurrentPage)
def __init__( self, parent = None ): super(XPagesWidget, self).__init__( parent ) # define custom properties self._currentPage = 1 self._pageCount = 10 self._itemCount = 0 self._pageSize = 50 self._itemsTitle = 'items' self._pagesSpinner = QSpinBox() self._pagesSpinner.setMinimum(1) self._pagesSpinner.setMaximum(10) self._pageSizeCombo = XComboBox(self) self._pageSizeCombo.setHint('all') self._pageSizeCombo.addItems(['', '25', '50', '75', '100']) self._pageSizeCombo.setCurrentIndex(2) self._nextButton = QToolButton(self) self._nextButton.setAutoRaise(True) self._nextButton.setArrowType(Qt.RightArrow) self._nextButton.setFixedWidth(16) self._prevButton = QToolButton(self) self._prevButton.setAutoRaise(True) self._prevButton.setArrowType(Qt.LeftArrow) self._prevButton.setFixedWidth(16) self._prevButton.setEnabled(False) self._pagesLabel = QLabel('of 10 for ', self) self._itemsLabel = QLabel(' items per page', self) # define the interface layout = QHBoxLayout() layout.addWidget(QLabel('Page', self)) layout.addWidget(self._prevButton) layout.addWidget(self._pagesSpinner) layout.addWidget(self._nextButton) layout.addWidget(self._pagesLabel) layout.addWidget(self._pageSizeCombo) layout.addWidget(self._itemsLabel) layout.addStretch(1) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) # create connections self._pageSizeCombo.currentIndexChanged.connect(self.pageSizePicked) self._nextButton.clicked.connect(self.gotoNext) self._prevButton.clicked.connect(self.gotoPrevious) self._pagesSpinner.editingFinished.connect(self.assignCurrentPage)
def __init__( self, parent ): super(XLocationWidget, self).__init__(parent) # define the interface self._locationEdit = XLineEdit(self) self._locationButton = QToolButton(self) self._urlTemplate = 'http://maps.google.com/maps?%(params)s' self._urlQueryKey = 'q' self._locationButton.setAutoRaise(True) self._locationButton.setIcon(QIcon(resources.find('img/map.png'))) self._locationEdit.setHint('no location set') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._locationEdit) layout.addWidget(self._locationButton) self.setLayout(layout) # create connections self._locationEdit.textChanged.connect(self.locationChanged) self._locationEdit.textEdited.connect(self.locationEdited) self._locationButton.clicked.connect(self.browseMaps)
def __init__( self, parent ): super(XUrlWidget, self).__init__(parent) # define the interface self._urlEdit = XLineEdit(self) self._urlButton = QToolButton(self) self._urlButton.setAutoRaise(True) self._urlButton.setIcon(QIcon(resources.find('img/web.png'))) self._urlButton.setToolTip('Browse Link') self._urlButton.setFocusPolicy(Qt.NoFocus) self._urlEdit.setHint('http://') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._urlEdit) layout.addWidget(self._urlButton) self.setLayout(layout) self.setFocusPolicy(Qt.StrongFocus) # create connections self._urlEdit.textChanged.connect(self.urlChanged) self._urlEdit.textEdited.connect(self.urlEdited) self._urlButton.clicked.connect(self.browse)
def __init__(self, parent=None): super(XViewProfileManager, self).__init__(parent) # define custom properties self._profiles = [] self._optionsMenuPolicy = Qt.DefaultContextMenu self._viewWidget = None # define the interface self._profileCombo = QComboBox(self) self._optionsButton = QToolButton(self) self._optionsButton.setAutoRaise(True) self._optionsButton.setToolTip('Advanced Options') self._optionsButton.setIcon(QIcon(resources.find('img/advanced.png'))) layout = QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._profileCombo) layout.addWidget(self._optionsButton) self.setLayout(layout) # create connections self._profileCombo.currentIndexChanged.connect(self.handleProfileChange) self._optionsButton.clicked.connect(self.showOptionsMenu)
def __init__( self, parent = None ): super(XFilepathEdit, self).__init__( parent ) # define custom properties self._validated = False self._validForeground = QColor(0, 120, 0) self._validBackground = QColor(0, 120, 0, 100) self._invalidForeground = QColor(255, 0, 0) self._invalidBackground = QColor(255, 0, 0, 100) self._normalizePath = False self._filepathMode = XFilepathEdit.Mode.OpenFile self._filepathEdit = XLineEdit(self) self._filepathButton = QToolButton(self) self._filepathTypes = 'All Files (*.*)' # set default properties ico = projexui.resources.find('img/folder.png') self._filepathEdit.setReadOnly(False) self._filepathEdit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._filepathButton.setText('...') self._filepathButton.setFixedSize(25, 23) self._filepathButton.setAutoRaise(True) self._filepathButton.setIcon(QIcon(ico)) self._filepathEdit.setContextMenuPolicy(Qt.CustomContextMenu) self.setWindowTitle('Load File') self.setAcceptDrops(True) # define the layout layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._filepathEdit) layout.addWidget(self._filepathButton) self.setLayout(layout) # create connections self._filepathEdit.installEventFilter(self) self._filepathButton.clicked.connect( self.pickFilepath ) self._filepathEdit.textChanged.connect( self.emitFilepathChanged ) self._filepathEdit.textChanged.connect( self.validateFilepath ) self._filepathEdit.customContextMenuRequested.connect( self.showMenu )
def clear(self): """ Clears out this toolbar from the system. """ # preserve the collapse button super(XToolBar, self).clear() # clears out the toolbar if self.isCollapsable(): self._collapseButton = QToolButton(self) self._collapseButton.setAutoRaise(True) self._collapseButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.addWidget(self._collapseButton) self.refreshButton() # create connection self._collapseButton.clicked.connect(self.toggleCollapsed) elif self._collapseButton: self._collapseButton.setParent(None) self._collapseButton.deleteLater() self._collapseButton = None
def __init__(self, parent=None, buttons=None): super(XPopupWidget, self).__init__(parent) # define custom properties self._anchor = XPopupWidget.Anchor.TopCenter self._autoCalculateAnchor = False self._autoCloseOnAccept = True self._autoCloseOnReject = True self._autoCloseOnFocusOut = False self._autoDefault = True self._first = True self._animated = False self._currentMode = None self._positionLinkedTo = [] self._possibleAnchors = XPopupWidget.Anchor.all() # define controls self._result = 0 self._resizable = True self._popupPadding = 10 self._titleBarVisible = True self._buttonBoxVisible = True self._dialogButton = QToolButton(self) self._closeButton = QToolButton(self) self._scrollArea = QScrollArea(self) self._sizeGrip = QSizeGrip(self) self._sizeGrip.setFixedWidth(12) self._sizeGrip.setFixedHeight(12) self._leftSizeGrip = QSizeGrip(self) self._leftSizeGrip.setFixedWidth(12) self._leftSizeGrip.setFixedHeight(12) if buttons is None: buttons = QDialogButtonBox.NoButton self._buttonBox = QDialogButtonBox(buttons, Qt.Horizontal, self) self._buttonBox.setContentsMargins(3, 0, 3, 9) self._scrollArea.setWidgetResizable(True) self._scrollArea.setFrameShape(QScrollArea.NoFrame) self._scrollArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) palette = self.palette() self._scrollArea.setPalette(palette) self._dialogButton.setToolTip('Popout to Dialog') self._closeButton.setToolTip('Close Popup') for btn in (self._dialogButton, self._closeButton): btn.setAutoRaise(True) btn.setIconSize(QSize(14, 14)) btn.setMaximumSize(16, 16) # setup the icons icon = QIcon(projexui.resources.find('img/dialog.png')) self._dialogButton.setIcon(icon) icon = QIcon(projexui.resources.find('img/close.png')) self._closeButton.setIcon(icon) # define the ui hlayout = QHBoxLayout() hlayout.setSpacing(0) hlayout.addStretch(1) hlayout.addWidget(self._dialogButton) hlayout.addWidget(self._closeButton) hlayout.setContentsMargins(0, 0, 0, 0) hlayout2 = QHBoxLayout() hlayout2.addWidget(self._buttonBox) hlayout2.setContentsMargins(0, 0, 3, 0) vlayout = QVBoxLayout() vlayout.addLayout(hlayout) vlayout.addWidget(self._scrollArea) vlayout.addLayout(hlayout2) vlayout.setContentsMargins(3, 2, 3, 2) vlayout.setSpacing(0) self.setLayout(vlayout) self.setPositionLinkedTo(parent) # set default properties self.setAutoFillBackground(True) self.setBackgroundRole(QPalette.Window) self.setWindowTitle('Popup') self.setFocusPolicy(Qt.StrongFocus) self.setCurrentMode(XPopupWidget.Mode.Popup) # create connections self._dialogButton.clicked.connect(self.setDialogMode) self._closeButton.clicked.connect(self.reject) self._buttonBox.accepted.connect(self.accept) self._buttonBox.rejected.connect(self.reject) self._buttonBox.clicked.connect(self.handleButtonClick)
class XUrlWidget(QWidget): urlChanged = Signal(str) urlEdited = Signal() def __init__(self, parent): super(XUrlWidget, self).__init__(parent) # define the interface self._urlEdit = XLineEdit(self) self._urlButton = QToolButton(self) self._urlButton.setAutoRaise(True) self._urlButton.setIcon(QIcon(resources.find('img/web.png'))) self._urlButton.setToolTip('Browse Link') self._urlButton.setFocusPolicy(Qt.NoFocus) self._urlEdit.setHint('http://') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._urlEdit) layout.addWidget(self._urlButton) self.setLayout(layout) self.setFocusPolicy(Qt.StrongFocus) # create connections self._urlEdit.textChanged.connect(self.urlChanged) self._urlEdit.textEdited.connect(self.urlEdited) self._urlButton.clicked.connect(self.browse) def blockSignals(self, state): """ Blocks the signals for this widget and its sub-parts. :param state | <bool> """ super(XUrlWidget, self).blockSignals(state) self._urlEdit.blockSignals(state) self._urlButton.blockSignals(state) def browse(self): """ Brings up a web browser with the address in a Google map. """ webbrowser.open(self.url()) def hint(self): """ Returns the hint associated with this widget. :return <str> """ return self._urlEdit.hint() def lineEdit(self): """ Returns the line edit linked with this widget. :return <XLineEdit> """ return self._urlEdit def setFocus(self): """ Sets the focus for this widget on its line edit. """ self._urlEdit.setFocus() @Slot(str) def setHint(self, hint): """ Sets the hint associated with this widget. :param hint | <str> """ self._urlEdit.setHint(hint) @Slot(str) def setUrl(self, url): """ Sets the url for this widget to the inputed url. :param url | <str> """ self._urlEdit.setText(nativestring(url)) def url(self): """ Returns the current url from the edit. :return <str> """ return nativestring(self._urlEdit.text()) x_hint = Property(str, hint, setHint) x_url = Property(str, url, setUrl)
def rebuild( self ): """ Rebuilds the user interface buttons for this widget. """ self.setUpdatesEnabled(False) # sync up the toolbuttons with our actions actions = self._actionGroup.actions() btns = self.findChildren(QToolButton) horiz = self.direction() in (QBoxLayout.LeftToRight, QBoxLayout.RightToLeft) # remove unnecessary buttons if len(actions) < len(btns): rem_btns = btns[len(actions)-1:] btns = btns[:len(actions)] for btn in rem_btns: btn.close() btn.setParent(None) btn.deleteLater() # create new buttons elif len(btns) < len(actions): for i in range(len(btns), len(actions)): btn = QToolButton(self) btn.setAutoFillBackground(True) btns.append(btn) self.layout().addWidget(btn) btn.clicked.connect(self.emitClicked) # determine coloring options palette = self.palette() checked = palette.color(palette.Highlight) checked_fg = palette.color(palette.HighlightedText) unchecked = palette.color(palette.Button) unchecked_fg = palette.color(palette.ButtonText) border = palette.color(palette.Mid) # define the stylesheet options options = {} options['top_left_radius'] = 0 options['top_right_radius'] = 0 options['bot_left_radius'] = 0 options['bot_right_radius'] = 0 options['border_color'] = border.name() options['checked_fg'] = checked_fg.name() options['checked_bg'] = checked.name() options['checked_bg_alt'] = checked.darker(120).name() options['unchecked_fg'] = unchecked_fg.name() options['unchecked_bg'] = unchecked.name() options['unchecked_bg_alt'] = unchecked.darker(120).name() options['padding_top'] = 1 options['padding_bottom'] = 1 options['padding_left'] = 1 options['padding_right'] = 1 if horiz: options['x1'] = 0 options['y1'] = 0 options['x2'] = 0 options['y2'] = 1 else: options['x1'] = 0 options['y1'] = 0 options['x2'] = 1 options['y2'] = 1 # sync up the actions and buttons count = len(actions) palette = self.palette() font = self.font() for i, action in enumerate(actions): btn = btns[i] # assign the action for this button if btn.defaultAction() != action: # clear out any existing actions for act in btn.actions(): btn.removeAction(act) # assign the given action btn.setDefaultAction(action) options['top_left_radius'] = 1 options['bot_left_radius'] = 1 options['top_right_radius'] = 1 options['bot_right_radius'] = 1 if horiz: options['padding_left'] = self._padding options['padding_right'] = self._padding else: options['padding_top'] = self._padding options['padding_bottom'] = self._padding if not i: if horiz: options['top_left_radius'] = self.cornerRadius() options['bot_left_radius'] = self.cornerRadius() options['padding_left'] += self.cornerRadius() / 3.0 else: options['top_left_radius'] = self.cornerRadius() options['top_right_radius'] = self.cornerRadius() options['padding_top'] += self.cornerRadius() / 3.0 if i == count - 1: if horiz: options['top_right_radius'] = self.cornerRadius() options['bot_right_radius'] = self.cornerRadius() options['padding_right'] += self.cornerRadius() / 3.0 else: options['bot_left_radius'] = self.cornerRadius() options['bot_right_radius'] = self.cornerRadius() options['padding_bottom'] += self.cornerRadius() / 3.0 btn.setFont(font) btn.setPalette(palette) btn.setStyleSheet(TOOLBUTTON_STYLE % options) if horiz: btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) else: btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.setUpdatesEnabled(True)
class XViewProfileManager(QWidget): currentProfileChanged = Signal(PyObject) optionsMenuRequested = Signal(QPoint) def __init__(self, parent=None): super(XViewProfileManager, self).__init__(parent) # define custom properties self._profiles = [] self._optionsMenuPolicy = Qt.DefaultContextMenu self._viewWidget = None # define the interface self._profileCombo = QComboBox(self) self._optionsButton = QToolButton(self) self._optionsButton.setAutoRaise(True) self._optionsButton.setToolTip('Advanced Options') self._optionsButton.setIcon(QIcon(resources.find('img/advanced.png'))) layout = QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._profileCombo) layout.addWidget(self._optionsButton) self.setLayout(layout) # create connections self._profileCombo.currentIndexChanged.connect(self.handleProfileChange) self._optionsButton.clicked.connect(self.showOptionsMenu) def addProfile(self, profile): """ Adds the inputed profile to the system. :param profile | <XViewProfile> """ if ( profile in self._profiles ): return self._profiles.append(profile) self._profileCombo.blockSignals(True) self._profileCombo.addItem(profile.name()) self._profileCombo.setCurrentIndex(self._profileCombo.count()-1) self._profileCombo.blockSignals(False) def currentProfile(self): """ Returns the currently selected profile from the system. :return <XViewProfile> """ index = self._profileCombo.currentIndex() if 0 <= index and index < len(self._profiles): return self._profiles[index] return None def handleProfileChange(self): """ Emits that the current profile has changed. """ # restore the profile settings prof = self.currentProfile() vwidget = self.viewWidget() if vwidget: prof.restore(vwidget) if not self.signalsBlocked(): self.currentProfileChanged.emit(self.currentProfile()) def optionsMenuPolicy(self): """ Returns the option menu policy for this widget. :return <Qt.MenuPolicy> """ return self._optionsMenuPolicy def profiles(self): """ Returns a list of all the profiles for this system. :return [<XViewProfile>, ..] """ return self._profiles def removeProfile(self, profile): """ Adds the inputed profile to the system. :param profile | <XViewProfile> """ if not profile in self._profiles: return index = self._profiles.index(profile) self._profiles.remove(profile) self._profileCombo.blockSignals(True) self._profileCombo.takeItem(index) self._profileCombo.blockSignals(False) def restoreSettings(self, settings): """ Restores settings from the application. :param settings | <QSettings> """ settings.beginGroup(self.objectName()) curr_prof = None curr_name = unwrapVariant(settings.value('current')) profiles = [] for prof_name in settings.childGroups(): settings.beginGroup(prof_name) prof_str = unwrapVariant(settings.value('profile')) profile = XViewProfile.fromString(prof_str) profile.setName(prof_name) if prof_name == curr_name: curr_prof = profile profiles.append(profile) settings.endGroup() self.blockSignals(True) self._profileCombo.blockSignals(True) self.setProfiles(profiles) if curr_prof: self.setCurrentProfile(curr_prof) self._profileCombo.blockSignals(False) self.blockSignals(False) settings.endGroup() def saveSettings(self, settings): """ Saves the settings for this widget to the application :param settings | <QSettings> """ settings.beginGroup(self.objectName()) curr_prof = self.currentProfile() if curr_prof: settings.setValue('current', curr_prof.name()) for profile in self.profiles(): settings.beginGroup(profile.name()) settings.setValue('profile', wrapVariant(profile.toString())) settings.endGroup() settings.endGroup() def setCurrentProfile(self, profile): """ Sets the current profile to the inputed profile. :param profile | <XViewProfile> """ try: index = self._profiles.index(profile) except ValueError: index = -1 self._profileCombo.setCurrentIndex(index) def setOptionsMenuPolicy(self, menuPolicy): """ Sets the options menu policy for this item. :param menuPolicy | <Qt.MenuPolicy> """ self._optionsMenuPolicy = menuPolicy def setProfiles(self, profiles): """ Sets a list of profiles to be the options for the manager. :param profiles | [<XViewProfile>, ..] """ self.blockSignals(True) self._profileCombo.blockSignals(True) self._profiles = profiles[:] self._profileCombo.clear() self._profileCombo.addItems(map(lambda x: x.name(), profiles)) self._profileCombo.blockSignals(False) self.blockSignals(False) def setViewWidget(self, viewWidget): """ Sets the view widget instance linked with this manager. :param viewWidget | <XViewWidget> """ self._viewWidget = viewWidget def showOptionsMenu(self): """ Displays the options menu. If the option menu policy is set to CustomContextMenu, then the optionMenuRequested signal will be emitted, otherwise the default context menu will be displayed. """ point = QPoint(0, self._optionsButton.height()) global_point = self._optionsButton.mapToGlobal(point) # prompt the custom context menu if self.optionsMenuPolicy() == Qt.CustomContextMenu: if not self.signalsBlocked(): self.optionsMenuRequested.emit(global_point) return # use the default context menu menu = XViewProfileManagerMenu(self) menu.exec_(global_point) def viewWidget(self): """ Returns the view widget that is associated with this manager. :return <XViewWidget> """ return self._viewWidget
class XViewProfileManager(QWidget): currentProfileChanged = Signal(PyObject) optionsMenuRequested = Signal(QPoint) def __init__(self, parent=None): super(XViewProfileManager, self).__init__(parent) # define custom properties self._profiles = [] self._optionsMenuPolicy = Qt.DefaultContextMenu self._viewWidget = None # define the interface self._profileCombo = QComboBox(self) self._optionsButton = QToolButton(self) self._optionsButton.setAutoRaise(True) self._optionsButton.setToolTip('Advanced Options') self._optionsButton.setIcon(QIcon(resources.find('img/advanced.png'))) layout = QHBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._profileCombo) layout.addWidget(self._optionsButton) self.setLayout(layout) # create connections self._profileCombo.currentIndexChanged.connect( self.handleProfileChange) self._optionsButton.clicked.connect(self.showOptionsMenu) def addProfile(self, profile): """ Adds the inputed profile to the system. :param profile | <XViewProfile> """ if (profile in self._profiles): return self._profiles.append(profile) self._profileCombo.blockSignals(True) self._profileCombo.addItem(profile.name()) self._profileCombo.setCurrentIndex(self._profileCombo.count() - 1) self._profileCombo.blockSignals(False) def currentProfile(self): """ Returns the currently selected profile from the system. :return <XViewProfile> """ index = self._profileCombo.currentIndex() if 0 <= index and index < len(self._profiles): return self._profiles[index] return None def handleProfileChange(self): """ Emits that the current profile has changed. """ # restore the profile settings prof = self.currentProfile() vwidget = self.viewWidget() if vwidget: prof.restore(vwidget) if not self.signalsBlocked(): self.currentProfileChanged.emit(self.currentProfile()) def optionsMenuPolicy(self): """ Returns the option menu policy for this widget. :return <Qt.MenuPolicy> """ return self._optionsMenuPolicy def profiles(self): """ Returns a list of all the profiles for this system. :return [<XViewProfile>, ..] """ return self._profiles def removeProfile(self, profile): """ Adds the inputed profile to the system. :param profile | <XViewProfile> """ if not profile in self._profiles: return index = self._profiles.index(profile) self._profiles.remove(profile) self._profileCombo.blockSignals(True) self._profileCombo.takeItem(index) self._profileCombo.blockSignals(False) def restoreSettings(self, settings): """ Restores settings from the application. :param settings | <QSettings> """ settings.beginGroup(self.objectName()) curr_prof = None curr_name = unwrapVariant(settings.value('current')) profiles = [] for prof_name in settings.childGroups(): settings.beginGroup(prof_name) prof_str = unwrapVariant(settings.value('profile')) profile = XViewProfile.fromString(prof_str) profile.setName(prof_name) if prof_name == curr_name: curr_prof = profile profiles.append(profile) settings.endGroup() self.blockSignals(True) self._profileCombo.blockSignals(True) self.setProfiles(profiles) if curr_prof: self.setCurrentProfile(curr_prof) self._profileCombo.blockSignals(False) self.blockSignals(False) settings.endGroup() def saveSettings(self, settings): """ Saves the settings for this widget to the application :param settings | <QSettings> """ settings.beginGroup(self.objectName()) curr_prof = self.currentProfile() if curr_prof: settings.setValue('current', curr_prof.name()) for profile in self.profiles(): settings.beginGroup(profile.name()) settings.setValue('profile', wrapVariant(profile.toString())) settings.endGroup() settings.endGroup() def setCurrentProfile(self, profile): """ Sets the current profile to the inputed profile. :param profile | <XViewProfile> """ try: index = self._profiles.index(profile) except ValueError: index = -1 self._profileCombo.setCurrentIndex(index) def setOptionsMenuPolicy(self, menuPolicy): """ Sets the options menu policy for this item. :param menuPolicy | <Qt.MenuPolicy> """ self._optionsMenuPolicy = menuPolicy def setProfiles(self, profiles): """ Sets a list of profiles to be the options for the manager. :param profiles | [<XViewProfile>, ..] """ self.blockSignals(True) self._profileCombo.blockSignals(True) self._profiles = profiles[:] self._profileCombo.clear() self._profileCombo.addItems(map(lambda x: x.name(), profiles)) self._profileCombo.blockSignals(False) self.blockSignals(False) def setViewWidget(self, viewWidget): """ Sets the view widget instance linked with this manager. :param viewWidget | <XViewWidget> """ self._viewWidget = viewWidget def showOptionsMenu(self): """ Displays the options menu. If the option menu policy is set to CustomContextMenu, then the optionMenuRequested signal will be emitted, otherwise the default context menu will be displayed. """ point = QPoint(0, self._optionsButton.height()) global_point = self._optionsButton.mapToGlobal(point) # prompt the custom context menu if self.optionsMenuPolicy() == Qt.CustomContextMenu: if not self.signalsBlocked(): self.optionsMenuRequested.emit(global_point) return # use the default context menu menu = XViewProfileManagerMenu(self) menu.exec_(global_point) def viewWidget(self): """ Returns the view widget that is associated with this manager. :return <XViewWidget> """ return self._viewWidget
class XCommentEdit(XTextEdit): attachmentRequested = Signal() def __init__(self, parent=None): super(XCommentEdit, self).__init__(parent) # define custom properties self._attachments = {} self._showAttachments = True # create toolbar self._toolbar = QToolBar(self) self._toolbar.setMovable(False) self._toolbar.setFixedHeight(30) self._toolbar.setAutoFillBackground(True) self._toolbar.setFocusProxy(self) self._toolbar.hide() # create toolbar buttons self._attachButton = QToolButton(self) self._attachButton.setIcon(QIcon(resources.find('img/attach.png'))) self._attachButton.setToolTip('Add Attachment') self._attachButton.setAutoRaise(True) self._attachButton.setIconSize(QSize(24, 24)) self._attachButton.setFixedSize(26, 26) self._submitButton = QPushButton(self) self._submitButton.setText('Submit') self._submitButton.setFocusProxy(self) # create attachments widget self._attachmentsEdit = XMultiTagEdit(self) self._attachmentsEdit.setAutoResizeToContents(True) self._attachmentsEdit.setFrameShape(XMultiTagEdit.NoFrame) self._attachmentsEdit.setViewMode(XMultiTagEdit.ListMode) self._attachmentsEdit.setEditable(False) self._attachmentsEdit.setFocusProxy(self) self._attachmentsEdit.hide() # define toolbar layout spacer = QWidget(self) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._attachAction = self._toolbar.addWidget(self._attachButton) self._toolbar.addWidget(spacer) self._toolbar.addWidget(self._submitButton) # set standard properties self.setAutoResizeToContents(True) self.setHint('add comment') self.setFocusPolicy(Qt.StrongFocus) self.setRequireShiftForNewLine(True) # create connections self._attachButton.clicked.connect(self.attachmentRequested) self._submitButton.clicked.connect(self.acceptText) self._attachmentsEdit.tagRemoved.connect(self.removeAttachment) self.focusChanged.connect(self.setToolbarVisible) def addAttachment(self, title, attachment): """ Adds an attachment to this comment. :param title | <str> attachment | <variant> """ self._attachments[title] = attachment self.resizeToContents() def attachments(self): """ Returns a list of attachments that have been linked to this widget. :return {<str> title: <variant> attachment, ..} """ return self._attachments.copy() def attachButton(self): """ Returns the attach button from the toolbar for this widget. :return <QToolButton> """ return self._attachButton @Slot() def clear(self): """ Clears out this widget and its attachments. """ # clear the attachment list self._attachments.clear() super(XCommentEdit, self).clear() def isToolbarVisible(self): """ Returns whether or not the toolbar for this comment edit is currently visible to the user. :return <bool> """ return self._toolbar.isVisible() def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clear() event.accept() else: super(XCommentEdit, self).keyPressEvent(event) @Slot() def pickAttachment(self): """ Prompts the user to select an attachment to add to this edit. """ filename = QFileDialog.getOpenFileName(self.window(), 'Select Attachment', '', 'All Files (*.*)') if type(filename) == tuple: filename = nativestring(filename[0]) filename = nativestring(filename) if filename: self.addAttachment(os.path.basename(filename), filename) def removeAttachment(self, title): """ Removes the attachment from the given title. :param title | <str> :return <variant> | attachment """ attachment = self._attachments.pop(nativestring(title), None) if attachment: self.resizeToContents() return attachment def resizeEvent(self, event): super(XCommentEdit, self).resizeEvent(event) self._toolbar.resize(self.width() - 4, 30) edit = self._attachmentsEdit edit.resize(self.width() - 4, edit.height()) def resizeToContents(self): """ Resizes this toolbar based on the contents of its text. """ if self._toolbar.isVisible(): doc = self.document() h = doc.documentLayout().documentSize().height() offset = 34 # update the attachments edit edit = self._attachmentsEdit if self._attachments: edit.move(2, self.height() - edit.height() - 31) edit.setTags(sorted(self._attachments.keys())) edit.show() offset = 34 + edit.height() else: edit.hide() offset = 34 self.setFixedHeight(h + offset) self._toolbar.move(2, self.height() - 32) else: super(XCommentEdit, self).resizeToContents() def setAttachments(self, attachments): """ Sets the attachments for this widget to the inputed list of attachments. :param attachments | {<str> title: <variant> attachment, ..} """ self._attachments = attachments self.resizeToContents() def setSubmitText(self, text): """ Sets the submission text for this edit. :param text | <str> """ self._submitButton.setText(text) def setShowAttachments(self, state): """ Sets whether or not to show the attachments for this edit. :param state | <bool> """ self._showAttachments = state self._attachAction.setVisible(state) def setToolbarVisible(self, state): """ Sets whether or not the toolbar is visible. :param state | <bool> """ self._toolbar.setVisible(state) self.resizeToContents() def showAttachments(self): """ Returns whether or not to show the attachments for this edit. :return <bool> """ return self._showAttachments def submitButton(self): """ Returns the submit button for this edit. :return <QPushButton> """ return self._submitButton def submitText(self): """ Returns the submission text for this edit. :return <str> """ return self._submitButton.text() def toolbar(self): """ Returns the toolbar widget for this comment edit. :return <QToolBar> """ return self._toolbar x_showAttachments = Property(bool, showAttachments, setShowAttachments) x_submitText = Property(str, submitText, setSubmitText)
def rebuild(self): """ Rebuilds the user interface buttons for this widget. """ self.setUpdatesEnabled(False) # sync up the toolbuttons with our actions actions = self._actionGroup.actions() btns = self.findChildren(QToolButton) horiz = self.direction() in (QBoxLayout.LeftToRight, QBoxLayout.RightToLeft) # remove unnecessary buttons if len(actions) < len(btns): rem_btns = btns[len(actions) - 1:] btns = btns[:len(actions)] for btn in rem_btns: btn.close() btn.setParent(None) btn.deleteLater() # create new buttons elif len(btns) < len(actions): for i in range(len(btns), len(actions)): btn = QToolButton(self) btn.setAutoFillBackground(True) btns.append(btn) self.layout().addWidget(btn) btn.clicked.connect(self.emitClicked) # determine coloring options palette = self.palette() checked = palette.color(palette.Highlight) checked_fg = palette.color(palette.HighlightedText) unchecked = palette.color(palette.Button) unchecked_fg = palette.color(palette.ButtonText) border = palette.color(palette.Mid) # define the stylesheet options options = {} options['top_left_radius'] = 0 options['top_right_radius'] = 0 options['bot_left_radius'] = 0 options['bot_right_radius'] = 0 options['border_color'] = border.name() options['checked_fg'] = checked_fg.name() options['checked_bg'] = checked.name() options['checked_bg_alt'] = checked.darker(120).name() options['unchecked_fg'] = unchecked_fg.name() options['unchecked_bg'] = unchecked.name() options['unchecked_bg_alt'] = unchecked.darker(120).name() options['padding_top'] = 1 options['padding_bottom'] = 1 options['padding_left'] = 1 options['padding_right'] = 1 if horiz: options['x1'] = 0 options['y1'] = 0 options['x2'] = 0 options['y2'] = 1 else: options['x1'] = 0 options['y1'] = 0 options['x2'] = 1 options['y2'] = 1 # sync up the actions and buttons count = len(actions) palette = self.palette() font = self.font() for i, action in enumerate(actions): btn = btns[i] # assign the action for this button if btn.defaultAction() != action: # clear out any existing actions for act in btn.actions(): btn.removeAction(act) # assign the given action btn.setDefaultAction(action) options['top_left_radius'] = 1 options['bot_left_radius'] = 1 options['top_right_radius'] = 1 options['bot_right_radius'] = 1 if horiz: options['padding_left'] = self._padding options['padding_right'] = self._padding else: options['padding_top'] = self._padding options['padding_bottom'] = self._padding if not i: if horiz: options['top_left_radius'] = self.cornerRadius() options['bot_left_radius'] = self.cornerRadius() options['padding_left'] += self.cornerRadius() / 3.0 else: options['top_left_radius'] = self.cornerRadius() options['top_right_radius'] = self.cornerRadius() options['padding_top'] += self.cornerRadius() / 3.0 if i == count - 1: if horiz: options['top_right_radius'] = self.cornerRadius() options['bot_right_radius'] = self.cornerRadius() options['padding_right'] += self.cornerRadius() / 3.0 else: options['bot_left_radius'] = self.cornerRadius() options['bot_right_radius'] = self.cornerRadius() options['padding_bottom'] += self.cornerRadius() / 3.0 btn.setFont(font) btn.setPalette(palette) btn.setStyleSheet(TOOLBUTTON_STYLE % options) if horiz: btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) else: btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.setUpdatesEnabled(True)
class XLocationWidget(QWidget): locationChanged = Signal(str) locationEdited = Signal() def __init__( self, parent ): super(XLocationWidget, self).__init__(parent) # define the interface self._locationEdit = XLineEdit(self) self._locationButton = QToolButton(self) self._urlTemplate = 'http://maps.google.com/maps?%(params)s' self._urlQueryKey = 'q' self._locationButton.setAutoRaise(True) self._locationButton.setIcon(QIcon(resources.find('img/map.png'))) self._locationEdit.setHint('no location set') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._locationEdit) layout.addWidget(self._locationButton) self.setLayout(layout) # create connections self._locationEdit.textChanged.connect(self.locationChanged) self._locationEdit.textEdited.connect(self.locationEdited) self._locationButton.clicked.connect(self.browseMaps) def blockSignals( self, state ): """ Blocks the signals for this widget and its sub-parts. :param state | <bool> """ super(XLocationWidget, self).blockSignals(state) self._locationEdit.blockSignals(state) self._locationButton.blockSignals(state) def browseMaps( self ): """ Brings up a web browser with the address in a Google map. """ url = self.urlTemplate() params = urllib.urlencode({self.urlQueryKey(): self.location()}) url = url % {'params': params} webbrowser.open(url) def hint( self ): """ Returns the hint associated with this widget. :return <str> """ return self._locationEdit.hint() def lineEdit( self ): """ Returns the line edit linked with this widget. :return <XLineEdit> """ return self._locationEdit def location( self ): """ Returns the current location from the edit. :return <str> """ return nativestring(self._locationEdit.text()) @Slot(str) def setHint( self, hint ): """ Sets the hint associated with this widget. :param hint | <str> """ self._locationEdit.setHint(hint) @Slot(str) def setLocation( self, location ): """ Sets the location for this widget to the inputed location. :param location | <str> """ self._locationEdit.setText(nativestring(location)) def setUrlQueryKey( self, key ): """ Sets the key for the URL to the inputed key. :param key | <str> """ self._urlQueryKey = nativestring(key) def setUrlTemplate( self, templ ): """ Sets the URL path template that will be used when looking up locations on the web. :param templ | <str> """ self._urlQueryTemplate = nativestring(templ) def urlQueryKey( self ): """ Returns the query key that will be used for this location. :return <str> """ return self._urlQueryKey def urlTemplate( self ): """ Returns the url template that will be used when mapping this location. :return <str> """ return self._urlTemplate x_hint = Property(str, hint, setHint) x_location = Property(str, location, setLocation) x_urlQueryKey = Property(str, urlQueryKey, setUrlQueryKey) x_urlTemplate = Property(str, urlTemplate, setUrlTemplate)
class XToolBar(QToolBar): collapseToggled = Signal(bool) def __init__(self, *args): super(XToolBar, self).__init__(*args) # set custom properties self._collapseButton = None self._collapsable = True self._collapsed = True self._collapsedSize = 14 self._autoCollapsible = False self._precollapseSize = None self._shadowed = False self._colored = False # set standard options self.layout().setSpacing(0) self.layout().setContentsMargins(1, 1, 1, 1) self.setMovable(False) self.clear() self.setMouseTracking(True) self.setOrientation(Qt.Horizontal) self.setCollapsed(False) def autoCollapsible(self): """ Returns whether or not this toolbar is auto-collapsible. When True, it will enter its collapsed state when the user hovers out of the bar. :return <bool> """ return self._autoCollapsible def clear(self): """ Clears out this toolbar from the system. """ # preserve the collapse button super(XToolBar, self).clear() # clears out the toolbar if self.isCollapsable(): self._collapseButton = QToolButton(self) self._collapseButton.setAutoRaise(True) self._collapseButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.addWidget(self._collapseButton) self.refreshButton() # create connection self._collapseButton.clicked.connect(self.toggleCollapsed) elif self._collapseButton: self._collapseButton.setParent(None) self._collapseButton.deleteLater() self._collapseButton = None def count(self): """ Returns the number of actions linked with this toolbar. :return <int> """ return len(self.actions()) def collapseButton(self): """ Returns the collapsing button for this toolbar. :return <QToolButton> """ return self._collapseButton def isCollapsable(self): """ Returns whether or not this toolbar is collapsable. :return <bool> """ return self._collapsable def isCollapsed( self ): """ Returns whether or not this toolbar is in a collapsed state. :return <bool> """ return self._collapsed and self.isCollapsable() def isColored(self): """ Returns whether or not to colorize the buttons on the toolbar when they are highlighted. :return <bool> """ return self._colored def isShadowed(self): """ Returns whether or not to show this toolbar with shadows. :return <bool> """ return self._shadowed def refreshButton(self): """ Refreshes the button for this toolbar. """ collapsed = self.isCollapsed() btn = self._collapseButton if not btn: return btn.setMaximumSize(MAX_SIZE, MAX_SIZE) # set up a vertical scrollbar if self.orientation() == Qt.Vertical: btn.setMaximumHeight(12) else: btn.setMaximumWidth(12) icon = '' # collapse/expand a vertical toolbar if self.orientation() == Qt.Vertical: if collapsed: self.setFixedWidth(self._collapsedSize) btn.setMaximumHeight(MAX_SIZE) btn.setArrowType(Qt.RightArrow) else: self.setMaximumWidth(MAX_SIZE) self._precollapseSize = None btn.setMaximumHeight(12) btn.setArrowType(Qt.LeftArrow) else: if collapsed: self.setFixedHeight(self._collapsedSize) btn.setMaximumWidth(MAX_SIZE) btn.setArrowType(Qt.DownArrow) else: self.setMaximumHeight(1000) self._precollapseSize = None btn.setMaximumWidth(12) btn.setArrowType(Qt.UpArrow) for index in range(1, self.layout().count()): item = self.layout().itemAt(index) if not item.widget(): continue if collapsed: item.widget().setMaximumSize(0, 0) else: item.widget().setMaximumSize(MAX_SIZE, MAX_SIZE) if not self.isCollapsable(): btn.hide() else: btn.show() def resizeEvent(self, event): super(XToolBar, self).resizeEvent(event) if not self._collapsed: if self.orientation() == Qt.Vertical: self._precollapseSize = self.width() else: self._precollapseSize = self.height() def setAutoCollapsible(self, state): """ Sets whether or not this toolbar is auto-collapsible. :param state | <bool> """ self._autoCollapsible = state def setCollapsed(self, state): """ Sets whether or not this toolbar is in a collapsed state. :return <bool> changed """ if state == self._collapsed: return False self._collapsed = state self.refreshButton() if not self.signalsBlocked(): self.collapseToggled.emit(state) return True def setCollapsable(self, state): """ Sets whether or not this toolbar is collapsable. :param state | <bool> """ if self._collapsable == state: return self._collapsable = state self.clear() def setOrientation( self, orientation ): """ Sets the orientation for this toolbar to the inputed value, and \ updates the contents margins and collapse button based on the vaule. :param orientation | <Qt.Orientation> """ super(XToolBar, self).setOrientation(orientation) self.refreshButton() def setShadowed(self, state): """ Sets whether or not this toolbar is shadowed. :param state | <bool> """ self._shadowed = state if state: self._colored = False for child in self.findChildren(XToolButton): child.setShadowed(state) def setColored(self, state): """ Sets whether or not this toolbar is shadowed. :param state | <bool> """ self._colored = state if state: self._shadowed = False for child in self.findChildren(XToolButton): child.setColored(state) def toggleCollapsed( self ): """ Toggles the collapsed state for this toolbar. :return <bool> changed """ return self.setCollapsed(not self.isCollapsed()) x_shadowed = Property(bool, isShadowed, setShadowed) x_colored = Property(bool, isColored, setColored)
def rebuild( self ): """ Rebuilds the parts widget with the latest text. """ navitem = self.currentItem() if ( navitem ): navitem.initialize() self.setUpdatesEnabled(False) self.scrollWidget().show() self._originalText = '' partsw = self.partsWidget() for button in self._buttonGroup.buttons(): self._buttonGroup.removeButton(button) button.close() button.setParent(None) button.deleteLater() # create the root button layout = partsw.layout() parts = self.parts() button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant('')) button.setProperty('is_completer', wrapVariant(True)) last_button = button self._buttonGroup.addButton(button) layout.insertWidget(0, button) # check to see if we have a navigation model setup if ( self._navigationModel ): last_item = self._navigationModel.itemByPath(self.text()) show_last = last_item and last_item.rowCount() > 0 else: show_last = False # load the navigation system count = len(parts) for i, part in enumerate(parts): path = self.separator().join(parts[:i+1]) button = QToolButton(partsw) button.setAutoRaise(True) button.setText(part) if ( self._navigationModel ): item = self._navigationModel.itemByPath(path) if ( item ): button.setIcon(item.icon()) button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(False)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 1, button) # determine if we should show the final button if ( show_last or i < (count - 1) ): button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(True)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 2, button) last_button = button if ( self.scrollWidget().width() < partsw.width() ): self.scrollParts(partsw.width() - self.scrollWidget().width()) self.setUpdatesEnabled(True) self.navigationChanged.emit()
class XUrlWidget(QWidget): urlChanged = Signal(str) urlEdited = Signal() def __init__( self, parent ): super(XUrlWidget, self).__init__(parent) # define the interface self._urlEdit = XLineEdit(self) self._urlButton = QToolButton(self) self._urlButton.setAutoRaise(True) self._urlButton.setIcon(QIcon(resources.find('img/web.png'))) self._urlButton.setToolTip('Browse Link') self._urlButton.setFocusPolicy(Qt.NoFocus) self._urlEdit.setHint('http://') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._urlEdit) layout.addWidget(self._urlButton) self.setLayout(layout) self.setFocusPolicy(Qt.StrongFocus) # create connections self._urlEdit.textChanged.connect(self.urlChanged) self._urlEdit.textEdited.connect(self.urlEdited) self._urlButton.clicked.connect(self.browse) def blockSignals( self, state ): """ Blocks the signals for this widget and its sub-parts. :param state | <bool> """ super(XUrlWidget, self).blockSignals(state) self._urlEdit.blockSignals(state) self._urlButton.blockSignals(state) def browse( self ): """ Brings up a web browser with the address in a Google map. """ webbrowser.open(self.url()) def hint( self ): """ Returns the hint associated with this widget. :return <str> """ return self._urlEdit.hint() def lineEdit( self ): """ Returns the line edit linked with this widget. :return <XLineEdit> """ return self._urlEdit def setFocus(self): """ Sets the focus for this widget on its line edit. """ self._urlEdit.setFocus() @Slot(str) def setHint( self, hint ): """ Sets the hint associated with this widget. :param hint | <str> """ self._urlEdit.setHint(hint) @Slot(str) def setUrl( self, url ): """ Sets the url for this widget to the inputed url. :param url | <str> """ self._urlEdit.setText(nativestring(url)) def url( self ): """ Returns the current url from the edit. :return <str> """ return nativestring(self._urlEdit.text()) x_hint = Property(str, hint, setHint) x_url = Property(str, url, setUrl)
class XPopupWidget(QWidget): """ """ Direction = enum('North', 'South', 'East', 'West') Mode = enum('Popup', 'Dialog', 'ToolTip') Anchor = enum('TopLeft', 'TopCenter', 'TopRight', 'LeftTop', 'LeftCenter', 'LeftBottom', 'RightTop', 'RightCenter', 'RightBottom', 'BottomLeft', 'BottomCenter', 'BottomRight') aboutToShow = Signal() accepted = Signal() closed = Signal() rejected = Signal() resetRequested = Signal() shown = Signal() buttonClicked = Signal(QAbstractButton) def __init__(self, parent=None, buttons=None): super(XPopupWidget, self).__init__(parent) # define custom properties self._anchor = XPopupWidget.Anchor.TopCenter self._autoCalculateAnchor = False self._autoCloseOnAccept = True self._autoCloseOnReject = True self._autoCloseOnFocusOut = False self._autoDefault = True self._first = True self._animated = False self._currentMode = None self._positionLinkedTo = [] self._possibleAnchors = XPopupWidget.Anchor.all() # define controls self._result = 0 self._resizable = True self._popupPadding = 10 self._titleBarVisible = True self._buttonBoxVisible = True self._dialogButton = QToolButton(self) self._closeButton = QToolButton(self) self._scrollArea = QScrollArea(self) self._sizeGrip = QSizeGrip(self) self._sizeGrip.setFixedWidth(12) self._sizeGrip.setFixedHeight(12) self._leftSizeGrip = QSizeGrip(self) self._leftSizeGrip.setFixedWidth(12) self._leftSizeGrip.setFixedHeight(12) if buttons is None: buttons = QDialogButtonBox.NoButton self._buttonBox = QDialogButtonBox(buttons, Qt.Horizontal, self) self._buttonBox.setContentsMargins(3, 0, 3, 9) self._scrollArea.setWidgetResizable(True) self._scrollArea.setFrameShape(QScrollArea.NoFrame) self._scrollArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) palette = self.palette() self._scrollArea.setPalette(palette) self._dialogButton.setToolTip('Popout to Dialog') self._closeButton.setToolTip('Close Popup') for btn in (self._dialogButton, self._closeButton): btn.setAutoRaise(True) btn.setIconSize(QSize(14, 14)) btn.setMaximumSize(16, 16) # setup the icons icon = QIcon(projexui.resources.find('img/dialog.png')) self._dialogButton.setIcon(icon) icon = QIcon(projexui.resources.find('img/close.png')) self._closeButton.setIcon(icon) # define the ui hlayout = QHBoxLayout() hlayout.setSpacing(0) hlayout.addStretch(1) hlayout.addWidget(self._dialogButton) hlayout.addWidget(self._closeButton) hlayout.setContentsMargins(0, 0, 0, 0) hlayout2 = QHBoxLayout() hlayout2.addWidget(self._buttonBox) hlayout2.setContentsMargins(0, 0, 3, 0) vlayout = QVBoxLayout() vlayout.addLayout(hlayout) vlayout.addWidget(self._scrollArea) vlayout.addLayout(hlayout2) vlayout.setContentsMargins(3, 2, 3, 2) vlayout.setSpacing(0) self.setLayout(vlayout) self.setPositionLinkedTo(parent) # set default properties self.setAutoFillBackground(True) self.setBackgroundRole(QPalette.Window) self.setWindowTitle('Popup') self.setFocusPolicy(Qt.StrongFocus) self.setCurrentMode(XPopupWidget.Mode.Popup) # create connections self._dialogButton.clicked.connect(self.setDialogMode) self._closeButton.clicked.connect(self.reject) self._buttonBox.accepted.connect(self.accept) self._buttonBox.rejected.connect(self.reject) self._buttonBox.clicked.connect(self.handleButtonClick) def addButton(self, button, role=QDialogButtonBox.ActionRole): """ Adds a custom button to the button box for this popup widget. :param button | <QAbstractButton> || <str> :return <button> || None (based on if a button or string was given) """ return self._buttonBox.addButton(button, role) def adjustContentsMargins( self ): """ Adjusts the contents for this widget based on the anchor and \ mode. """ anchor = self.anchor() mode = self.currentMode() # margins for a dialog if ( mode == XPopupWidget.Mode.Dialog ): self.setContentsMargins(0, 0, 0, 0) # margins for a top anchor point elif ( anchor & (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.TopCenter | XPopupWidget.Anchor.TopRight) ): self.setContentsMargins(0, self.popupPadding() + 5, 0, 0) # margins for a bottom anchor point elif ( anchor & (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter | XPopupWidget.Anchor.BottomRight) ): self.setContentsMargins(0, 0, 0, self.popupPadding()) # margins for a left anchor point elif ( anchor & (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.LeftCenter | XPopupWidget.Anchor.LeftBottom) ): self.setContentsMargins(self.popupPadding(), 0, 0, 0) # margins for a right anchor point else: self.setContentsMargins(0, 0, self.popupPadding(), 0) self.adjustMask() def adjustMask(self): """ Updates the alpha mask for this popup widget. """ if self.currentMode() == XPopupWidget.Mode.Dialog: self.clearMask() return path = self.borderPath() bitmap = QBitmap(self.width(), self.height()) bitmap.fill(QColor('white')) with XPainter(bitmap) as painter: painter.setRenderHint(XPainter.Antialiasing) pen = QPen(QColor('black')) pen.setWidthF(0.75) painter.setPen(pen) painter.setBrush(QColor('black')) painter.drawPath(path) self.setMask(bitmap) def adjustSize(self): """ Adjusts the size of this popup to best fit the new widget size. """ widget = self.centralWidget() if widget is None: super(XPopupWidget, self).adjustSize() return widget.adjustSize() hint = widget.minimumSizeHint() size = widget.minimumSize() width = max(size.width(), hint.width()) height = max(size.height(), hint.height()) width += 20 height += 20 if self._buttonBoxVisible: height += self.buttonBox().height() + 10 if self._titleBarVisible: height += max(self._dialogButton.height(), self._closeButton.height()) + 10 curr_w = self.width() curr_h = self.height() # determine if we need to move based on our anchor anchor = self.anchor() if anchor & (self.Anchor.LeftBottom | self.Anchor.RightBottom | \ self.Anchor.BottomLeft | self.Anchor.BottomCenter | \ self.Anchor.BottomRight): delta_y = height - curr_h elif anchor & (self.Anchor.LeftCenter | self.Anchor.RightCenter): delta_y = (height - curr_h) / 2 else: delta_y = 0 if anchor & (self.Anchor.RightTop | self.Anchor.RightCenter | \ self.Anchor.RightTop | self.Anchor.TopRight): delta_x = width - curr_w elif anchor & (self.Anchor.TopCenter | self.Anchor.BottomCenter): delta_x = (width - curr_w) / 2 else: delta_x = 0 self.setMinimumSize(width, height) self.resize(width, height) pos = self.pos() pos.setX(pos.x() - delta_x) pos.setY(pos.y() - delta_y) self.move(pos) @Slot() def accept(self): """ Emits the accepted signal and closes the popup. """ self._result = 1 if not self.signalsBlocked(): self.accepted.emit() if self.autoCloseOnAccept(): self.close() def anchor( self ): """ Returns the anchor point for this popup widget. :return <XPopupWidget.Anchor> """ return self._anchor def autoCalculateAnchor( self ): """ Returns whether or not this popup should calculate the anchor point on popup based on the parent widget and the popup point. :return <bool> """ return self._autoCalculateAnchor def autoCloseOnAccept( self ): """ Returns whether or not this popup widget manages its own close on accept behavior. :return <bool> """ return self._autoCloseOnAccept def autoCloseOnReject( self ): """ Returns whether or not this popup widget manages its own close on reject behavior. :return <bool> """ return self._autoCloseOnReject def autoCloseOnFocusOut(self): """ Returns whether or not this popup widget should auto-close when the user clicks off the view. :return <bool> """ return self._autoCloseOnFocusOut def autoDefault(self): """ Returns whether or not clicking enter should default to the accept key. :return <bool> """ return self._autoDefault def borderPath(self): """ Returns the border path that will be drawn for this widget. :return <QPainterPath> """ path = QPainterPath() x = 1 y = 1 w = self.width() - 2 h = self.height() - 2 pad = self.popupPadding() anchor = self.anchor() # create a path for a top-center based popup if anchor == XPopupWidget.Anchor.TopCenter: path.moveTo(x, y + pad) path.lineTo(x + ((w/2) - pad), y + pad) path.lineTo(x + (w/2), y) path.lineTo(x + ((w/2) + pad), y + pad) path.lineTo(x + w, y + pad) path.lineTo(x + w, y + h) path.lineTo(x, y + h) path.lineTo(x, y + pad) # create a path for a top-left based popup elif anchor == XPopupWidget.Anchor.TopLeft: path.moveTo(x, y + pad) path.lineTo(x + pad, y) path.lineTo(x + 2 * pad, y + pad) path.lineTo(x + w, y + pad) path.lineTo(x + w, y + h) path.lineTo(x, y + h) path.lineTo(x, y + pad) # create a path for a top-right based popup elif anchor == XPopupWidget.Anchor.TopRight: path.moveTo(x, y + pad) path.lineTo(x + w - 2 * pad, y + pad) path.lineTo(x + w - pad, y) path.lineTo(x + w, y + pad) path.lineTo(x + w, y + h) path.lineTo(x, y + h) path.lineTo(x, y + pad) # create a path for a bottom-left based popup elif anchor == XPopupWidget.Anchor.BottomLeft: path.moveTo(x, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h - pad) path.lineTo(x + 2 * pad, y + h - pad) path.lineTo(x + pad, y + h) path.lineTo(x, y + h - pad) path.lineTo(x, y) # create a path for a south based popup elif anchor == XPopupWidget.Anchor.BottomCenter: path.moveTo(x, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h - pad) path.lineTo(x + ((w/2) + pad), y + h - pad) path.lineTo(x + (w/2), y + h) path.lineTo(x + ((w/2) - pad), y + h - pad) path.lineTo(x, y + h - pad) path.lineTo(x, y) # create a path for a bottom-right based popup elif anchor == XPopupWidget.Anchor.BottomRight: path.moveTo(x, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h - pad) path.lineTo(x + w - pad, y + h) path.lineTo(x + w - 2 * pad, y + h - pad) path.lineTo(x, y + h - pad) path.lineTo(x, y) # create a path for a right-top based popup elif anchor == XPopupWidget.Anchor.RightTop: path.moveTo(x, y) path.lineTo(x + w - pad, y) path.lineTo(x + w, y + pad) path.lineTo(x + w - pad, y + 2 * pad) path.lineTo(x + w - pad, y + h) path.lineTo(x, y + h) path.lineTo(x, y) # create a path for a right-center based popup elif anchor == XPopupWidget.Anchor.RightCenter: path.moveTo(x, y) path.lineTo(x + w - pad, y) path.lineTo(x + w - pad, y + ((h/2) - pad)) path.lineTo(x + w, y + (h/2)) path.lineTo(x + w - pad, y + ((h/2) + pad)) path.lineTo(x + w - pad, y + h) path.lineTo(x, y + h) path.lineTo(x, y) # create a path for a right-bottom based popup elif anchor == XPopupWidget.Anchor.RightBottom: path.moveTo(x, y) path.lineTo(x + w - pad, y) path.lineTo(x + w - pad, y + h - 2 * pad) path.lineTo(x + w, y + h - pad) path.lineTo(x + w - pad, y + h) path.lineTo(x, y + h) path.lineTo(x, y) # create a path for a left-top based popup elif anchor == XPopupWidget.Anchor.LeftTop: path.moveTo(x + pad, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h) path.lineTo(x + pad, y + h) path.lineTo(x + pad, y + 2 * pad) path.lineTo(x, y + pad) path.lineTo(x + pad, y) # create a path for an left-center based popup elif anchor == XPopupWidget.Anchor.LeftCenter: path.moveTo(x + pad, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h) path.lineTo(x + pad, y + h) path.lineTo(x + pad, y + ((h/2) + pad)) path.lineTo(x, y + (h/2)) path.lineTo(x + pad, y + ((h/2) - pad)) path.lineTo(x + pad, y) # create a path for a left-bottom based popup elif anchor == XPopupWidget.Anchor.LeftBottom: path.moveTo(x + pad, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h) path.lineTo(x + pad, y + h) path.lineTo(x, y + h - pad) path.lineTo(x + pad, y + h - 2 * pad) path.lineTo(x + pad, y) return path def buttonBox( self ): """ Returns the button box that is used to control this popup widget. :return <QDialogButtonBox> """ return self._buttonBox def centralWidget( self ): """ Returns the central widget that is being used by this popup. :return <QWidget> """ return self._scrollArea.widget() def close(self): """ Closes the popup widget and central widget. """ widget = self.centralWidget() if widget and not widget.close(): return super(XPopupWidget, self).close() def closeEvent(self, event): widget = self.centralWidget() if widget and not widget.close() and \ self.currentMode() != XPopupWidget.Mode.ToolTip: event.ignore() else: super(XPopupWidget, self).closeEvent(event) self.closed.emit() def currentMode( self ): """ Returns the current mode for this widget. :return <XPopupWidget.Mode> """ return self._currentMode @deprecatedmethod('XPopupWidget', 'Direction is no longer used, use anchor instead') def direction( self ): """ Returns the current direction parameter for this widget. :return <XPopupWidget.Direction> """ anchor = self.anchor() if ( anchor & (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.TopCenter | XPopupWidget.Anchor.TopRight) ): return XPopupWidget.Direction.North elif ( anchor & (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter | XPopupWidget.Anchor.BottomRight) ): return XPopupWidget.Direction.South elif ( anchor & (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.LeftCenter | XPopupWidget.Anchor.LeftBottom) ): return XPopupWidget.Direction.East else: return XPopupWidget.Direction.West def exec_(self, pos=None): self._result = 0 self.setWindowModality(Qt.ApplicationModal) self.popup(pos) while self.isVisible(): QApplication.processEvents() return self.result() def eventFilter(self, object, event): """ Processes when the window is moving to update the position for the popup if in popup mode. :param object | <QObject> event | <QEvent> """ if not self.isVisible(): return False links = self.positionLinkedTo() is_dialog = self.currentMode() == self.Mode.Dialog if object not in links: return False if event.type() == event.Close: self.close() return False if event.type() == event.Hide and not is_dialog: self.hide() return False if event.type() == event.Move and not is_dialog: deltaPos = event.pos() - event.oldPos() self.move(self.pos() + deltaPos) return False if self.currentMode() != self.Mode.ToolTip: return False if event.type() == event.Leave: pos = object.mapFromGlobal(QCursor.pos()) if (not object.rect().contains(pos)): self.close() event.accept() return True if event.type() in (event.MouseButtonPress, event.MouseButtonDblClick): self.close() event.accept() return True return False @Slot(QAbstractButton) def handleButtonClick(self, button): """ Handles the button click for this widget. If the Reset button was clicked, then the resetRequested signal will be emitted. All buttons will emit the buttonClicked signal. :param button | <QAbstractButton> """ if ( self.signalsBlocked() ): return if ( button == self._buttonBox.button(QDialogButtonBox.Reset) ): self.resetRequested.emit() self.buttonClicked.emit(button) def isAnimated(self): """ Returns whether or not the popup widget should animate its opacity when it is shown. :return <bool> """ return self._animated def isPossibleAnchor(self, anchor): return bool(anchor & self._possibleAnchors) def isResizable(self): """ Returns if this popup is resizable or not. :return <bool> """ return self._resizable def keyPressEvent( self, event ): """ Looks for the Esc key to close the popup. :param event | <QKeyEvent> """ if ( event.key() == Qt.Key_Escape ): self.reject() event.accept() return elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): if self._autoDefault: self.accept() event.accept() return super(XPopupWidget, self).keyPressEvent(event) def mapAnchorFrom(self, widget, point): """ Returns the anchor point that best fits within the given widget from the inputed global position. :param widget | <QWidget> point | <QPoint> :return <XPopupWidget.Anchor> """ screen_geom = QtGui.QDesktopWidget(self).screenGeometry() # calculate the end rect for each position Anchor = self.Anchor w = self.width() h = self.height() possible_rects = { # top anchors Anchor.TopLeft: QtCore.QRect(point.x(), point.y(), w, h), Anchor.TopCenter: QtCore.QRect(point.x() - w / 2, point.y(), w, h), Anchor.TopRight: QtCore.QRect(point.x() - w, point.y(), w, h), # left anchors Anchor.LeftTop: QtCore.QRect(point.x(), point.y(), w, h), Anchor.LeftCenter: QtCore.QRect(point.x(), point.y() - h / 2, w, h), Anchor.LeftBottom: QtCore.QRect(point.x(), point.y() - h, w, h), # bottom anchors Anchor.BottomLeft: QtCore.QRect(point.x(), point.y() - h, w, h), Anchor.BottomCenter: QtCore.QRect(point.x() - w / 2, point.y() - h, w, h), Anchor.BottomRight: QtCore.QRect(point.x() - w, point.y() - h, w, h), # right anchors Anchor.RightTop: QtCore.QRect(point.x() - self.width(), point.y(), w, h), Anchor.RightCenter: QtCore.QRect(point.x() - self.width(), point.y() - h / 2, w, h), Anchor.RightBottom: QtCore.QRect(point.x() - self.width(), point.y() - h, w ,h) } for anchor in (Anchor.TopCenter, Anchor.BottomCenter, Anchor.LeftCenter, Anchor.RightCenter, Anchor.TopLeft, Anchor.LeftTop, Anchor.BottomLeft, Anchor.LeftBottom, Anchor.TopRight, Anchor.RightTop, Anchor.BottomRight, Anchor.RightBottom): if not self.isPossibleAnchor(anchor): continue rect = possible_rects[anchor] if screen_geom.contains(rect): return anchor return self.anchor() def popup(self, pos=None): """ Pops up this widget at the inputed position. The inputed point should \ be in global space. :param pos | <QPoint> :return <bool> success """ if self._first and self.centralWidget() is not None: self.adjustSize() self._first = False if not self.signalsBlocked(): self.aboutToShow.emit() if not pos: pos = QCursor.pos() if self.currentMode() == XPopupWidget.Mode.Dialog and \ self.isVisible(): return False elif self.currentMode() == XPopupWidget.Mode.Dialog: self.setPopupMode() # auto-calculate the point if self.autoCalculateAnchor(): self.setAnchor(self.mapAnchorFrom(self.parent(), pos)) pad = self.popupPadding() # determine where to move based on the anchor anchor = self.anchor() # MODIFY X POSITION # align x-left if ( anchor & (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.BottomLeft) ): pos.setX(pos.x() - pad) # align x-center elif ( anchor & (XPopupWidget.Anchor.TopCenter | XPopupWidget.Anchor.BottomCenter) ): pos.setX(pos.x() - self.width() / 2) # align x-right elif ( anchor & (XPopupWidget.Anchor.TopRight | XPopupWidget.Anchor.BottomRight) ): pos.setX(pos.x() - self.width() + pad) # align x-padded elif ( anchor & (XPopupWidget.Anchor.RightTop | XPopupWidget.Anchor.RightCenter | XPopupWidget.Anchor.RightBottom) ): pos.setX(pos.x() - self.width()) # MODIFY Y POSITION # align y-top if ( anchor & (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.RightTop) ): pos.setY(pos.y() - pad) # align y-center elif ( anchor & (XPopupWidget.Anchor.LeftCenter | XPopupWidget.Anchor.RightCenter) ): pos.setY(pos.y() - self.height() / 2) # align y-bottom elif ( anchor & (XPopupWidget.Anchor.LeftBottom | XPopupWidget.Anchor.RightBottom) ): pos.setY(pos.y() - self.height() + pad) # align y-padded elif ( anchor & (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter | XPopupWidget.Anchor.BottomRight) ): pos.setY(pos.y() - self.height()) self.adjustMask() self.move(pos) self.update() self.setUpdatesEnabled(True) if self.isAnimated(): anim = QPropertyAnimation(self, 'windowOpacity') anim.setParent(self) anim.setStartValue(0.0) anim.setEndValue(self.windowOpacity()) anim.setDuration(500) anim.finished.connect(anim.deleteLater) self.setWindowOpacity(0.0) else: anim = None self.show() if self.currentMode() != XPopupWidget.Mode.ToolTip: self.activateWindow() widget = self.centralWidget() if widget: self.centralWidget().setFocus() if anim: anim.start() if not self.signalsBlocked(): self.shown.emit() return True def paintEvent(self, event): """ Overloads the paint event to handle painting pointers for the popup \ mode. :param event | <QPaintEvent> """ # use the base technique for the dialog mode if self.currentMode() == XPopupWidget.Mode.Dialog: super(XPopupWidget, self).paintEvent(event) return # setup the coloring options palette = self.palette() with XPainter(self) as painter: pen = QPen(palette.color(palette.Window).darker(130)) pen.setWidthF(1.75) painter.setPen(pen) painter.setRenderHint(painter.Antialiasing) painter.setBrush(palette.color(palette.Window)) painter.drawPath(self.borderPath()) def popupPadding(self): """ Returns the amount of pixels to pad the popup arrow for this widget. :return <int> """ return self._popupPadding def possibleAnchors(self): return self._possibleAnchors def positionLinkedTo(self): """ Returns the widget that this popup is linked to for positional changes. :return [<QWidget>, ..] """ return self._positionLinkedTo @Slot() def reject(self): """ Emits the accepted signal and closes the popup. """ self._result = 0 if not self.signalsBlocked(): self.rejected.emit() if self.autoCloseOnReject(): self.close() def result(self): return self._result def resizeEvent(self, event): """ Resizes this widget and updates the mask. :param event | <QResizeEvent> """ self.setUpdatesEnabled(False) super(XPopupWidget, self).resizeEvent(event) self.adjustMask() self.setUpdatesEnabled(True) x = self.width() - self._sizeGrip.width() y = self.height() - self._sizeGrip.height() self._leftSizeGrip.move(0, y) self._sizeGrip.move(x, y) def scrollArea(self): """ Returns the scroll area widget for this popup. :return <QScrollArea> """ return self._scrollArea def setAnimated(self, state): """ Sets whether or not the popup widget should animate its opacity when it is shown. :param state | <bool> """ self._animated = state self.setAttribute(Qt.WA_TranslucentBackground, state) def setAutoCloseOnAccept( self, state ): """ Sets whether or not the popup handles closing for accepting states. :param state | <bool> """ self._autoCloseOnAccept = state def setAutoCloseOnReject( self, state ): """ Sets whether or not the popup handles closing for rejecting states. :param state | <bool> """ self._autoCloseOnReject = state def setAutoDefault(self, state): """ Sets whether or not the buttons should respond to defaulting options when the user is interacting with it. :param state | <bool> """ self._autoDefault = state for button in self.buttonBox().buttons(): button.setAutoDefault(state) button.setDefault(state) def setAnchor( self, anchor ): """ Sets the anchor position for this popup widget to the inputed point. :param anchor | <XPopupWidget.Anchor> """ self._anchor = anchor self.adjustContentsMargins() def setAutoCalculateAnchor(self, state=True): """ Sets whether or not this widget should auto-calculate the anchor point based on the parent position when the popup is triggered. :param state | <bool> """ self._autoCalculateAnchor = state def setAutoCloseOnFocusOut(self, state): """ Sets whether or not this popup widget should auto-close when the user clicks off the view. :param state | <bool> """ self._autoCloseOnFocusOut = state self.updateModeSettings() def setCentralWidget( self, widget ): """ Sets the central widget that will be used by this popup. :param widget | <QWidget> || None """ self._scrollArea.takeWidget() self._scrollArea.setWidget(widget) self.adjustSize() def setCurrentMode( self, mode ): """ Sets the current mode for this dialog to the inputed mode. :param mode | <XPopupWidget.Mode> """ if ( self._currentMode == mode ): return self._currentMode = mode self.updateModeSettings() @Slot() def setDialogMode(self): """ Sets the current mode value to Dialog. """ self.setCurrentMode(XPopupWidget.Mode.Dialog) @deprecatedmethod('XPopupWidget', 'Direction is no longer used, use setAnchor instead') def setDirection( self, direction ): """ Sets the direction for this widget to the inputed direction. :param direction | <XPopupWidget.Direction> """ if ( direction == XPopupWidget.Direction.North ): self.setAnchor(XPopupWidget.Anchor.TopCenter) elif ( direction == XPopupWidget.Direction.South ): self.setAnchor(XPopupWidget.Anchor.BottomCenter) elif ( direction == XPopupWidget.Direction.East ): self.setAnchor(XPopupWidget.Anchor.LeftCenter) else: self.setAnchor(XPopupWidget.Anchor.RightCenter) def setPalette(self, palette): """ Sets the palette for this widget and the scroll area. :param palette | <QPalette> """ super(XPopupWidget, self).setPalette(palette) self._scrollArea.setPalette(palette) def setPopupMode( self ): """ Sets the current mode value to Popup. """ self.setCurrentMode(XPopupWidget.Mode.Popup) def setPopupPadding( self, padding ): """ Sets the amount to pad the popup area when displaying this widget. :param padding | <int> """ self._popupPadding = padding self.adjustContentsMargins() def setPossibleAnchors(self, anchors): self._possibleAnchors = anchors def setPositionLinkedTo(self, widgets): """ Sets the widget that this popup will be linked to for positional changes. :param widgets | <QWidget> || [<QWidget>, ..] """ if type(widgets) in (list, set, tuple): new_widgets = list(widgets) else: new_widgets = [] widget = widgets while widget: widget.installEventFilter(self) new_widgets.append(widget) widget = widget.parent() self._positionLinkedTo = new_widgets def setResizable( self, state ): self._resizable = state self._sizeGrip.setVisible(state) self._leftSizeGrip.setVisible(state) def setShowButtonBox(self, state): self._buttonBoxVisible = state self.buttonBox().setVisible(state) def setShowTitleBar(self, state): self._titleBarVisible = state self._dialogButton.setVisible(state) self._closeButton.setVisible(state) def setToolTipMode(self): """ Sets the mode for this popup widget to ToolTip """ self.setCurrentMode(XPopupWidget.Mode.ToolTip) def setVisible(self, state): super(XPopupWidget, self).setVisible(state) widget = self.centralWidget() if widget: widget.setVisible(state) def timerEvent( self, event ): """ When the timer finishes, hide the tooltip popup widget. :param event | <QEvent> """ if self.currentMode() == XPopupWidget.Mode.ToolTip: self.killTimer(event.timerId()) event.accept() self.close() else: super(XPopupWidget, self).timerEvent(event) def updateModeSettings(self): mode = self.currentMode() is_visible = self.isVisible() # display as a floating dialog if mode == XPopupWidget.Mode.Dialog: self.setWindowFlags(Qt.Dialog | Qt.Tool) self.setAttribute(Qt.WA_TransparentForMouseEvents, False) self._closeButton.setVisible(False) self._dialogButton.setVisible(False) # display as a user tooltip elif mode == XPopupWidget.Mode.ToolTip: flags = Qt.Popup | Qt.FramelessWindowHint self.setWindowFlags(flags) self.setBackgroundRole(QPalette.Window) self.setAttribute(Qt.WA_TransparentForMouseEvents) self.setShowTitleBar(False) self.setShowButtonBox(False) self.setFocusPolicy(Qt.NoFocus) # hide the scrollbars policy = Qt.ScrollBarAlwaysOff self._scrollArea.setVerticalScrollBarPolicy(policy) self._scrollArea.setHorizontalScrollBarPolicy(policy) # display as a popup widget else: flags = Qt.Popup | Qt.FramelessWindowHint if not self.autoCloseOnFocusOut(): flags |= Qt.Tool self.setWindowFlags(flags) self._closeButton.setVisible(self._titleBarVisible) self._dialogButton.setVisible(self._titleBarVisible) self.setBackgroundRole(QPalette.Window) self.adjustContentsMargins() if ( is_visible ): self.show() @staticmethod @deprecatedmethod('XPopupWidget', 'This method no longer has an effect as we are not '\ 'storing references to the tooltip.') def hideToolTip(key = None): """ Hides any existing tooltip popup widgets. :warning This method is deprecated! """ pass @staticmethod def showToolTip( text, point = None, anchor = None, parent = None, background = None, foreground = None, key = None, seconds = 5 ): """ Displays a popup widget as a tooltip bubble. :param text | <str> point | <QPoint> || None anchor | <XPopupWidget.Mode.Anchor> || None parent | <QWidget> || None background | <QColor> || None foreground | <QColor> || None key | <str> || None seconds | <int> """ if point is None: point = QCursor.pos() if parent is None: parent = QApplication.activeWindow() if anchor is None and parent is None: anchor = XPopupWidget.Anchor.TopCenter # create a new tooltip widget widget = XPopupWidget(parent) widget.setToolTipMode() widget.setResizable(False) # create the tooltip label label = QLabel(text, widget) label.setOpenExternalLinks(True) label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) label.setMargin(3) label.setIndent(3) label.adjustSize() widget.setCentralWidget(label) # update the tip label.adjustSize() widget.adjustSize() palette = widget.palette() if not background: background = palette.color(palette.ToolTipBase) if not foreground: foreground = palette.color(palette.ToolTipText) palette.setColor(palette.Window, QColor(background)) palette.setColor(palette.WindowText, QColor(foreground)) widget.setPalette(palette) widget.centralWidget().setPalette(palette) if anchor is None: widget.setAutoCalculateAnchor(True) else: widget.setAnchor(anchor) widget.setAutoCloseOnFocusOut(True) widget.setAttribute(Qt.WA_DeleteOnClose) widget.popup(point) widget.startTimer(1000 * seconds) return widget
def __init__( self, parent = None ): super(XFindWidget, self).__init__( parent ) # define custom properties self._textEdit = None self._webView = None self._lastCursor = QTextCursor() self._lastText = '' self._closeButton = QToolButton(self) self._closeButton.setIcon(QIcon(resources.find('img/close.png'))) self._closeButton.setAutoRaise(True) self._closeButton.setToolTip('Hide the Find Field.') self._searchEdit = XLineEdit(self) self._searchEdit.setHint('search for...') self._previousButton = QToolButton(self) self._previousButton.setIcon(QIcon(resources.find('img/back.png'))) self._previousButton.setAutoRaise(True) self._previousButton.setToolTip('Find Previous') self._nextButton = QToolButton(self) self._nextButton.setIcon(QIcon(resources.find('img/forward.png'))) self._nextButton.setAutoRaise(True) self._nextButton.setToolTip('Find Next') self._caseSensitiveCheckbox = QCheckBox(self) self._caseSensitiveCheckbox.setText('Case Sensitive') self._wholeWordsCheckbox = QCheckBox(self) self._wholeWordsCheckbox.setText('Whole Words Only') self._regexCheckbox = QCheckBox(self) self._regexCheckbox.setText('Use Regex') self._findAction = QAction(self) self._findAction.setText('Find...') self._findAction.setIcon(QIcon(resources.find('img/search.png'))) self._findAction.setToolTip('Find in Text') self._findAction.setShortcut(QKeySequence('Ctrl+F')) self._findAction.setShortcutContext(Qt.WidgetWithChildrenShortcut) # layout the widgets layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget( self._closeButton ) layout.addWidget( self._searchEdit ) layout.addWidget( self._previousButton ) layout.addWidget( self._nextButton ) layout.addWidget( self._caseSensitiveCheckbox ) layout.addWidget( self._wholeWordsCheckbox ) layout.addWidget( self._regexCheckbox ) self.setLayout(layout) # create connections self._findAction.triggered.connect( self.show ) self._searchEdit.textChanged.connect( self.findNext ) self._closeButton.clicked.connect( self.hide ) self._previousButton.clicked.connect( self.findPrev ) self._nextButton.clicked.connect( self.findNext ) self._caseSensitiveCheckbox.clicked.connect( self.findNext ) self._wholeWordsCheckbox.clicked.connect( self.findNext ) self._searchEdit.returnPressed.connect( self.findNext ) self._regexCheckbox.clicked.connect( self.findNext )
class XToolBar(QToolBar): collapseToggled = Signal(bool) def __init__(self, *args): super(XToolBar, self).__init__(*args) # set custom properties self._collapseButton = None self._collapsable = True self._collapsed = True self._collapsedSize = 14 self._autoCollapsible = False self._precollapseSize = None self._shadowed = False self._colored = False # set standard options self.layout().setSpacing(0) self.layout().setContentsMargins(1, 1, 1, 1) self.setMovable(False) self.clear() self.setMouseTracking(True) self.setOrientation(Qt.Horizontal) self.setCollapsed(False) def autoCollapsible(self): """ Returns whether or not this toolbar is auto-collapsible. When True, it will enter its collapsed state when the user hovers out of the bar. :return <bool> """ return self._autoCollapsible def clear(self): """ Clears out this toolbar from the system. """ # preserve the collapse button super(XToolBar, self).clear() # clears out the toolbar if self.isCollapsable(): self._collapseButton = QToolButton(self) self._collapseButton.setAutoRaise(True) self._collapseButton.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.addWidget(self._collapseButton) self.refreshButton() # create connection self._collapseButton.clicked.connect(self.toggleCollapsed) elif self._collapseButton: self._collapseButton.setParent(None) self._collapseButton.deleteLater() self._collapseButton = None def count(self): """ Returns the number of actions linked with this toolbar. :return <int> """ return len(self.actions()) def collapseButton(self): """ Returns the collapsing button for this toolbar. :return <QToolButton> """ return self._collapseButton def isCollapsable(self): """ Returns whether or not this toolbar is collapsable. :return <bool> """ return self._collapsable def isCollapsed(self): """ Returns whether or not this toolbar is in a collapsed state. :return <bool> """ return self._collapsed and self.isCollapsable() def isColored(self): """ Returns whether or not to colorize the buttons on the toolbar when they are highlighted. :return <bool> """ return self._colored def isShadowed(self): """ Returns whether or not to show this toolbar with shadows. :return <bool> """ return self._shadowed def refreshButton(self): """ Refreshes the button for this toolbar. """ collapsed = self.isCollapsed() btn = self._collapseButton if not btn: return btn.setMaximumSize(MAX_SIZE, MAX_SIZE) # set up a vertical scrollbar if self.orientation() == Qt.Vertical: btn.setMaximumHeight(12) else: btn.setMaximumWidth(12) icon = '' # collapse/expand a vertical toolbar if self.orientation() == Qt.Vertical: if collapsed: self.setFixedWidth(self._collapsedSize) btn.setMaximumHeight(MAX_SIZE) btn.setArrowType(Qt.RightArrow) else: self.setMaximumWidth(MAX_SIZE) self._precollapseSize = None btn.setMaximumHeight(12) btn.setArrowType(Qt.LeftArrow) else: if collapsed: self.setFixedHeight(self._collapsedSize) btn.setMaximumWidth(MAX_SIZE) btn.setArrowType(Qt.DownArrow) else: self.setMaximumHeight(1000) self._precollapseSize = None btn.setMaximumWidth(12) btn.setArrowType(Qt.UpArrow) for index in range(1, self.layout().count()): item = self.layout().itemAt(index) if not item.widget(): continue if collapsed: item.widget().setMaximumSize(0, 0) else: item.widget().setMaximumSize(MAX_SIZE, MAX_SIZE) if not self.isCollapsable(): btn.hide() else: btn.show() def resizeEvent(self, event): super(XToolBar, self).resizeEvent(event) if not self._collapsed: if self.orientation() == Qt.Vertical: self._precollapseSize = self.width() else: self._precollapseSize = self.height() def setAutoCollapsible(self, state): """ Sets whether or not this toolbar is auto-collapsible. :param state | <bool> """ self._autoCollapsible = state def setCollapsed(self, state): """ Sets whether or not this toolbar is in a collapsed state. :return <bool> changed """ if state == self._collapsed: return False self._collapsed = state self.refreshButton() if not self.signalsBlocked(): self.collapseToggled.emit(state) return True def setCollapsable(self, state): """ Sets whether or not this toolbar is collapsable. :param state | <bool> """ if self._collapsable == state: return self._collapsable = state self.clear() def setOrientation(self, orientation): """ Sets the orientation for this toolbar to the inputed value, and \ updates the contents margins and collapse button based on the vaule. :param orientation | <Qt.Orientation> """ super(XToolBar, self).setOrientation(orientation) self.refreshButton() def setShadowed(self, state): """ Sets whether or not this toolbar is shadowed. :param state | <bool> """ self._shadowed = state if state: self._colored = False for child in self.findChildren(XToolButton): child.setShadowed(state) def setColored(self, state): """ Sets whether or not this toolbar is shadowed. :param state | <bool> """ self._colored = state if state: self._shadowed = False for child in self.findChildren(XToolButton): child.setColored(state) def toggleCollapsed(self): """ Toggles the collapsed state for this toolbar. :return <bool> changed """ return self.setCollapsed(not self.isCollapsed()) x_shadowed = Property(bool, isShadowed, setShadowed) x_colored = Property(bool, isColored, setColored)
class XPagesWidget(QWidget): """ """ currentPageChanged = Signal(int) pageSizeChanged = Signal(int) pageCountChanged = Signal(int) def __init__(self, parent=None): super(XPagesWidget, self).__init__(parent) # define custom properties self._currentPage = 1 self._pageCount = 10 self._itemCount = 0 self._pageSize = 50 self._itemsTitle = 'items' self._pagesSpinner = QSpinBox() self._pagesSpinner.setMinimum(1) self._pagesSpinner.setMaximum(10) self._pageSizeCombo = XComboBox(self) self._pageSizeCombo.setHint('all') self._pageSizeCombo.addItems(['', '25', '50', '75', '100']) self._pageSizeCombo.setCurrentIndex(2) self._nextButton = QToolButton(self) self._nextButton.setAutoRaise(True) self._nextButton.setArrowType(Qt.RightArrow) self._nextButton.setFixedWidth(16) self._prevButton = QToolButton(self) self._prevButton.setAutoRaise(True) self._prevButton.setArrowType(Qt.LeftArrow) self._prevButton.setFixedWidth(16) self._prevButton.setEnabled(False) self._pagesLabel = QLabel('of 10 for ', self) self._itemsLabel = QLabel(' items per page', self) # define the interface layout = QHBoxLayout() layout.addWidget(QLabel('Page', self)) layout.addWidget(self._prevButton) layout.addWidget(self._pagesSpinner) layout.addWidget(self._nextButton) layout.addWidget(self._pagesLabel) layout.addWidget(self._pageSizeCombo) layout.addWidget(self._itemsLabel) layout.addStretch(1) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) # create connections self._pageSizeCombo.currentIndexChanged.connect(self.pageSizePicked) self._nextButton.clicked.connect(self.gotoNext) self._prevButton.clicked.connect(self.gotoPrevious) self._pagesSpinner.editingFinished.connect(self.assignCurrentPage) def assignCurrentPage(self): """ Assigns the page for the spinner to be current. """ self.setCurrentPage(self._pagesSpinner.value()) def currentPage(self): """ Reutrns the current page for this widget. :return <int> """ return self._currentPage @Slot() def gotoFirst(self): """ Goes to the first page. :sa setCurrentPage """ self.setCurrentPage(1) @Slot() def gotoLast(self): """ Goes to the last page. :sa setCurrentPage """ self.setCurrentPage(self.pageCount()) @Slot() def gotoNext(self): """ Goes to the next page. :sa setCurrentPage """ next_page = self.currentPage() + 1 if (next_page > self.pageCount()): return self.setCurrentPage(next_page) @Slot() def gotoPrevious(self): """ Goes to the previous page. :sa setCurrentPage """ prev_page = self.currentPage() - 1 if (prev_page == 0): return self.setCurrentPage(prev_page) def itemCount(self): """ Returns the total number of items this widget holds. If no item count is defined, it will not be displayed in the label, otherwise it will show. :return <int> """ return self._itemCount def itemsTitle(self): """ Returns the items title for this instance. :return <str> """ return self._itemsTitle def pageCount(self): """ Returns the number of pages that this widget holds. :return <int> """ return self._pageCount def pageSize(self): """ Returns the number of items that should be visible in a page. :return <int> """ return self._pageSize def pageSizeOptions(self): """ Returns the list of options that will be displayed for this default size options. :return [<str>, ..] """ return map(str, self._pageSizeCombo.items()) def pageSizePicked(self, pageSize): """ Updates when the user picks a page size. :param pageSize | <str> """ try: pageSize = int(self._pageSizeCombo.currentText()) except ValueError: pageSize = 0 self.setPageSize(pageSize) self.pageSizeChanged.emit(pageSize) def refreshLabels(self): """ Refreshes the labels to display the proper title and count information. """ itemCount = self.itemCount() title = self.itemsTitle() if (not itemCount): self._itemsLabel.setText(' %s per page' % title) else: msg = ' %s per page, %i %s total' % (title, itemCount, title) self._itemsLabel.setText(msg) @Slot(int) def setCurrentPage(self, pageno): """ Sets the current page for this widget to the inputed page. :param pageno | <int> """ if (pageno == self._currentPage): return if (pageno <= 0): pageno = 1 self._currentPage = pageno self._prevButton.setEnabled(pageno > 1) self._nextButton.setEnabled(pageno < self.pageCount()) self._pagesSpinner.blockSignals(True) self._pagesSpinner.setValue(pageno) self._pagesSpinner.blockSignals(False) if (not self.signalsBlocked()): self.currentPageChanged.emit(pageno) @Slot(int) def setItemCount(self, itemCount): """ Sets the item count for this page to the inputed value. :param itemCount | <int> """ self._itemCount = itemCount self.refreshLabels() @Slot(str) def setItemsTitle(self, title): """ Sets the title that will be displayed when the items labels are rendered :param title | <str> """ self._itemsTitle = nativestring(title) self.refreshLabels() @Slot(int) def setPageCount(self, pageCount): """ Sets the number of pages that this widget holds. :param pageCount | <int> """ if (pageCount == self._pageCount): return pageCount = max(1, pageCount) self._pageCount = pageCount self._pagesSpinner.setMaximum(pageCount) self._pagesLabel.setText('of %i for ' % pageCount) if (pageCount and self.currentPage() <= 0): self.setCurrentPage(1) elif (pageCount < self.currentPage()): self.setCurrentPage(pageCount) if (not self.signalsBlocked()): self.pageCountChanged.emit(pageCount) self._prevButton.setEnabled(self.currentPage() > 1) self._nextButton.setEnabled(self.currentPage() < pageCount) @Slot(int) def setPageSize(self, pageSize): """ Sets the number of items that should be visible in a page. Setting the value to 0 will use all sizes :return <int> """ if self._pageSize == pageSize: return self._pageSize = pageSize # update the display size ssize = nativestring(pageSize) if (ssize == '0'): ssize = '' self._pageSizeCombo.blockSignals(True) index = self._pageSizeCombo.findText(ssize) self._pageSizeCombo.setCurrentIndex(index) self._pageSizeCombo.blockSignals(False) def setPageSizeOptions(self, options): """ Sets the options that will be displayed for this default size. :param options | [<str>,. ..] """ self._pageSizeCombo.blockSignals(True) self._pageSizeCombo.addItems(options) ssize = nativestring(self.pageSize()) if (ssize == '0'): ssize = '' index = self._pageSizeCombo.findText() self._pageSizeCombo.setCurrentIndex(index) self._pageSizeCombo.blockSignals(False) x_itemsTitle = Property(str, itemsTitle, setItemsTitle) x_pageCount = Property(int, pageCount, setPageCount) x_pageSize = Property(int, pageSize, setPageSize) x_pageSizeOptions = Property(list, pageSizeOptions, setPageSizeOptions)
class XSplitterHandle(QSplitterHandle): CollapseDirection = enum('After', 'Before') def __init__( self, orientation, parent ): super(XSplitterHandle, self).__init__( orientation, parent ) # create a layout for the different buttons self._collapsed = False self._storedSizes = None self._collapseBefore = QToolButton(self) self._resizeGrip = QLabel(self) self._collapseAfter = QToolButton(self) self._collapseBefore.setAutoRaise(True) self._collapseAfter.setAutoRaise(True) self._collapseBefore.setCursor(Qt.ArrowCursor) self._collapseAfter.setCursor(Qt.ArrowCursor) # define the layout layout = QBoxLayout(QBoxLayout.LeftToRight, self) layout.setContentsMargins(0, 0, 0, 0) layout.addStretch(1) layout.addWidget(self._collapseBefore) layout.addWidget(self._resizeGrip) layout.addWidget(self._collapseAfter) layout.addStretch(1) self.setLayout(layout) # set the orientation to start with self.setOrientation(orientation) # create connections self._collapseAfter.clicked.connect( self.toggleCollapseAfter ) self._collapseBefore.clicked.connect( self.toggleCollapseBefore ) def collapse( self, direction ): """ Collapses this splitter handle before or after other widgets based on \ the inputed CollapseDirection. :param direction | <XSplitterHandle.CollapseDirection> :return <bool> | success """ if ( self.isCollapsed() ): return False splitter = self.parent() if ( not splitter ): return False sizes = splitter.sizes() handles = [splitter.handle(i) for i in range(len(sizes))] index = handles.index(self) self.markCollapsed(direction, sizes) # determine the sizes to use based on the direction if ( direction == XSplitterHandle.CollapseDirection.Before ): sizes = [0 for i in range(i)] + sizes[i+1:] else: sizes = sizes[:i] + [0 for i in range(i, len(sizes))] splitter.setSizes(sizes) return True def collapseAfter( self, handle ): """ Collapses the splitter after the inputed handle. :param handle | <XSplitterHandle> """ self.setUpdatesEnabled(False) # collapse all items after the current handle if ( handle.isCollapsed() ): self.setSizes(handle.restoreSizes()) found = False sizes = self.sizes() handle.storeSizes(sizes) for c in range(self.count()): if ( self.handle(c) == handle ): found = True if ( found ): sizes[c] = 0 self.setSizes(sizes) self.update() self.setUpdatesEnabled(True) def collapseBefore( self, handle ): """ Collapses the splitter before the inputed handle. :param handle | <XSplitterHandle> """ self.setUpdatesEnabled(False) # collapse all items after the current handle if ( handle.isCollapsed() ): self.setSizes(handle.restoreSizes()) # collapse all items before the current handle found = False sizes = self.sizes() handle.storeSizes(sizes) for c in range(self.count()): if ( self.handle(c) == handle ): break sizes[c] = 0 self.setSizes(sizes) self.setUpdatesEnabled(True) def isCollapsed( self ): """ Returns whether or not this widget is collapsed. :return <bool> """ return self._collapsed def markCollapsed( self, direction, sizes ): """ Updates the interface to reflect that the splitter is collapsed. :param direction | <XSplitterHandle.CollapseDirection> sizes | [<int>, ..] """ self._collapsed = True self._storedSizes = sizes[:] if ( direction == XSplitterHandle.CollapseDirection.Before ): if ( self.orientation() == Qt.Horizontal ): self._collapseAfter.setArrowType( Qt.RightArrow ) self._collapseBefore.setArrowType( Qt.RightArrow ) else: self._collapseAfter.setArrowType( Qt.DownArrow ) self._collapseBefore.setArrowType( Qt.DownArrow ) else: if ( self.orientation() == Qt.Horizontal ): self._collapseAfter.setArrowType( Qt.LeftArrow ) self._collapseBefore.setArrowType( Qt.LeftArrow ) else: self._collapseAfter.setArrowType( Qt.UpArrow ) self._collapseAfter.setArrowType( Qt.UpArrow ) def paintEvent( self, event ): """ Overloads the paint event to handle drawing the splitter lines. :param event | <QPaintEvent> """ lines = [] count = 20 # calculate the lines if ( self.orientation() == Qt.Vertical ): x = self._resizeGrip.pos().x() h = self.height() spacing = int(float(self._resizeGrip.width()) / count) for i in range(count): lines.append(QLine(x, 0, x, h)) x += spacing else: y = self._resizeGrip.pos().y() w = self.width() spacing = int(float(self._resizeGrip.height()) / count) for i in range(count): lines.append(QLine(0, y, w, y)) y += spacing # draw the lines with XPainter(self) as painter: pal = self.palette() painter.setPen(pal.color(pal.Window).darker(120)) painter.drawLines(lines) def setOrientation( self, orientation ): """ Sets the orientation for this handle and updates the widgets linked \ with it. :param orientation | <Qt.Orientation> """ super(XSplitterHandle, self).setOrientation(orientation) if ( orientation == Qt.Vertical ): self.layout().setDirection( QBoxLayout.LeftToRight ) # update the widgets self._collapseBefore.setFixedSize(30, 10) self._collapseAfter.setFixedSize(30, 10) self._resizeGrip.setFixedSize(60, 10) self._collapseBefore.setArrowType(Qt.UpArrow) self._collapseAfter.setArrowType(Qt.DownArrow) elif ( orientation == Qt.Horizontal ): self.layout().setDirection( QBoxLayout.TopToBottom ) # update the widgets self._collapseBefore.setFixedSize(10, 30) self._collapseAfter.setFixedSize(10, 30) self._resizeGrip.setFixedSize(10, 60) self._collapseBefore.setArrowType(Qt.LeftArrow) self._collapseAfter.setArrowType(Qt.RightArrow) def uncollapse( self ): """ Uncollapses the splitter this handle is associated with by restoring \ its sizes from before the collapse occurred. :return <bool> | changed """ if ( not self.isCollapsed() ): return False self.parent().setSizes(self._storedSizes) self.unmarkCollapsed() return True def unmarkCollapsed( self ): """ Unmarks this splitter as being in a collapsed state, clearing any \ collapsed information. """ if ( not self.isCollapsed() ): return self._collapsed = False self._storedSizes = None if ( self.orientation() == Qt.Vertical ): self._collapseBefore.setArrowType( Qt.UpArrow ) self._collapseAfter.setArrowType( Qt.DownArrow ) else: self._collapseBefore.setArrowType( Qt.LeftArrow ) self._collapseAfter.setArrowType( Qt.RightArrow ) def toggleCollapseAfter( self ): """ Collapses the splitter after this handle. """ if ( self.isCollapsed() ): self.uncollapse() else: self.collapse( XSplitterHandle.CollapseDirection.After ) def toggleCollapseBefore( self ): """ Collapses the splitter before this handle. """ if ( self.isCollapsed() ): self.uncollapse() else: self.collapse( XSplitterHandle.CollapseDirection.Before )
class XZoomSlider(QWidget): zoomAmountChanged = Signal(int) def __init__(self, parent=None): super(XZoomSlider, self).__init__(parent) # define the interface in_icon = projexui.resources.find('img/zoom_in.png') out_icon = projexui.resources.find('img/zoom_out.png') self._zoomInButton = QToolButton(self) self._zoomInButton.setAutoRaise(True) self._zoomInButton.setToolTip('Zoom In') self._zoomInButton.setIcon(QIcon(in_icon)) self._zoomOutButton = QToolButton(self) self._zoomOutButton.setAutoRaise(True) self._zoomOutButton.setToolTip('Zoom Out') self._zoomOutButton.setIcon(QIcon(out_icon)) self._zoomSlider = QSlider(Qt.Horizontal, self) self._zoomSlider.setRange(10, 100) self._zoomSlider.setValue(100) # define the layout layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._zoomOutButton) layout.addWidget(self._zoomSlider) layout.addWidget(self._zoomInButton) self.setLayout(layout) # create connections self._zoomSlider.valueChanged.connect(self.emitZoomAmountChanged) self._zoomInButton.clicked.connect(self.zoomIn) self._zoomOutButton.clicked.connect(self.zoomOut) def emitZoomAmountChanged(self): """ Emits the current zoom amount, provided the signals are not being blocked. """ if not self.signalsBlocked(): self.zoomAmountChanged.emit(self.zoomAmount()) def maximum(self): """ Returns the maximum zoom level for this widget. :return <int> """ return self._zoomSlider.maximum() def minimum(self): """ Returns the minimum zoom level for this widget. :return <int> """ return self._zoomSlider.minimum() @Slot(int) def setMaximum(self, maximum): """ Sets the maximum zoom level for this widget. :param maximum | <int> """ self._zoomSlider.setMaximum(maximum) @Slot(int) def setMinimum(self, minimum): """ Sets the minimum zoom level for this widget. :param minimum | <int> """ self._zoomSlider.setMinimum(minimum) def setZoomStep(self, amount): """ Sets how much a single step for the zoom in/out will be. :param amount | <int> """ self._zoomSlider.setPageStep(amount) @Slot(int) def setZoomAmount(self, amount): """ Sets the zoom amount for this widget to the inputed amount. :param amount | <int> """ self._zoomSlider.setValue(amount) def zoomAmount(self): """ Returns the current zoom amount for this widget. :return <int> """ return self._zoomSlider.value() @Slot() def zoomIn(self): """ Zooms in by a single page step. """ self._zoomSlider.triggerAction(QSlider.SliderPageStepAdd) def zoomInButton(self): """ Returns the zoom in button from the left side of this widget. :return <QToolButton> """ return self._zoomInButton @Slot() def zoomOut(self): """ Zooms out by a single page step. """ self._zoomSlider.triggerAction(QSlider.SliderPageStepSub) def zoomOutButton(self): """ Returns the zoom out button from the right side of this widget. :return <QToolButton> """ return self._zoomOutButton def zoomSlider(self): """ Returns the slider widget of this zoom slider. :return <QSlider> """ return self._zoomSlider def zoomStep(self): """ Returns the amount for a single step when the user clicks the zoom in/ out amount. :return <int> """ return self._zoomSlider.pageStep() x_maximum = Property(int, maximum, setMaximum) x_minimum = Property(int, minimum, setMinimum) z_zoomStep = Property(int, zoomStep, setZoomStep)
class XSplitterHandle(QSplitterHandle): CollapseDirection = enum('After', 'Before') def __init__(self, orientation, parent): super(XSplitterHandle, self).__init__(orientation, parent) # create a layout for the different buttons self._collapsed = False self._storedSizes = None self._collapseBefore = QToolButton(self) self._resizeGrip = QLabel(self) self._collapseAfter = QToolButton(self) self._collapseBefore.setAutoRaise(True) self._collapseAfter.setAutoRaise(True) self._collapseBefore.setCursor(Qt.ArrowCursor) self._collapseAfter.setCursor(Qt.ArrowCursor) # define the layout layout = QBoxLayout(QBoxLayout.LeftToRight, self) layout.setContentsMargins(0, 0, 0, 0) layout.addStretch(1) layout.addWidget(self._collapseBefore) layout.addWidget(self._resizeGrip) layout.addWidget(self._collapseAfter) layout.addStretch(1) self.setLayout(layout) # set the orientation to start with self.setOrientation(orientation) # create connections self._collapseAfter.clicked.connect(self.toggleCollapseAfter) self._collapseBefore.clicked.connect(self.toggleCollapseBefore) def collapse(self, direction): """ Collapses this splitter handle before or after other widgets based on \ the inputed CollapseDirection. :param direction | <XSplitterHandle.CollapseDirection> :return <bool> | success """ if (self.isCollapsed()): return False splitter = self.parent() if (not splitter): return False sizes = splitter.sizes() handles = [splitter.handle(i) for i in range(len(sizes))] index = handles.index(self) self.markCollapsed(direction, sizes) # determine the sizes to use based on the direction if (direction == XSplitterHandle.CollapseDirection.Before): sizes = [0 for i in range(i)] + sizes[i + 1:] else: sizes = sizes[:i] + [0 for i in range(i, len(sizes))] splitter.setSizes(sizes) return True def collapseAfter(self, handle): """ Collapses the splitter after the inputed handle. :param handle | <XSplitterHandle> """ self.setUpdatesEnabled(False) # collapse all items after the current handle if (handle.isCollapsed()): self.setSizes(handle.restoreSizes()) found = False sizes = self.sizes() handle.storeSizes(sizes) for c in range(self.count()): if (self.handle(c) == handle): found = True if (found): sizes[c] = 0 self.setSizes(sizes) self.update() self.setUpdatesEnabled(True) def collapseBefore(self, handle): """ Collapses the splitter before the inputed handle. :param handle | <XSplitterHandle> """ self.setUpdatesEnabled(False) # collapse all items after the current handle if (handle.isCollapsed()): self.setSizes(handle.restoreSizes()) # collapse all items before the current handle found = False sizes = self.sizes() handle.storeSizes(sizes) for c in range(self.count()): if (self.handle(c) == handle): break sizes[c] = 0 self.setSizes(sizes) self.setUpdatesEnabled(True) def isCollapsed(self): """ Returns whether or not this widget is collapsed. :return <bool> """ return self._collapsed def markCollapsed(self, direction, sizes): """ Updates the interface to reflect that the splitter is collapsed. :param direction | <XSplitterHandle.CollapseDirection> sizes | [<int>, ..] """ self._collapsed = True self._storedSizes = sizes[:] if (direction == XSplitterHandle.CollapseDirection.Before): if (self.orientation() == Qt.Horizontal): self._collapseAfter.setArrowType(Qt.RightArrow) self._collapseBefore.setArrowType(Qt.RightArrow) else: self._collapseAfter.setArrowType(Qt.DownArrow) self._collapseBefore.setArrowType(Qt.DownArrow) else: if (self.orientation() == Qt.Horizontal): self._collapseAfter.setArrowType(Qt.LeftArrow) self._collapseBefore.setArrowType(Qt.LeftArrow) else: self._collapseAfter.setArrowType(Qt.UpArrow) self._collapseAfter.setArrowType(Qt.UpArrow) def paintEvent(self, event): """ Overloads the paint event to handle drawing the splitter lines. :param event | <QPaintEvent> """ lines = [] count = 20 # calculate the lines if (self.orientation() == Qt.Vertical): x = self._resizeGrip.pos().x() h = self.height() spacing = int(float(self._resizeGrip.width()) / count) for i in range(count): lines.append(QLine(x, 0, x, h)) x += spacing else: y = self._resizeGrip.pos().y() w = self.width() spacing = int(float(self._resizeGrip.height()) / count) for i in range(count): lines.append(QLine(0, y, w, y)) y += spacing # draw the lines with XPainter(self) as painter: pal = self.palette() painter.setPen(pal.color(pal.Window).darker(120)) painter.drawLines(lines) def setOrientation(self, orientation): """ Sets the orientation for this handle and updates the widgets linked \ with it. :param orientation | <Qt.Orientation> """ super(XSplitterHandle, self).setOrientation(orientation) if (orientation == Qt.Vertical): self.layout().setDirection(QBoxLayout.LeftToRight) # update the widgets self._collapseBefore.setFixedSize(30, 10) self._collapseAfter.setFixedSize(30, 10) self._resizeGrip.setFixedSize(60, 10) self._collapseBefore.setArrowType(Qt.UpArrow) self._collapseAfter.setArrowType(Qt.DownArrow) elif (orientation == Qt.Horizontal): self.layout().setDirection(QBoxLayout.TopToBottom) # update the widgets self._collapseBefore.setFixedSize(10, 30) self._collapseAfter.setFixedSize(10, 30) self._resizeGrip.setFixedSize(10, 60) self._collapseBefore.setArrowType(Qt.LeftArrow) self._collapseAfter.setArrowType(Qt.RightArrow) def uncollapse(self): """ Uncollapses the splitter this handle is associated with by restoring \ its sizes from before the collapse occurred. :return <bool> | changed """ if (not self.isCollapsed()): return False self.parent().setSizes(self._storedSizes) self.unmarkCollapsed() return True def unmarkCollapsed(self): """ Unmarks this splitter as being in a collapsed state, clearing any \ collapsed information. """ if (not self.isCollapsed()): return self._collapsed = False self._storedSizes = None if (self.orientation() == Qt.Vertical): self._collapseBefore.setArrowType(Qt.UpArrow) self._collapseAfter.setArrowType(Qt.DownArrow) else: self._collapseBefore.setArrowType(Qt.LeftArrow) self._collapseAfter.setArrowType(Qt.RightArrow) def toggleCollapseAfter(self): """ Collapses the splitter after this handle. """ if (self.isCollapsed()): self.uncollapse() else: self.collapse(XSplitterHandle.CollapseDirection.After) def toggleCollapseBefore(self): """ Collapses the splitter before this handle. """ if (self.isCollapsed()): self.uncollapse() else: self.collapse(XSplitterHandle.CollapseDirection.Before)
def rebuild(self): """ Rebuilds the parts widget with the latest text. """ navitem = self.currentItem() if (navitem): navitem.initialize() self.setUpdatesEnabled(False) self.scrollWidget().show() self._originalText = '' partsw = self.partsWidget() for button in self._buttonGroup.buttons(): self._buttonGroup.removeButton(button) button.close() button.setParent(None) button.deleteLater() # create the root button layout = partsw.layout() parts = self.parts() button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant('')) button.setProperty('is_completer', wrapVariant(True)) last_button = button self._buttonGroup.addButton(button) layout.insertWidget(0, button) # check to see if we have a navigation model setup if (self._navigationModel): last_item = self._navigationModel.itemByPath(self.text()) show_last = last_item and last_item.rowCount() > 0 else: show_last = False # load the navigation system count = len(parts) for i, part in enumerate(parts): path = self.separator().join(parts[:i + 1]) button = QToolButton(partsw) button.setAutoRaise(True) button.setText(part) if (self._navigationModel): item = self._navigationModel.itemByPath(path) if (item): button.setIcon(item.icon()) button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(False)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 1, button) # determine if we should show the final button if (show_last or i < (count - 1)): button = QToolButton(partsw) button.setAutoRaise(True) button.setMaximumWidth(12) button.setArrowType(Qt.RightArrow) button.setProperty('path', wrapVariant(path)) button.setProperty('is_completer', wrapVariant(True)) self._buttonGroup.addButton(button) layout.insertWidget((i * 2) + 2, button) last_button = button if (self.scrollWidget().width() < partsw.width()): self.scrollParts(partsw.width() - self.scrollWidget().width()) self.setUpdatesEnabled(True) self.navigationChanged.emit()
class XPopupWidget(QWidget): """ """ Direction = enum('North', 'South', 'East', 'West') Mode = enum('Popup', 'Dialog', 'ToolTip') Anchor = enum('TopLeft', 'TopCenter', 'TopRight', 'LeftTop', 'LeftCenter', 'LeftBottom', 'RightTop', 'RightCenter', 'RightBottom', 'BottomLeft', 'BottomCenter', 'BottomRight') aboutToShow = Signal() accepted = Signal() closed = Signal() rejected = Signal() resetRequested = Signal() shown = Signal() buttonClicked = Signal(QAbstractButton) def __init__(self, parent=None, buttons=None): super(XPopupWidget, self).__init__(parent) # define custom properties self._anchor = XPopupWidget.Anchor.TopCenter self._autoCalculateAnchor = False self._autoCloseOnAccept = True self._autoCloseOnReject = True self._autoCloseOnFocusOut = False self._autoDefault = True self._first = True self._animated = False self._currentMode = None self._positionLinkedTo = [] self._possibleAnchors = XPopupWidget.Anchor.all() # define controls self._result = 0 self._resizable = True self._popupPadding = 10 self._titleBarVisible = True self._buttonBoxVisible = True self._dialogButton = QToolButton(self) self._closeButton = QToolButton(self) self._scrollArea = QScrollArea(self) self._sizeGrip = QSizeGrip(self) self._sizeGrip.setFixedWidth(12) self._sizeGrip.setFixedHeight(12) self._leftSizeGrip = QSizeGrip(self) self._leftSizeGrip.setFixedWidth(12) self._leftSizeGrip.setFixedHeight(12) if buttons is None: buttons = QDialogButtonBox.NoButton self._buttonBox = QDialogButtonBox(buttons, Qt.Horizontal, self) self._buttonBox.setContentsMargins(3, 0, 3, 9) self._scrollArea.setWidgetResizable(True) self._scrollArea.setFrameShape(QScrollArea.NoFrame) self._scrollArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) palette = self.palette() self._scrollArea.setPalette(palette) self._dialogButton.setToolTip('Popout to Dialog') self._closeButton.setToolTip('Close Popup') for btn in (self._dialogButton, self._closeButton): btn.setAutoRaise(True) btn.setIconSize(QSize(14, 14)) btn.setMaximumSize(16, 16) # setup the icons icon = QIcon(projexui.resources.find('img/dialog.png')) self._dialogButton.setIcon(icon) icon = QIcon(projexui.resources.find('img/close.png')) self._closeButton.setIcon(icon) # define the ui hlayout = QHBoxLayout() hlayout.setSpacing(0) hlayout.addStretch(1) hlayout.addWidget(self._dialogButton) hlayout.addWidget(self._closeButton) hlayout.setContentsMargins(0, 0, 0, 0) hlayout2 = QHBoxLayout() hlayout2.addWidget(self._buttonBox) hlayout2.setContentsMargins(0, 0, 3, 0) vlayout = QVBoxLayout() vlayout.addLayout(hlayout) vlayout.addWidget(self._scrollArea) vlayout.addLayout(hlayout2) vlayout.setContentsMargins(3, 2, 3, 2) vlayout.setSpacing(0) self.setLayout(vlayout) self.setPositionLinkedTo(parent) # set default properties self.setAutoFillBackground(True) self.setBackgroundRole(QPalette.Window) self.setWindowTitle('Popup') self.setFocusPolicy(Qt.StrongFocus) self.setCurrentMode(XPopupWidget.Mode.Popup) # create connections self._dialogButton.clicked.connect(self.setDialogMode) self._closeButton.clicked.connect(self.reject) self._buttonBox.accepted.connect(self.accept) self._buttonBox.rejected.connect(self.reject) self._buttonBox.clicked.connect(self.handleButtonClick) def addButton(self, button, role=QDialogButtonBox.ActionRole): """ Adds a custom button to the button box for this popup widget. :param button | <QAbstractButton> || <str> :return <button> || None (based on if a button or string was given) """ return self._buttonBox.addButton(button, role) def adjustContentsMargins(self): """ Adjusts the contents for this widget based on the anchor and \ mode. """ anchor = self.anchor() mode = self.currentMode() # margins for a dialog if (mode == XPopupWidget.Mode.Dialog): self.setContentsMargins(0, 0, 0, 0) # margins for a top anchor point elif (anchor & (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.TopCenter | XPopupWidget.Anchor.TopRight)): self.setContentsMargins(0, self.popupPadding() + 5, 0, 0) # margins for a bottom anchor point elif ( anchor & (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter | XPopupWidget.Anchor.BottomRight)): self.setContentsMargins(0, 0, 0, self.popupPadding()) # margins for a left anchor point elif (anchor & (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.LeftCenter | XPopupWidget.Anchor.LeftBottom)): self.setContentsMargins(self.popupPadding(), 0, 0, 0) # margins for a right anchor point else: self.setContentsMargins(0, 0, self.popupPadding(), 0) self.adjustMask() def adjustMask(self): """ Updates the alpha mask for this popup widget. """ if self.currentMode() == XPopupWidget.Mode.Dialog: self.clearMask() return path = self.borderPath() bitmap = QBitmap(self.width(), self.height()) bitmap.fill(QColor('white')) with XPainter(bitmap) as painter: painter.setRenderHint(XPainter.Antialiasing) pen = QPen(QColor('black')) pen.setWidthF(0.75) painter.setPen(pen) painter.setBrush(QColor('black')) painter.drawPath(path) self.setMask(bitmap) def adjustSize(self): """ Adjusts the size of this popup to best fit the new widget size. """ widget = self.centralWidget() if widget is None: super(XPopupWidget, self).adjustSize() return widget.adjustSize() hint = widget.minimumSizeHint() size = widget.minimumSize() width = max(size.width(), hint.width()) height = max(size.height(), hint.height()) width += 20 height += 20 if self._buttonBoxVisible: height += self.buttonBox().height() + 10 if self._titleBarVisible: height += max(self._dialogButton.height(), self._closeButton.height()) + 10 curr_w = self.width() curr_h = self.height() # determine if we need to move based on our anchor anchor = self.anchor() if anchor & (self.Anchor.LeftBottom | self.Anchor.RightBottom | \ self.Anchor.BottomLeft | self.Anchor.BottomCenter | \ self.Anchor.BottomRight): delta_y = height - curr_h elif anchor & (self.Anchor.LeftCenter | self.Anchor.RightCenter): delta_y = (height - curr_h) / 2 else: delta_y = 0 if anchor & (self.Anchor.RightTop | self.Anchor.RightCenter | \ self.Anchor.RightTop | self.Anchor.TopRight): delta_x = width - curr_w elif anchor & (self.Anchor.TopCenter | self.Anchor.BottomCenter): delta_x = (width - curr_w) / 2 else: delta_x = 0 self.setMinimumSize(width, height) self.resize(width, height) pos = self.pos() pos.setX(pos.x() - delta_x) pos.setY(pos.y() - delta_y) self.move(pos) @Slot() def accept(self): """ Emits the accepted signal and closes the popup. """ self._result = 1 if not self.signalsBlocked(): self.accepted.emit() if self.autoCloseOnAccept(): self.close() def anchor(self): """ Returns the anchor point for this popup widget. :return <XPopupWidget.Anchor> """ return self._anchor def autoCalculateAnchor(self): """ Returns whether or not this popup should calculate the anchor point on popup based on the parent widget and the popup point. :return <bool> """ return self._autoCalculateAnchor def autoCloseOnAccept(self): """ Returns whether or not this popup widget manages its own close on accept behavior. :return <bool> """ return self._autoCloseOnAccept def autoCloseOnReject(self): """ Returns whether or not this popup widget manages its own close on reject behavior. :return <bool> """ return self._autoCloseOnReject def autoCloseOnFocusOut(self): """ Returns whether or not this popup widget should auto-close when the user clicks off the view. :return <bool> """ return self._autoCloseOnFocusOut def autoDefault(self): """ Returns whether or not clicking enter should default to the accept key. :return <bool> """ return self._autoDefault def borderPath(self): """ Returns the border path that will be drawn for this widget. :return <QPainterPath> """ path = QPainterPath() x = 1 y = 1 w = self.width() - 2 h = self.height() - 2 pad = self.popupPadding() anchor = self.anchor() # create a path for a top-center based popup if anchor == XPopupWidget.Anchor.TopCenter: path.moveTo(x, y + pad) path.lineTo(x + ((w / 2) - pad), y + pad) path.lineTo(x + (w / 2), y) path.lineTo(x + ((w / 2) + pad), y + pad) path.lineTo(x + w, y + pad) path.lineTo(x + w, y + h) path.lineTo(x, y + h) path.lineTo(x, y + pad) # create a path for a top-left based popup elif anchor == XPopupWidget.Anchor.TopLeft: path.moveTo(x, y + pad) path.lineTo(x + pad, y) path.lineTo(x + 2 * pad, y + pad) path.lineTo(x + w, y + pad) path.lineTo(x + w, y + h) path.lineTo(x, y + h) path.lineTo(x, y + pad) # create a path for a top-right based popup elif anchor == XPopupWidget.Anchor.TopRight: path.moveTo(x, y + pad) path.lineTo(x + w - 2 * pad, y + pad) path.lineTo(x + w - pad, y) path.lineTo(x + w, y + pad) path.lineTo(x + w, y + h) path.lineTo(x, y + h) path.lineTo(x, y + pad) # create a path for a bottom-left based popup elif anchor == XPopupWidget.Anchor.BottomLeft: path.moveTo(x, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h - pad) path.lineTo(x + 2 * pad, y + h - pad) path.lineTo(x + pad, y + h) path.lineTo(x, y + h - pad) path.lineTo(x, y) # create a path for a south based popup elif anchor == XPopupWidget.Anchor.BottomCenter: path.moveTo(x, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h - pad) path.lineTo(x + ((w / 2) + pad), y + h - pad) path.lineTo(x + (w / 2), y + h) path.lineTo(x + ((w / 2) - pad), y + h - pad) path.lineTo(x, y + h - pad) path.lineTo(x, y) # create a path for a bottom-right based popup elif anchor == XPopupWidget.Anchor.BottomRight: path.moveTo(x, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h - pad) path.lineTo(x + w - pad, y + h) path.lineTo(x + w - 2 * pad, y + h - pad) path.lineTo(x, y + h - pad) path.lineTo(x, y) # create a path for a right-top based popup elif anchor == XPopupWidget.Anchor.RightTop: path.moveTo(x, y) path.lineTo(x + w - pad, y) path.lineTo(x + w, y + pad) path.lineTo(x + w - pad, y + 2 * pad) path.lineTo(x + w - pad, y + h) path.lineTo(x, y + h) path.lineTo(x, y) # create a path for a right-center based popup elif anchor == XPopupWidget.Anchor.RightCenter: path.moveTo(x, y) path.lineTo(x + w - pad, y) path.lineTo(x + w - pad, y + ((h / 2) - pad)) path.lineTo(x + w, y + (h / 2)) path.lineTo(x + w - pad, y + ((h / 2) + pad)) path.lineTo(x + w - pad, y + h) path.lineTo(x, y + h) path.lineTo(x, y) # create a path for a right-bottom based popup elif anchor == XPopupWidget.Anchor.RightBottom: path.moveTo(x, y) path.lineTo(x + w - pad, y) path.lineTo(x + w - pad, y + h - 2 * pad) path.lineTo(x + w, y + h - pad) path.lineTo(x + w - pad, y + h) path.lineTo(x, y + h) path.lineTo(x, y) # create a path for a left-top based popup elif anchor == XPopupWidget.Anchor.LeftTop: path.moveTo(x + pad, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h) path.lineTo(x + pad, y + h) path.lineTo(x + pad, y + 2 * pad) path.lineTo(x, y + pad) path.lineTo(x + pad, y) # create a path for an left-center based popup elif anchor == XPopupWidget.Anchor.LeftCenter: path.moveTo(x + pad, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h) path.lineTo(x + pad, y + h) path.lineTo(x + pad, y + ((h / 2) + pad)) path.lineTo(x, y + (h / 2)) path.lineTo(x + pad, y + ((h / 2) - pad)) path.lineTo(x + pad, y) # create a path for a left-bottom based popup elif anchor == XPopupWidget.Anchor.LeftBottom: path.moveTo(x + pad, y) path.lineTo(x + w, y) path.lineTo(x + w, y + h) path.lineTo(x + pad, y + h) path.lineTo(x, y + h - pad) path.lineTo(x + pad, y + h - 2 * pad) path.lineTo(x + pad, y) return path def buttonBox(self): """ Returns the button box that is used to control this popup widget. :return <QDialogButtonBox> """ return self._buttonBox def centralWidget(self): """ Returns the central widget that is being used by this popup. :return <QWidget> """ return self._scrollArea.widget() def close(self): """ Closes the popup widget and central widget. """ widget = self.centralWidget() if widget and not widget.close(): return super(XPopupWidget, self).close() def closeEvent(self, event): widget = self.centralWidget() if widget and not widget.close() and \ self.currentMode() != XPopupWidget.Mode.ToolTip: event.ignore() else: super(XPopupWidget, self).closeEvent(event) self.closed.emit() def currentMode(self): """ Returns the current mode for this widget. :return <XPopupWidget.Mode> """ return self._currentMode @deprecatedmethod('XPopupWidget', 'Direction is no longer used, use anchor instead') def direction(self): """ Returns the current direction parameter for this widget. :return <XPopupWidget.Direction> """ anchor = self.anchor() if (anchor & (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.TopCenter | XPopupWidget.Anchor.TopRight)): return XPopupWidget.Direction.North elif ( anchor & (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter | XPopupWidget.Anchor.BottomRight)): return XPopupWidget.Direction.South elif (anchor & (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.LeftCenter | XPopupWidget.Anchor.LeftBottom)): return XPopupWidget.Direction.East else: return XPopupWidget.Direction.West def exec_(self, pos=None): self._result = 0 self.setWindowModality(Qt.ApplicationModal) self.popup(pos) while self.isVisible(): QApplication.processEvents() return self.result() def eventFilter(self, object, event): """ Processes when the window is moving to update the position for the popup if in popup mode. :param object | <QObject> event | <QEvent> """ if not self.isVisible(): return False links = self.positionLinkedTo() is_dialog = self.currentMode() == self.Mode.Dialog if object not in links: return False if event.type() == event.Close: self.close() return False if event.type() == event.Hide and not is_dialog: self.hide() return False if event.type() == event.Move and not is_dialog: deltaPos = event.pos() - event.oldPos() self.move(self.pos() + deltaPos) return False if self.currentMode() != self.Mode.ToolTip: return False if event.type() == event.Leave: pos = object.mapFromGlobal(QCursor.pos()) if (not object.rect().contains(pos)): self.close() event.accept() return True if event.type() in (event.MouseButtonPress, event.MouseButtonDblClick): self.close() event.accept() return True return False @Slot(QAbstractButton) def handleButtonClick(self, button): """ Handles the button click for this widget. If the Reset button was clicked, then the resetRequested signal will be emitted. All buttons will emit the buttonClicked signal. :param button | <QAbstractButton> """ if (self.signalsBlocked()): return if (button == self._buttonBox.button(QDialogButtonBox.Reset)): self.resetRequested.emit() self.buttonClicked.emit(button) def isAnimated(self): """ Returns whether or not the popup widget should animate its opacity when it is shown. :return <bool> """ return self._animated def isPossibleAnchor(self, anchor): return bool(anchor & self._possibleAnchors) def isResizable(self): """ Returns if this popup is resizable or not. :return <bool> """ return self._resizable def keyPressEvent(self, event): """ Looks for the Esc key to close the popup. :param event | <QKeyEvent> """ if (event.key() == Qt.Key_Escape): self.reject() event.accept() return elif (event.key() in (Qt.Key_Return, Qt.Key_Enter)): if self._autoDefault: self.accept() event.accept() return super(XPopupWidget, self).keyPressEvent(event) def mapAnchorFrom(self, widget, point): """ Returns the anchor point that best fits within the given widget from the inputed global position. :param widget | <QWidget> point | <QPoint> :return <XPopupWidget.Anchor> """ screen_geom = QtGui.QDesktopWidget(self).screenGeometry() # calculate the end rect for each position Anchor = self.Anchor w = self.width() h = self.height() possible_rects = { # top anchors Anchor.TopLeft: QtCore.QRect(point.x(), point.y(), w, h), Anchor.TopCenter: QtCore.QRect(point.x() - w / 2, point.y(), w, h), Anchor.TopRight: QtCore.QRect(point.x() - w, point.y(), w, h), # left anchors Anchor.LeftTop: QtCore.QRect(point.x(), point.y(), w, h), Anchor.LeftCenter: QtCore.QRect(point.x(), point.y() - h / 2, w, h), Anchor.LeftBottom: QtCore.QRect(point.x(), point.y() - h, w, h), # bottom anchors Anchor.BottomLeft: QtCore.QRect(point.x(), point.y() - h, w, h), Anchor.BottomCenter: QtCore.QRect(point.x() - w / 2, point.y() - h, w, h), Anchor.BottomRight: QtCore.QRect(point.x() - w, point.y() - h, w, h), # right anchors Anchor.RightTop: QtCore.QRect(point.x() - self.width(), point.y(), w, h), Anchor.RightCenter: QtCore.QRect(point.x() - self.width(), point.y() - h / 2, w, h), Anchor.RightBottom: QtCore.QRect(point.x() - self.width(), point.y() - h, w, h) } for anchor in (Anchor.TopCenter, Anchor.BottomCenter, Anchor.LeftCenter, Anchor.RightCenter, Anchor.TopLeft, Anchor.LeftTop, Anchor.BottomLeft, Anchor.LeftBottom, Anchor.TopRight, Anchor.RightTop, Anchor.BottomRight, Anchor.RightBottom): if not self.isPossibleAnchor(anchor): continue rect = possible_rects[anchor] if screen_geom.contains(rect): return anchor return self.anchor() def popup(self, pos=None): """ Pops up this widget at the inputed position. The inputed point should \ be in global space. :param pos | <QPoint> :return <bool> success """ if self._first and self.centralWidget() is not None: self.adjustSize() self._first = False if not self.signalsBlocked(): self.aboutToShow.emit() if not pos: pos = QCursor.pos() if self.currentMode() == XPopupWidget.Mode.Dialog and \ self.isVisible(): return False elif self.currentMode() == XPopupWidget.Mode.Dialog: self.setPopupMode() # auto-calculate the point if self.autoCalculateAnchor(): self.setAnchor(self.mapAnchorFrom(self.parent(), pos)) pad = self.popupPadding() # determine where to move based on the anchor anchor = self.anchor() # MODIFY X POSITION # align x-left if (anchor & (XPopupWidget.Anchor.TopLeft | XPopupWidget.Anchor.BottomLeft)): pos.setX(pos.x() - pad) # align x-center elif (anchor & (XPopupWidget.Anchor.TopCenter | XPopupWidget.Anchor.BottomCenter)): pos.setX(pos.x() - self.width() / 2) # align x-right elif ( anchor & (XPopupWidget.Anchor.TopRight | XPopupWidget.Anchor.BottomRight)): pos.setX(pos.x() - self.width() + pad) # align x-padded elif (anchor & (XPopupWidget.Anchor.RightTop | XPopupWidget.Anchor.RightCenter | XPopupWidget.Anchor.RightBottom)): pos.setX(pos.x() - self.width()) # MODIFY Y POSITION # align y-top if (anchor & (XPopupWidget.Anchor.LeftTop | XPopupWidget.Anchor.RightTop)): pos.setY(pos.y() - pad) # align y-center elif (anchor & (XPopupWidget.Anchor.LeftCenter | XPopupWidget.Anchor.RightCenter)): pos.setY(pos.y() - self.height() / 2) # align y-bottom elif (anchor & (XPopupWidget.Anchor.LeftBottom | XPopupWidget.Anchor.RightBottom)): pos.setY(pos.y() - self.height() + pad) # align y-padded elif ( anchor & (XPopupWidget.Anchor.BottomLeft | XPopupWidget.Anchor.BottomCenter | XPopupWidget.Anchor.BottomRight)): pos.setY(pos.y() - self.height()) self.adjustMask() self.move(pos) self.update() self.setUpdatesEnabled(True) if self.isAnimated(): anim = QPropertyAnimation(self, 'windowOpacity') anim.setParent(self) anim.setStartValue(0.0) anim.setEndValue(self.windowOpacity()) anim.setDuration(500) anim.finished.connect(anim.deleteLater) self.setWindowOpacity(0.0) else: anim = None self.show() if self.currentMode() != XPopupWidget.Mode.ToolTip: self.activateWindow() widget = self.centralWidget() if widget: self.centralWidget().setFocus() if anim: anim.start() if not self.signalsBlocked(): self.shown.emit() return True def paintEvent(self, event): """ Overloads the paint event to handle painting pointers for the popup \ mode. :param event | <QPaintEvent> """ # use the base technique for the dialog mode if self.currentMode() == XPopupWidget.Mode.Dialog: super(XPopupWidget, self).paintEvent(event) return # setup the coloring options palette = self.palette() with XPainter(self) as painter: pen = QPen(palette.color(palette.Window).darker(130)) pen.setWidthF(1.75) painter.setPen(pen) painter.setRenderHint(painter.Antialiasing) painter.setBrush(palette.color(palette.Window)) painter.drawPath(self.borderPath()) def popupPadding(self): """ Returns the amount of pixels to pad the popup arrow for this widget. :return <int> """ return self._popupPadding def possibleAnchors(self): return self._possibleAnchors def positionLinkedTo(self): """ Returns the widget that this popup is linked to for positional changes. :return [<QWidget>, ..] """ return self._positionLinkedTo @Slot() def reject(self): """ Emits the accepted signal and closes the popup. """ self._result = 0 if not self.signalsBlocked(): self.rejected.emit() if self.autoCloseOnReject(): self.close() def result(self): return self._result def resizeEvent(self, event): """ Resizes this widget and updates the mask. :param event | <QResizeEvent> """ self.setUpdatesEnabled(False) super(XPopupWidget, self).resizeEvent(event) self.adjustMask() self.setUpdatesEnabled(True) x = self.width() - self._sizeGrip.width() y = self.height() - self._sizeGrip.height() self._leftSizeGrip.move(0, y) self._sizeGrip.move(x, y) def scrollArea(self): """ Returns the scroll area widget for this popup. :return <QScrollArea> """ return self._scrollArea def setAnimated(self, state): """ Sets whether or not the popup widget should animate its opacity when it is shown. :param state | <bool> """ self._animated = state self.setAttribute(Qt.WA_TranslucentBackground, state) def setAutoCloseOnAccept(self, state): """ Sets whether or not the popup handles closing for accepting states. :param state | <bool> """ self._autoCloseOnAccept = state def setAutoCloseOnReject(self, state): """ Sets whether or not the popup handles closing for rejecting states. :param state | <bool> """ self._autoCloseOnReject = state def setAutoDefault(self, state): """ Sets whether or not the buttons should respond to defaulting options when the user is interacting with it. :param state | <bool> """ self._autoDefault = state for button in self.buttonBox().buttons(): button.setAutoDefault(state) button.setDefault(state) def setAnchor(self, anchor): """ Sets the anchor position for this popup widget to the inputed point. :param anchor | <XPopupWidget.Anchor> """ self._anchor = anchor self.adjustContentsMargins() def setAutoCalculateAnchor(self, state=True): """ Sets whether or not this widget should auto-calculate the anchor point based on the parent position when the popup is triggered. :param state | <bool> """ self._autoCalculateAnchor = state def setAutoCloseOnFocusOut(self, state): """ Sets whether or not this popup widget should auto-close when the user clicks off the view. :param state | <bool> """ self._autoCloseOnFocusOut = state self.updateModeSettings() def setCentralWidget(self, widget): """ Sets the central widget that will be used by this popup. :param widget | <QWidget> || None """ self._scrollArea.takeWidget() self._scrollArea.setWidget(widget) self.adjustSize() def setCurrentMode(self, mode): """ Sets the current mode for this dialog to the inputed mode. :param mode | <XPopupWidget.Mode> """ if (self._currentMode == mode): return self._currentMode = mode self.updateModeSettings() @Slot() def setDialogMode(self): """ Sets the current mode value to Dialog. """ self.setCurrentMode(XPopupWidget.Mode.Dialog) @deprecatedmethod('XPopupWidget', 'Direction is no longer used, use setAnchor instead') def setDirection(self, direction): """ Sets the direction for this widget to the inputed direction. :param direction | <XPopupWidget.Direction> """ if (direction == XPopupWidget.Direction.North): self.setAnchor(XPopupWidget.Anchor.TopCenter) elif (direction == XPopupWidget.Direction.South): self.setAnchor(XPopupWidget.Anchor.BottomCenter) elif (direction == XPopupWidget.Direction.East): self.setAnchor(XPopupWidget.Anchor.LeftCenter) else: self.setAnchor(XPopupWidget.Anchor.RightCenter) def setPalette(self, palette): """ Sets the palette for this widget and the scroll area. :param palette | <QPalette> """ super(XPopupWidget, self).setPalette(palette) self._scrollArea.setPalette(palette) def setPopupMode(self): """ Sets the current mode value to Popup. """ self.setCurrentMode(XPopupWidget.Mode.Popup) def setPopupPadding(self, padding): """ Sets the amount to pad the popup area when displaying this widget. :param padding | <int> """ self._popupPadding = padding self.adjustContentsMargins() def setPossibleAnchors(self, anchors): self._possibleAnchors = anchors def setPositionLinkedTo(self, widgets): """ Sets the widget that this popup will be linked to for positional changes. :param widgets | <QWidget> || [<QWidget>, ..] """ if type(widgets) in (list, set, tuple): new_widgets = list(widgets) else: new_widgets = [] widget = widgets while widget: widget.installEventFilter(self) new_widgets.append(widget) widget = widget.parent() self._positionLinkedTo = new_widgets def setResizable(self, state): self._resizable = state self._sizeGrip.setVisible(state) self._leftSizeGrip.setVisible(state) def setShowButtonBox(self, state): self._buttonBoxVisible = state self.buttonBox().setVisible(state) def setShowTitleBar(self, state): self._titleBarVisible = state self._dialogButton.setVisible(state) self._closeButton.setVisible(state) def setToolTipMode(self): """ Sets the mode for this popup widget to ToolTip """ self.setCurrentMode(XPopupWidget.Mode.ToolTip) def setVisible(self, state): super(XPopupWidget, self).setVisible(state) widget = self.centralWidget() if widget: widget.setVisible(state) def timerEvent(self, event): """ When the timer finishes, hide the tooltip popup widget. :param event | <QEvent> """ if self.currentMode() == XPopupWidget.Mode.ToolTip: self.killTimer(event.timerId()) event.accept() self.close() else: super(XPopupWidget, self).timerEvent(event) def updateModeSettings(self): mode = self.currentMode() is_visible = self.isVisible() # display as a floating dialog if mode == XPopupWidget.Mode.Dialog: self.setWindowFlags(Qt.Dialog | Qt.Tool) self.setAttribute(Qt.WA_TransparentForMouseEvents, False) self._closeButton.setVisible(False) self._dialogButton.setVisible(False) # display as a user tooltip elif mode == XPopupWidget.Mode.ToolTip: flags = Qt.Popup | Qt.FramelessWindowHint self.setWindowFlags(flags) self.setBackgroundRole(QPalette.Window) self.setAttribute(Qt.WA_TransparentForMouseEvents) self.setShowTitleBar(False) self.setShowButtonBox(False) self.setFocusPolicy(Qt.NoFocus) # hide the scrollbars policy = Qt.ScrollBarAlwaysOff self._scrollArea.setVerticalScrollBarPolicy(policy) self._scrollArea.setHorizontalScrollBarPolicy(policy) # display as a popup widget else: flags = Qt.Popup | Qt.FramelessWindowHint if not self.autoCloseOnFocusOut(): flags |= Qt.Tool self.setWindowFlags(flags) self._closeButton.setVisible(self._titleBarVisible) self._dialogButton.setVisible(self._titleBarVisible) self.setBackgroundRole(QPalette.Window) self.adjustContentsMargins() if (is_visible): self.show() @staticmethod @deprecatedmethod('XPopupWidget', 'This method no longer has an effect as we are not '\ 'storing references to the tooltip.') def hideToolTip(key=None): """ Hides any existing tooltip popup widgets. :warning This method is deprecated! """ pass @staticmethod def showToolTip(text, point=None, anchor=None, parent=None, background=None, foreground=None, key=None, seconds=5): """ Displays a popup widget as a tooltip bubble. :param text | <str> point | <QPoint> || None anchor | <XPopupWidget.Mode.Anchor> || None parent | <QWidget> || None background | <QColor> || None foreground | <QColor> || None key | <str> || None seconds | <int> """ if point is None: point = QCursor.pos() if parent is None: parent = QApplication.activeWindow() if anchor is None and parent is None: anchor = XPopupWidget.Anchor.TopCenter # create a new tooltip widget widget = XPopupWidget(parent) widget.setToolTipMode() widget.setResizable(False) # create the tooltip label label = QLabel(text, widget) label.setOpenExternalLinks(True) label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) label.setMargin(3) label.setIndent(3) label.adjustSize() widget.setCentralWidget(label) # update the tip label.adjustSize() widget.adjustSize() palette = widget.palette() if not background: background = palette.color(palette.ToolTipBase) if not foreground: foreground = palette.color(palette.ToolTipText) palette.setColor(palette.Window, QColor(background)) palette.setColor(palette.WindowText, QColor(foreground)) widget.setPalette(palette) widget.centralWidget().setPalette(palette) if anchor is None: widget.setAutoCalculateAnchor(True) else: widget.setAnchor(anchor) widget.setAutoCloseOnFocusOut(True) widget.setAttribute(Qt.WA_DeleteOnClose) widget.popup(point) widget.startTimer(1000 * seconds) return widget
class XPagesWidget(QWidget): """ """ currentPageChanged = Signal(int) pageSizeChanged = Signal(int) pageCountChanged = Signal(int) def __init__( self, parent = None ): super(XPagesWidget, self).__init__( parent ) # define custom properties self._currentPage = 1 self._pageCount = 10 self._itemCount = 0 self._pageSize = 50 self._itemsTitle = 'items' self._pagesSpinner = QSpinBox() self._pagesSpinner.setMinimum(1) self._pagesSpinner.setMaximum(10) self._pageSizeCombo = XComboBox(self) self._pageSizeCombo.setHint('all') self._pageSizeCombo.addItems(['', '25', '50', '75', '100']) self._pageSizeCombo.setCurrentIndex(2) self._nextButton = QToolButton(self) self._nextButton.setAutoRaise(True) self._nextButton.setArrowType(Qt.RightArrow) self._nextButton.setFixedWidth(16) self._prevButton = QToolButton(self) self._prevButton.setAutoRaise(True) self._prevButton.setArrowType(Qt.LeftArrow) self._prevButton.setFixedWidth(16) self._prevButton.setEnabled(False) self._pagesLabel = QLabel('of 10 for ', self) self._itemsLabel = QLabel(' items per page', self) # define the interface layout = QHBoxLayout() layout.addWidget(QLabel('Page', self)) layout.addWidget(self._prevButton) layout.addWidget(self._pagesSpinner) layout.addWidget(self._nextButton) layout.addWidget(self._pagesLabel) layout.addWidget(self._pageSizeCombo) layout.addWidget(self._itemsLabel) layout.addStretch(1) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) # create connections self._pageSizeCombo.currentIndexChanged.connect(self.pageSizePicked) self._nextButton.clicked.connect(self.gotoNext) self._prevButton.clicked.connect(self.gotoPrevious) self._pagesSpinner.editingFinished.connect(self.assignCurrentPage) def assignCurrentPage( self ): """ Assigns the page for the spinner to be current. """ self.setCurrentPage(self._pagesSpinner.value()) def currentPage( self ): """ Reutrns the current page for this widget. :return <int> """ return self._currentPage @Slot() def gotoFirst( self ): """ Goes to the first page. :sa setCurrentPage """ self.setCurrentPage(1) @Slot() def gotoLast( self ): """ Goes to the last page. :sa setCurrentPage """ self.setCurrentPage(self.pageCount()) @Slot() def gotoNext( self ): """ Goes to the next page. :sa setCurrentPage """ next_page = self.currentPage() + 1 if ( next_page > self.pageCount() ): return self.setCurrentPage(next_page) @Slot() def gotoPrevious( self ): """ Goes to the previous page. :sa setCurrentPage """ prev_page = self.currentPage() - 1 if ( prev_page == 0 ): return self.setCurrentPage(prev_page) def itemCount( self ): """ Returns the total number of items this widget holds. If no item count is defined, it will not be displayed in the label, otherwise it will show. :return <int> """ return self._itemCount def itemsTitle( self ): """ Returns the items title for this instance. :return <str> """ return self._itemsTitle def pageCount( self ): """ Returns the number of pages that this widget holds. :return <int> """ return self._pageCount def pageSize( self ): """ Returns the number of items that should be visible in a page. :return <int> """ return self._pageSize def pageSizeOptions( self ): """ Returns the list of options that will be displayed for this default size options. :return [<str>, ..] """ return map(str, self._pageSizeCombo.items()) def pageSizePicked( self, pageSize ): """ Updates when the user picks a page size. :param pageSize | <str> """ try: pageSize = int(self._pageSizeCombo.currentText()) except ValueError: pageSize = 0 self.setPageSize(pageSize) self.pageSizeChanged.emit(pageSize) def refreshLabels( self ): """ Refreshes the labels to display the proper title and count information. """ itemCount = self.itemCount() title = self.itemsTitle() if ( not itemCount ): self._itemsLabel.setText(' %s per page' % title) else: msg = ' %s per page, %i %s total' % (title, itemCount, title) self._itemsLabel.setText(msg) @Slot(int) def setCurrentPage( self, pageno ): """ Sets the current page for this widget to the inputed page. :param pageno | <int> """ if ( pageno == self._currentPage ): return if ( pageno <= 0 ): pageno = 1 self._currentPage = pageno self._prevButton.setEnabled(pageno > 1) self._nextButton.setEnabled(pageno < self.pageCount()) self._pagesSpinner.blockSignals(True) self._pagesSpinner.setValue(pageno) self._pagesSpinner.blockSignals(False) if ( not self.signalsBlocked() ): self.currentPageChanged.emit(pageno) @Slot(int) def setItemCount( self, itemCount ): """ Sets the item count for this page to the inputed value. :param itemCount | <int> """ self._itemCount = itemCount self.refreshLabels() @Slot(str) def setItemsTitle( self, title ): """ Sets the title that will be displayed when the items labels are rendered :param title | <str> """ self._itemsTitle = nativestring(title) self.refreshLabels() @Slot(int) def setPageCount( self, pageCount ): """ Sets the number of pages that this widget holds. :param pageCount | <int> """ if ( pageCount == self._pageCount ): return pageCount = max(1, pageCount) self._pageCount = pageCount self._pagesSpinner.setMaximum(pageCount) self._pagesLabel.setText('of %i for ' % pageCount) if ( pageCount and self.currentPage() <= 0 ): self.setCurrentPage(1) elif ( pageCount < self.currentPage() ): self.setCurrentPage(pageCount) if ( not self.signalsBlocked() ): self.pageCountChanged.emit(pageCount) self._prevButton.setEnabled(self.currentPage() > 1) self._nextButton.setEnabled(self.currentPage() < pageCount) @Slot(int) def setPageSize( self, pageSize ): """ Sets the number of items that should be visible in a page. Setting the value to 0 will use all sizes :return <int> """ if self._pageSize == pageSize: return self._pageSize = pageSize # update the display size ssize = nativestring(pageSize) if ( ssize == '0' ): ssize = '' self._pageSizeCombo.blockSignals(True) index = self._pageSizeCombo.findText(ssize) self._pageSizeCombo.setCurrentIndex(index) self._pageSizeCombo.blockSignals(False) def setPageSizeOptions( self, options ): """ Sets the options that will be displayed for this default size. :param options | [<str>,. ..] """ self._pageSizeCombo.blockSignals(True) self._pageSizeCombo.addItems(options) ssize = nativestring(self.pageSize()) if ( ssize == '0' ): ssize = '' index = self._pageSizeCombo.findText() self._pageSizeCombo.setCurrentIndex(index) self._pageSizeCombo.blockSignals(False) x_itemsTitle = Property(str, itemsTitle, setItemsTitle) x_pageCount = Property(int, pageCount, setPageCount) x_pageSize = Property(int, pageSize, setPageSize) x_pageSizeOptions = Property(list, pageSizeOptions, setPageSizeOptions)
class XFindWidget(QWidget): """ """ __designer_icon__ = resources.find('img/search.png') def __init__( self, parent = None ): super(XFindWidget, self).__init__( parent ) # define custom properties self._textEdit = None self._webView = None self._lastCursor = QTextCursor() self._lastText = '' self._closeButton = QToolButton(self) self._closeButton.setIcon(QIcon(resources.find('img/close.png'))) self._closeButton.setAutoRaise(True) self._closeButton.setToolTip('Hide the Find Field.') self._searchEdit = XLineEdit(self) self._searchEdit.setHint('search for...') self._previousButton = QToolButton(self) self._previousButton.setIcon(QIcon(resources.find('img/back.png'))) self._previousButton.setAutoRaise(True) self._previousButton.setToolTip('Find Previous') self._nextButton = QToolButton(self) self._nextButton.setIcon(QIcon(resources.find('img/forward.png'))) self._nextButton.setAutoRaise(True) self._nextButton.setToolTip('Find Next') self._caseSensitiveCheckbox = QCheckBox(self) self._caseSensitiveCheckbox.setText('Case Sensitive') self._wholeWordsCheckbox = QCheckBox(self) self._wholeWordsCheckbox.setText('Whole Words Only') self._regexCheckbox = QCheckBox(self) self._regexCheckbox.setText('Use Regex') self._findAction = QAction(self) self._findAction.setText('Find...') self._findAction.setIcon(QIcon(resources.find('img/search.png'))) self._findAction.setToolTip('Find in Text') self._findAction.setShortcut(QKeySequence('Ctrl+F')) self._findAction.setShortcutContext(Qt.WidgetWithChildrenShortcut) # layout the widgets layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget( self._closeButton ) layout.addWidget( self._searchEdit ) layout.addWidget( self._previousButton ) layout.addWidget( self._nextButton ) layout.addWidget( self._caseSensitiveCheckbox ) layout.addWidget( self._wholeWordsCheckbox ) layout.addWidget( self._regexCheckbox ) self.setLayout(layout) # create connections self._findAction.triggered.connect( self.show ) self._searchEdit.textChanged.connect( self.findNext ) self._closeButton.clicked.connect( self.hide ) self._previousButton.clicked.connect( self.findPrev ) self._nextButton.clicked.connect( self.findNext ) self._caseSensitiveCheckbox.clicked.connect( self.findNext ) self._wholeWordsCheckbox.clicked.connect( self.findNext ) self._searchEdit.returnPressed.connect( self.findNext ) self._regexCheckbox.clicked.connect( self.findNext ) def find( self, flags = 0 ): """ Looks throught the text document based on the current criteria. The \ inputed flags will be merged with the generated search flags. :param flags | <QTextDocument.FindFlag> :return <bool> | success """ # check against the web and text views if ( not (self._textEdit or self._webView) ): fg = QColor('darkRed') bg = QColor('red').lighter(180) palette = self.palette() palette.setColor(palette.Text, fg) palette.setColor(palette.Base, bg) self._searchEdit.setPalette(palette) self._searchEdit.setToolTip( 'No Text Edit is linked.' ) return False if ( self._caseSensitiveCheckbox.isChecked() ): flags |= QTextDocument.FindCaseSensitively if ( self._textEdit and self._wholeWordsCheckbox.isChecked() ): flags |= QTextDocument.FindWholeWords terms = self._searchEdit.text() if ( terms != self._lastText ): self._lastCursor = QTextCursor() if ( self._regexCheckbox.isChecked() ): terms = QRegExp(terms) palette = self.palette() # search on a text edit if ( self._textEdit ): cursor = self._textEdit.document().find(terms, self._lastCursor, QTextDocument.FindFlags(flags)) found = not cursor.isNull() self._lastCursor = cursor self._textEdit.setTextCursor(cursor) elif ( QWebPage ): flags = QWebPage.FindFlags(flags) flags |= QWebPage.FindWrapsAroundDocument found = self._webView.findText(terms, flags) self._lastText = self._searchEdit.text() if ( not terms or found ): fg = palette.color(palette.Text) bg = palette.color(palette.Base) else: fg = QColor('darkRed') bg = QColor('red').lighter(180) palette.setColor(palette.Text, fg) palette.setColor(palette.Base, bg) self._searchEdit.setPalette(palette) return found def findNext( self ): """ Looks for the search terms that come up next based on the criteria. :return <bool> | success """ return self.find() def findPrev( self ): """ Looks for the search terms that come up last based on the criteria. :return <bool> | success """ return self.find(QTextDocument.FindBackward) def setTextEdit( self, textEdit ): """ Sets the text edit that this find widget will use to search. :param textEdit | <QTextEdit> """ if ( self._textEdit ): self._textEdit.removeAction(self._findAction) self._textEdit = textEdit if ( textEdit ): textEdit.addAction(self._findAction) def setWebView( self, webView ): """ Sets the web view edit that this find widget will use to search. :param webView | <QWebView> """ if ( self._webView ): self._webView.removeAction(self._findAction) self._webView = webView if ( webView ): webView.addAction(self._findAction) def show( self ): """ Sets this widget visible and then makes the find field have focus. """ super(XFindWidget, self).show() self._searchEdit.setFocus() def textEdit( self ): """ Returns the text edit linked with this find widget. :return <QTextEdit> """ return self._textEdit def webView( self ): """ Returns the text edit linked with this find widget. :return <QWebView> """ return self._webView
class XLocationWidget(QWidget): locationChanged = Signal(str) locationEdited = Signal() def __init__(self, parent): super(XLocationWidget, self).__init__(parent) # define the interface self._locationEdit = XLineEdit(self) self._locationButton = QToolButton(self) self._urlTemplate = 'http://maps.google.com/maps?%(params)s' self._urlQueryKey = 'q' self._locationButton.setAutoRaise(True) self._locationButton.setIcon(QIcon(resources.find('img/map.png'))) self._locationEdit.setHint('no location set') layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._locationEdit) layout.addWidget(self._locationButton) self.setLayout(layout) # create connections self._locationEdit.textChanged.connect(self.locationChanged) self._locationEdit.textEdited.connect(self.locationEdited) self._locationButton.clicked.connect(self.browseMaps) def blockSignals(self, state): """ Blocks the signals for this widget and its sub-parts. :param state | <bool> """ super(XLocationWidget, self).blockSignals(state) self._locationEdit.blockSignals(state) self._locationButton.blockSignals(state) def browseMaps(self): """ Brings up a web browser with the address in a Google map. """ url = self.urlTemplate() params = urllib.urlencode({self.urlQueryKey(): self.location()}) url = url % {'params': params} webbrowser.open(url) def hint(self): """ Returns the hint associated with this widget. :return <str> """ return self._locationEdit.hint() def lineEdit(self): """ Returns the line edit linked with this widget. :return <XLineEdit> """ return self._locationEdit def location(self): """ Returns the current location from the edit. :return <str> """ return nativestring(self._locationEdit.text()) @Slot(str) def setHint(self, hint): """ Sets the hint associated with this widget. :param hint | <str> """ self._locationEdit.setHint(hint) @Slot(str) def setLocation(self, location): """ Sets the location for this widget to the inputed location. :param location | <str> """ self._locationEdit.setText(nativestring(location)) def setUrlQueryKey(self, key): """ Sets the key for the URL to the inputed key. :param key | <str> """ self._urlQueryKey = nativestring(key) def setUrlTemplate(self, templ): """ Sets the URL path template that will be used when looking up locations on the web. :param templ | <str> """ self._urlQueryTemplate = nativestring(templ) def urlQueryKey(self): """ Returns the query key that will be used for this location. :return <str> """ return self._urlQueryKey def urlTemplate(self): """ Returns the url template that will be used when mapping this location. :return <str> """ return self._urlTemplate x_hint = Property(str, hint, setHint) x_location = Property(str, location, setLocation) x_urlQueryKey = Property(str, urlQueryKey, setUrlQueryKey) x_urlTemplate = Property(str, urlTemplate, setUrlTemplate)
def reset( self ): """ Resets the user interface buttons for this widget. """ # clear previous widgets for btn in self.findChildren(QToolButton): btn.close() btn.setParent(None) btn.deleteLater() # determine coloring options palette = self.palette() unchecked = palette.color(palette.Button) # determine if this is a dark or light scheme avg = (unchecked.red() + unchecked.green() + unchecked.blue()) / 3.0 if ( avg < 140 ): checked = unchecked.lighter(115) checked_clr = self.colorString(unchecked.lighter(120)) border_clr = self.colorString(unchecked.darker(140)) unchecked_clr = self.colorString(checked.lighter(140)) unchecked_clr_alt = self.colorString(checked.lighter(120)) checked_clr_alt = self.colorString(unchecked) else: checked = unchecked.lighter(120) checked_clr = self.colorString(unchecked) border_clr = self.colorString(unchecked.darker(160)) unchecked_clr = self.colorString(checked) unchecked_clr_alt = self.colorString(checked.darker(130)) checked_clr_alt = self.colorString(unchecked.darker(120)) # define the stylesheet options options = {} options['top_left_radius'] = 0 options['top_right_radius'] = 0 options['bot_left_radius'] = 0 options['bot_right_radius'] = 0 options['border_color'] = border_clr options['checked_clr'] = checked_clr options['checked_clr_alt'] = checked_clr_alt options['unchecked_clr'] = unchecked_clr options['unchecked_clr_alt'] = unchecked_clr_alt options['padding_top'] = 1 options['padding_bottom'] = 1 options['padding_left'] = 1 options['padding_right'] = 1 horiz = self.direction() in (QBoxLayout.LeftToRight, QBoxLayout.RightToLeft) if ( horiz ): options['x1'] = 0 options['y1'] = 0 options['x2'] = 0 options['y2'] = 1 else: options['x1'] = 0 options['y1'] = 0 options['x2'] = 1 options['y2'] = 1 actions = self.actionGroup().actions() count = len(actions) for i, action in enumerate(actions): btn = QToolButton(self) btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) btn.setDefaultAction(action) self.layout().insertWidget(i, btn) options['top_left_radius'] = 1 options['bot_left_radius'] = 1 options['top_right_radius'] = 1 options['bot_right_radius'] = 1 if ( horiz ): options['padding_left'] = self._padding options['padding_right'] = self._padding else: options['padding_top'] = self._padding options['padding_bottom'] = self._padding if ( not i ): if ( horiz ): options['top_left_radius'] = self.cornerRadius() options['bot_left_radius'] = self.cornerRadius() options['padding_left'] += self.cornerRadius() / 3.0 else: options['top_left_radius'] = self.cornerRadius() options['top_right_radius'] = self.cornerRadius() options['padding_top'] += self.cornerRadius() / 3.0 elif ( i == count - 1 ): if ( horiz ): options['top_right_radius'] = self.cornerRadius() options['bot_right_radius'] = self.cornerRadius() options['padding_right'] += self.cornerRadius() / 3.0 else: options['bot_left_radius'] = self.cornerRadius() options['bot_right_radius'] = self.cornerRadius() options['padding_bottom'] += self.cornerRadius() / 3.0 btn.setStyleSheet(TOOLBUTTON_STYLE % options) btn.setAutoFillBackground(True)
class XFilepathEdit(QWidget): """ The XFilepathEdit class provides a common interface to prompt the user to select a filepath from the filesystem. It can be configured to load directories, point to a save file path location, or to an open file path location. It can also be setup to color changed based on the validity of the existance of the filepath. == Example == |>>> from projexui.widgets.xfilepathedit import XFilepathEdit |>>> import projexui | |>>> # create the edit |>>> edit = projexui.testWidget(XFilepathEdit) | |>>> # set the filepath |>>> edit.setFilepath('/path/to/file') | |>>> # prompt the user to select the filepath |>>> edit.pickFilepath() | |>>> # enable the coloring validation |>>> edit.setValidated(True) """ __designer_icon__ = projexui.resources.find('img/file.png') Mode = enum('OpenFile', 'SaveFile', 'Path', 'OpenFiles') filepathChanged = Signal(str) def __init__( self, parent = None ): super(XFilepathEdit, self).__init__( parent ) # define custom properties self._validated = False self._validForeground = QColor(0, 120, 0) self._validBackground = QColor(0, 120, 0, 100) self._invalidForeground = QColor(255, 0, 0) self._invalidBackground = QColor(255, 0, 0, 100) self._normalizePath = False self._filepathMode = XFilepathEdit.Mode.OpenFile self._filepathEdit = XLineEdit(self) self._filepathButton = QToolButton(self) self._filepathTypes = 'All Files (*.*)' # set default properties ico = projexui.resources.find('img/folder.png') self._filepathEdit.setReadOnly(False) self._filepathEdit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._filepathButton.setText('...') self._filepathButton.setFixedSize(25, 23) self._filepathButton.setAutoRaise(True) self._filepathButton.setIcon(QIcon(ico)) self._filepathEdit.setContextMenuPolicy(Qt.CustomContextMenu) self.setWindowTitle('Load File') self.setAcceptDrops(True) # define the layout layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._filepathEdit) layout.addWidget(self._filepathButton) self.setLayout(layout) # create connections self._filepathEdit.installEventFilter(self) self._filepathButton.clicked.connect( self.pickFilepath ) self._filepathEdit.textChanged.connect( self.emitFilepathChanged ) self._filepathEdit.textChanged.connect( self.validateFilepath ) self._filepathEdit.customContextMenuRequested.connect( self.showMenu ) def autoRaise( self ): """ Returns whether or not the tool button will auto raise. :return <bool> """ return self._filepathButton.autoRaise() @Slot() def clearFilepath( self ): """ Clears the filepath contents for this path. """ self.setFilepath('') @Slot() def copyFilepath( self ): """ Copies the current filepath contents to the current clipboard. """ clipboard = QApplication.instance().clipboard() clipboard.setText(self.filepath()) clipboard.setText(self.filepath(), clipboard.Selection) def dragEnterEvent( self, event ): """ Processes drag enter events. :param event | <QDragEvent> """ if ( event.mimeData().hasUrls() ): event.acceptProposedAction() def dragMoveEvent( self, event ): """ Processes drag move events. :param event | <QDragEvent> """ if ( event.mimeData().hasUrls() ): event.acceptProposedAction() def dropEvent( self, event ): """ Processes drop event. :param event | <QDropEvent> """ if event.mimeData().hasUrls(): url = event.mimeData().urls()[0] filepath = url.toLocalFile() if filepath: self.setFilepath(filepath) def emitFilepathChanged( self ): """ Emits the filepathChanged signal for this widget if the signals are \ not being blocked. """ if ( not self.signalsBlocked() ): self.filepathChanged.emit(self.filepath()) def eventFilter( self, object, event ): """ Overloads the eventFilter to look for click events on the line edit. :param object | <QObject> event | <QEvent> """ if ( object == self._filepathEdit and \ self._filepathEdit.isReadOnly() and \ event.type() == event.MouseButtonPress and \ event.button() == Qt.LeftButton ): self.pickFilepath() return False def filepath( self, validated = False ): """ Returns the filepath for this widget. If the validated flag is set \ then this method will only return if the file or folder actually \ exists for this path. In the case of a SaveFile, only the base folder \ needs to exist on the system, in other modes the actual filepath must \ exist. If not validated, the text will return whatever is currently \ entered. :return <str> """ paths = self.filepaths() if not paths: return '' if not validated or self.isValid(): return paths[0] return '' def filepaths(self): """ Returns a list of the filepaths for this edit. :return [<str>, ..] """ return nativestring(self._filepathEdit.text()).split(os.path.pathsep) def filepathMode( self ): """ Returns the filepath mode for this widget. :return <XFilepathEdit.Mode> """ return self._filepathMode def filepathModeText( self ): """ Returns the text representation for this filepath mode. :return <str> """ return XFilepathEdit.Mode[self._filepathMode] def filepathTypes( self ): """ Returns the filepath types that will be used for this widget. :return <str> """ return self._filepathTypes def hint( self ): """ Returns the hint for this filepath. :return <str> """ return self._filepathEdit.hint() def icon( self ): """ Returns the icon that is used for this filepath widget. :return <QIcon> """ return self._filepathButton.icon() def invalidBackground( self ): """ Returns the invalid background color for this widget. :return <QColor> """ return self._invalidBackground def invalidForeground( self ): """ Returns the invalid foreground color for this widget. :return <QColor> """ return self._invalidForeground def isValid( self ): """ Returns whether or not the filepath exists on the system. \ In the case of a SaveFile, only the base folder \ needs to exist on the system, in other modes the actual filepath must \ exist. :return <bool> """ check = nativestring(self._filepathEdit.text()).split(os.path.pathsep)[0] if ( self.filepathMode() == XFilepathEdit.Mode.SaveFile ): check = os.path.dirname(check) return os.path.exists(check) def isValidated( self ): """ Set whether or not to validate the filepath as the user is working \ with it. :return <bool> """ return self._validated def isReadOnly( self ): """ Returns if the widget is read only for text editing or not. :return <bool> """ return self._filepathEdit.isReadOnly() def normalizePath(self): """ Returns whether or not the path should be normalized for the current operating system. When off, it will be defaulted to forward slashes (/). :return <bool> """ return self._normalizePath def pickFilepath( self ): """ Prompts the user to select a filepath from the system based on the \ current filepath mode. """ mode = self.filepathMode() filepath = '' filepaths = [] curr_dir = nativestring(self._filepathEdit.text()) if ( not curr_dir ): curr_dir = QDir.currentPath() if mode == XFilepathEdit.Mode.SaveFile: filepath = QFileDialog.getSaveFileName( self, self.windowTitle(), curr_dir, self.filepathTypes() ) elif mode == XFilepathEdit.Mode.OpenFile: filepath = QFileDialog.getOpenFileName( self, self.windowTitle(), curr_dir, self.filepathTypes() ) elif mode == XFilepathEdit.Mode.OpenFiles: filepaths = QFileDialog.getOpenFileNames( self, self.windowTitle(), curr_dir, self.filepathTypes() ) else: filepath = QFileDialog.getExistingDirectory( self, self.windowTitle(), curr_dir ) if filepath: if type(filepath) == tuple: filepath = filepath[0] self.setFilepath(nativestring(filepath)) elif filepaths: self.setFilepaths(map(str, filepaths)) def setAutoRaise( self, state ): """ Sets whether or not the tool button will auto raise. :param state | <bool> """ self._filepathButton.setAutoRaise(state) @Slot(int) def setFilepathMode( self, mode ): """ Sets the filepath mode for this widget to the inputed mode. :param mode | <XFilepathEdit.Mode> """ self._filepathMode = mode @Slot(str) def setFilepathModeText( self, text ): """ Sets the filepath mode for this widget based on the inputed text. :param text | <str> :return <bool> | success """ try: self.setFilepathMode(XFilepathEdit.Mode[nativestring(text)]) return True except KeyError: return False @Slot(str) def setFilepathTypes( self, filepathTypes ): """ Sets the filepath type string that will be used when looking up \ filepaths. :param filepathTypes | <str> """ self._filepathTypes = filepathTypes @Slot(str) def setFilepath(self, filepath): """ Sets the filepath text for this widget to the inputed path. :param filepath | <str> """ if not filepath: self._filepathEdit.setText('') return if self.normalizePath(): filepath = os.path.normpath(nativestring(filepath)) else: filepath = os.path.normpath(nativestring(filepath)).replace('\\', '/') self._filepathEdit.setText(filepath) def setFilepaths(self, filepaths): """ Sets the list of the filepaths for this widget to the inputed paths. :param filepaths | [<str>, ..] """ self.setFilepath(os.path.pathsep.join(filepaths)) def setHint(self, hint): """ Sets the hint for this filepath. :param hint | <str> """ if self.normalizePath(): filepath = os.path.normpath(nativestring(hint)) else: filepath = os.path.normpath(nativestring(hint)).replace('\\', '/') self._filepathEdit.setHint(hint) def setIcon( self, icon ): """ Sets the icon that will be used for this widget's tool button. :param icon | <QIcon> || <str> """ self._filepathButton.setIcon(QIcon(icon)) def setInvalidBackground( self, bg ): """ Sets the invalid background color for this widget to the inputed widget. :param bg | <QColor> """ self._invalidBackground = QColor(bg) def setInvalidForeground( self, fg ): """ Sets the invalid foreground color for this widget to the inputed widget. :param fg | <QColor> """ self._invalidForeground = QColor(fg) def setNormalizePath(self, state): """ Sets whether or not the path should be normalized for the current operating system. When off, it will be defaulted to forward slashes (/). :param state | <bool> """ self._normalizePath = state @Slot(bool) def setReadOnly( self, state ): """ Sets whether or not this filepath widget is readonly in the text edit. :param state | <bool> """ self._filepathEdit.setReadOnly(state) @Slot(bool) def setValidated( self, state ): """ Set whether or not to validate the path as the user edits it. :param state | <bool> """ self._validated = state palette = self.palette() # reset the palette to default, revalidate self._filepathEdit.setPalette(palette) self.validate() def setValidBackground( self, bg ): """ Sets the valid background color for this widget to the inputed color. :param bg | <QColor> """ self._validBackground = QColor(bg) def setValidForeground( self, fg ): """ Sets the valid foreground color for this widget to the inputed color. :param fg | <QColor> """ self._validForeground = QColor(fg) def showMenu( self, pos ): """ Popups a menu for this widget. """ menu = QMenu(self) menu.setAttribute(Qt.WA_DeleteOnClose) menu.addAction('Clear').triggered.connect(self.clearFilepath) menu.addSeparator() menu.addAction('Copy Filepath').triggered.connect(self.copyFilepath) menu.exec_(self.mapToGlobal(pos)) def validBackground( self ): """ Returns the valid background color for this widget. :return <QColor> """ return self._validBackground def validForeground( self ): """ Returns the valid foreground color for this widget. :return <QColor> """ return self._validForeground def validateFilepath( self ): """ Alters the color scheme based on the validation settings. """ if ( not self.isValidated() ): return valid = self.isValid() if ( not valid ): fg = self.invalidForeground() bg = self.invalidBackground() else: fg = self.validForeground() bg = self.validBackground() palette = self.palette() palette.setColor(palette.Base, bg) palette.setColor(palette.Text, fg) self._filepathEdit.setPalette(palette) # map Qt properties x_autoRaise = Property(bool, autoRaise, setAutoRaise) x_filepathTypes = Property(str, filepathTypes, setFilepathTypes) x_filepath = Property(str, filepath, setFilepath) x_readOnly = Property(bool, isReadOnly, setReadOnly) x_validated = Property(bool, isValidated, setValidated) x_hint = Property(str, hint, setHint) x_icon = Property('QIcon', icon, setIcon) x_normalizePath = Property(bool, normalizePath, setNormalizePath) x_invalidForeground = Property('QColor', invalidForeground, setInvalidForeground) x_invalidBackground = Property('QColor', invalidBackground, setInvalidBackground) x_validForeground = Property('QColor', validForeground, setValidForeground) x_validBackground = Property('QColor', validBackground, setValidBackground) x_filepathModeText = Property(str, filepathModeText, setFilepathModeText)