Пример #1
0
 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)
Пример #2
0
    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)
Пример #3
0
 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 )
Пример #4
0
    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)
Пример #5
0
    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)
Пример #6
0
    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)
Пример #7
0
    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)
Пример #8
0
    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)
Пример #9
0
 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)
Пример #10
0
 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)
Пример #11
0
 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)
Пример #12
0
 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)
Пример #13
0
 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 )
Пример #14
0
 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)
Пример #15
0
    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
Пример #16
0
 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
Пример #17
0
    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)
Пример #18
0
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)
Пример #19
0
 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)
Пример #20
0
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
Пример #21
0
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
Пример #22
0
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)
Пример #23
0
    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)
Пример #24
0
 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)
Пример #25
0
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)
Пример #26
0
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)
Пример #27
0
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)
Пример #28
0
 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()
Пример #29
0
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)
Пример #30
0
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
Пример #31
0
 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 )
Пример #32
0
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)
Пример #33
0
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)
Пример #34
0
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 )
Пример #35
0
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)
Пример #36
0
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)
Пример #37
0
    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()
Пример #38
0
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
Пример #39
0
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)
Пример #40
0
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
Пример #41
0
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)
Пример #42
0
 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)
Пример #43
0
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)