def __init__(self, parent=None): super(XQueryBuilderWidget, self).__init__(parent) # load the user interface projexui.loadUi(__file__, self) self.setMinimumWidth(470) # define custom properties self._rules = {} self._defaultQuery = [] self._completionTerms = [] self._minimumCount = 1 # set default properties self._container = QWidget(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) layout.addStretch(1) self._container.setLayout(layout) self.uiQueryAREA.setWidget(self._container) # create connections self.uiResetBTN.clicked.connect(self.emitResetRequested) self.uiSaveBTN.clicked.connect(self.emitSaveRequested) self.uiCancelBTN.clicked.connect(self.emitCancelRequested) self.resetRequested.connect(self.reset)
def __init__(self, parent=None): super(XCommentEdit, self).__init__(parent) # define custom properties self._attachments = {} self._showAttachments = True # create toolbar self._toolbar = QToolBar(self) self._toolbar.setMovable(False) self._toolbar.setFixedHeight(30) self._toolbar.setAutoFillBackground(True) self._toolbar.setFocusProxy(self) self._toolbar.hide() # create toolbar buttons self._attachButton = QToolButton(self) self._attachButton.setIcon(QIcon(resources.find('img/attach.png'))) self._attachButton.setToolTip('Add Attachment') self._attachButton.setAutoRaise(True) self._attachButton.setIconSize(QSize(24, 24)) self._attachButton.setFixedSize(26, 26) self._submitButton = QPushButton(self) self._submitButton.setText('Submit') self._submitButton.setFocusProxy(self) # create attachments widget self._attachmentsEdit = XMultiTagEdit(self) self._attachmentsEdit.setAutoResizeToContents(True) self._attachmentsEdit.setFrameShape(XMultiTagEdit.NoFrame) self._attachmentsEdit.setViewMode(XMultiTagEdit.ListMode) self._attachmentsEdit.setEditable(False) self._attachmentsEdit.setFocusProxy(self) self._attachmentsEdit.hide() # define toolbar layout spacer = QWidget(self) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._attachAction = self._toolbar.addWidget(self._attachButton) self._toolbar.addWidget(spacer) self._toolbar.addWidget(self._submitButton) # set standard properties self.setAutoResizeToContents(True) self.setHint('add comment') self.setFocusPolicy(Qt.StrongFocus) self.setRequireShiftForNewLine(True) # create connections self._attachButton.clicked.connect(self.attachmentRequested) self._submitButton.clicked.connect(self.acceptText) self._attachmentsEdit.tagRemoved.connect(self.removeAttachment) self.focusChanged.connect(self.setToolbarVisible)
def __init__( self, parent = None ): super(XRolloutWidget, self).__init__( parent ) # define custom properties self.setWidgetResizable(True) # set default properties widget = QWidget(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) layout.addStretch(1) widget.setLayout(layout) self.setWidget(widget)
def __init__(self, parent=None): super(XRolloutWidget, self).__init__(parent) # define custom properties self.setWidgetResizable(True) # set default properties widget = QWidget(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(3) layout.addStretch(1) widget.setLayout(layout) self.setWidget(widget)
def __init__( self, parent = None ): super(XQueryBuilderWidget, self).__init__( parent ) # load the user interface projexui.loadUi(__file__, self) self.setMinimumWidth(470) # define custom properties self._rules = {} self._defaultQuery = [] self._completionTerms = [] self._minimumCount = 1 # set default properties self._container = QWidget(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) layout.addStretch(1) self._container.setLayout(layout) self.uiQueryAREA.setWidget(self._container) # create connections self.uiResetBTN.clicked.connect( self.emitResetRequested ) self.uiSaveBTN.clicked.connect( self.emitSaveRequested ) self.uiCancelBTN.clicked.connect( self.emitCancelRequested ) self.resetRequested.connect( self.reset )
def __init__(self, parent=None): super(XOrbQueryContainer, self).__init__(parent) # load the user interface projexui.loadUi(__file__, self) # define custom properties self._queryWidget = parent self._entryWidget = QWidget(self) self._currentJoiner = QueryCompound.Op.And layout = QVBoxLayout() layout.addStretch(1) layout.setSpacing(3) self._entryWidget.setLayout(layout) self.uiQueryAREA.setWidget(self._entryWidget) # set default properties self.setContextMenuPolicy(Qt.CustomContextMenu) # create connections (use old-style syntax or PySide errors) self.connect(self.uiBackBTN, SIGNAL('clicked()'), self.exitCompound) self.entriesUpdated.connect(self.refreshEntries)
def __init__( self, parent = None ): super(XOrbQueryContainer, self).__init__( parent ) # load the user interface projexui.loadUi(__file__, self) # define custom properties self._queryWidget = parent self._entryWidget = QWidget(self) self._currentJoiner = QueryCompound.Op.And layout = QVBoxLayout() layout.addStretch(1) layout.setSpacing(3) self._entryWidget.setLayout(layout) self.uiQueryAREA.setWidget(self._entryWidget) # set default properties self.setContextMenuPolicy(Qt.CustomContextMenu) # create connections (use old-style syntax or PySide errors) self.connect(self.uiBackBTN, SIGNAL('clicked()'), self.exitCompound) self.entriesUpdated.connect(self.refreshEntries)
class XQueryBuilderWidget(QWidget): """ """ saveRequested = Signal() resetRequested = Signal() cancelRequested = Signal() def __init__(self, parent=None): super(XQueryBuilderWidget, self).__init__(parent) # load the user interface projexui.loadUi(__file__, self) self.setMinimumWidth(470) # define custom properties self._rules = {} self._defaultQuery = [] self._completionTerms = [] self._minimumCount = 1 # set default properties self._container = QWidget(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) layout.addStretch(1) self._container.setLayout(layout) self.uiQueryAREA.setWidget(self._container) # create connections self.uiResetBTN.clicked.connect(self.emitResetRequested) self.uiSaveBTN.clicked.connect(self.emitSaveRequested) self.uiCancelBTN.clicked.connect(self.emitCancelRequested) self.resetRequested.connect(self.reset) def addLineWidget(self, query=None): """ Adds a new line widget to the system with the given values. :param query | (<str> term, <str> operator, <str> vlaue) || None """ widget = XQueryLineWidget(self) widget.setTerms(sorted(self._rules.keys())) widget.setQuery(query) index = self._container.layout().count() - 1 self._container.layout().insertWidget(index, widget) widget.addRequested.connect(self.addLineWidget) widget.removeRequested.connect(self.removeLineWidget) # update the remove enabled options for these widgets self.updateRemoveEnabled() def addRule(self, rule): """ Adds a rule to the system. :param rule | <XQueryRule> """ self._rules[rule.term()] = rule self.updateRules() def clear(self): """ Clears out all the widgets from the system. """ for lineWidget in self.lineWidgets(): lineWidget.setParent(None) lineWidget.deleteLater() def completionTerms(self): """ Returns the list of terms that will be used as a global override for completion terms when the query rule generates a QLineEdit instance. :return [<str>, ..] """ return self._completionTerms def count(self): """ Returns the count of the line widgets in the system. :return <int> """ return len(self.lineWidgets()) def currentQuery(self): """ Returns the current query string for this widget. :return [(<str> term, <str> operator, <str> value), ..] """ widgets = self.lineWidgets() output = [] for widget in widgets: output.append(widget.query()) return output def defaultQuery(self): """ Returns the default query for the system. :return [(<str> term, <str> operator, <str> value), ..] """ return self._defaultQuery def keyPressEvent(self, event): """ Emits the save requested signal for this builder for when the enter or return press is clicked. :param event | <QKeyEvent> """ if (event.key() in (Qt.Key_Enter, Qt.Key_Return)): self.emitSaveRequested() super(XQueryBuilderWidget, self).keyPressEvent(event) def emitCancelRequested(self): """ Emits the cancel requested signal. """ if (not self.signalsBlocked()): self.cancelRequested.emit() def emitResetRequested(self): """ Emits the reste requested signal. """ if (not self.signalsBlocked()): self.resetRequested.emit() def emitSaveRequested(self): """ Emits the save requested signal. """ if (not self.signalsBlocked()): self.saveRequested.emit() def findRule(self, term): """ Looks up a rule by the inputed term. :param term | <str> :return <XQueryRule> || None """ return self._rules.get(nativestring(term)) def removeLineWidget(self, widget): """ Removes the line widget from the query. :param widget | <XQueryLineWidget> """ widget.setParent(None) widget.deleteLater() self.updateRemoveEnabled() def minimumCount(self): """ Defines the minimum number of query widgets that are allowed. :return <int> """ return self._minimumCount def lineWidgets(self): """ Returns a list of line widgets for this system. :return [<XQueryLineWidget>, ..] """ return self.findChildren(XQueryLineWidget) def reset(self): """ Resets the system to the default query. """ self.setCurrentQuery(self.defaultQuery()) def setCompletionTerms(self, terms): """ Sets the list of terms that will be used as a global override for completion terms when the query rule generates a QLineEdit instance. :param terms | [<str>, ..] """ self._completionTerms = terms def setCurrentQuery(self, query): """ Sets the query for this system to the inputed query. :param query | [(<str> term, <str> operator, <str> value), ..] """ self.clear() for entry in query: self.addLineWidget(entry) # make sure we have the minimum number of widgets for i in range(self.minimumCount() - len(query)): self.addLineWidget() def setDefaultQuery(self, query): """ Sets the default query that will be used when the user clicks on the \ reset button or the reset method is called. :param query | [(<str> term, <str> operator, <str> value), ..] """ self._defaultQuery = query[:] def setMinimumCount(self, count): """ Sets the minimum number of line widgets that are allowed at any \ given time. :param count | <int> """ self._minimumCount = count def setRules(self, rules): """ Sets all the rules for this builder. :param rules | [<XQueryRule>, ..] """ if (type(rules) in (list, tuple)): self._rules = dict([(x.term(), x) for x in rules]) self.updateRules() return True elif (type(rules) == dict): self._rules = rules.copy() self.updateRules() return True else: return False def setTerms(self, terms): """ Sets a simple rule list by accepting a list of strings for terms. \ This is a convenience method for the setRules method. :param rules | [<str> term, ..] """ return self.setRules([XQueryRule(term=term) for term in terms]) def updateRemoveEnabled(self): """ Updates the remove enabled baesd on the current number of line widgets. """ lineWidgets = self.lineWidgets() count = len(lineWidgets) state = self.minimumCount() < count for widget in lineWidgets: widget.setRemoveEnabled(state) def updateRules(self): """ Updates the query line items to match the latest rule options. """ terms = sorted(self._rules.keys()) for child in self.lineWidgets(): child.setTerms(terms)
class XOrbQueryContainer(QWidget): """ """ entriesUpdated = Signal() enterCompoundRequested = Signal(object, object) exitCompoundRequested = Signal() def __init__( self, parent = None ): super(XOrbQueryContainer, self).__init__( parent ) # load the user interface projexui.loadUi(__file__, self) # define custom properties self._queryWidget = parent self._entryWidget = QWidget(self) self._currentJoiner = QueryCompound.Op.And layout = QVBoxLayout() layout.addStretch(1) layout.setSpacing(3) self._entryWidget.setLayout(layout) self.uiQueryAREA.setWidget(self._entryWidget) # set default properties self.setContextMenuPolicy(Qt.CustomContextMenu) # create connections (use old-style syntax or PySide errors) self.connect(self.uiBackBTN, SIGNAL('clicked()'), self.exitCompound) self.entriesUpdated.connect(self.refreshEntries) def addEntry(self, query=None, entry=None): if query is None: query = Query() layout = self._entryWidget.layout() index = layout.count() - 1 if entry: index = layout.indexOf(entry) + 1 widget = XOrbQueryEntryWidget(self, self.tableType()) layout.insertWidget(index, widget) widget.setQuery(query) if not self.signalsBlocked(): self.entriesUpdated.emit() return widget def checkedEntries(self): """ Returns the widgets that are checked for this widget. :return [<XOrbQueryEntryWidget>, ..] """ return [entry for entry in self.entries() if entry.isChecked()] def clear(self): """ Clears out the widgets for this query builder. """ layout = self._entryWidget.layout() for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() widget.close() def createCompoundFromChecked(self): """ Creates a new compound query from the checked entry list. :return <orb.QueryCompound> """ checked_entries = self.checkedEntries() if len(checked_entries) <= 1: return QueryCompound() self.setUpdatesEnabled(False) joiner = self.currentJoiner() query = Query() for entry in checked_entries: if joiner == QueryCompound.Op.And: query &= entry.query() else: query |= entry.query() # clear out the existing containers first = checked_entries[0] first.setQuery(query) first.setChecked(False) layout = self._entryWidget.layout() for i in range(len(checked_entries) - 1, 0, -1): w = checked_entries[i] layout.takeAt(layout.indexOf(w)) w.close() self.refreshEntries() self.setUpdatesEnabled(True) if not self.signalsBlocked(): self.enterCompound(first, query) def currentJoiner(self): return self._currentJoiner def enterCompound(self, entry, query): # enter an existing compound if QueryCompound.typecheck(query): self.enterCompoundRequested.emit(entry, query) # create a new compound from the checked entries else: self.createCompoundFromChecked() def entries(self): """ Returns the entry widgets for this widget. :return [<XOrbQueryEntryWidget>, ..] """ layout = self._entryWidget.layout() output = [] for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() if not isinstance(widget, XOrbQueryEntryWidget): continue output.append(widget) return output def exitCompound(self): self.exitCompoundRequested.emit() def isNull(self): """ Returns whether or not any widgets have been defined for this container yet. :return <bool> """ return self._entryWidget.layout().count() <= 1 def moveDown(self, entry): """ Moves the current query down one entry. """ if not entry: return entries = self.entries() next = entries[entries.index(entry) + 1] entry_q = entry.query() next_q = next.query() next.setQuery(entry_q) entry.setQuery(next_q) def moveUp(self, entry): """ Moves the current query down up one entry. """ if not entry: return entries = self.entries() next = entries[entries.index(entry) - 1] entry_q = entry.query() next_q = next.query() next.setQuery(entry_q) entry.setQuery(next_q) def pluginFactory(self): """ Returns the plugin factory for this widget. You can define a custom factory for handling specific columns or column types based on your table type. :return <XOrbQueryPluginFactory> """ return self._queryWidget.pluginFactory() def query(self): """ Returns the query that is defined by this current panel. :return <orb.Query> """ joiner = self.currentJoiner() query = Query() for entry in self.entries(): if joiner == QueryCompound.Op.And: query &= entry.query() else: query |= entry.query() query.setName(self.uiNameTXT.text()) return query def queryWidget(self): """ Returns the query widget linked with this container. :return <XOrbQueryWidget> """ return self._queryWidget def removeEntry(self, entry): if not entry: return layout = self._entryWidget.layout() if layout.count() == 2: entry.setQuery(Query()) return layout.takeAt(layout.indexOf(entry)) entry.close() if not self.signalsBlocked(): self.entriesUpdated.emit() def refreshEntries(self): layout = self._entryWidget.layout() for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() widget.setFirst(i == 0) widget.setLast(i == (layout.count() - 2)) def setCurrentJoiner(self, joiner): self._currentJoiner = joiner layout = self._entryWidget.layout() for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() widget.setJoiner(joiner) def setQuery(self, query): """ Sets the query for this wigdet to the inputed query instance. :param query | <orb.Query> || <orb.QueryCompound> """ if not self.isNull() and hash(query) == hash(self.query()): return # add entries table = self.tableType() self.setUpdatesEnabled(False) self.blockSignals(True) self.clear() if query is None or table is None: self.setEnabled(False) self.setUpdatesEnabled(True) self.blockSignals(False) return else: self.setEnabled(True) # load the queries for this item if QueryCompound.typecheck(query): queries = query.queries() self.setCurrentJoiner(query.operatorType()) else: queries = [query] self.uiNameTXT.setText(query.name()) layout = self._entryWidget.layout() for index, query in enumerate(queries): widget = self.addEntry(query) widget.setFirst(index == 0) widget.setLast(index == (len(queries) - 1)) widget.setJoiner(self.currentJoiner()) self.setUpdatesEnabled(True) self.blockSignals(False) def setShowBack(self, state): # check to see if we're working on the current query self.uiBackBTN.setVisible(state) self.uiNameTXT.setVisible(state) def tableType(self): """ Returns the table type instance for this widget. :return <subclass of orb.Table> """ return self._queryWidget.tableType()
def __init__(self, parent=None): super(XNavigationEdit, self).__init__(parent) # define custom properties self._separator = '/' self._partsEditingEnabled = True self._originalText = '' self._scrollWidget = QScrollArea(self) self._partsWidget = QWidget(self._scrollWidget) self._buttonGroup = QButtonGroup(self) self._scrollAmount = 0 self._navigationModel = None # create the completer tree palette = self.palette() palette.setColor(palette.Base, palette.color(palette.Window)) palette.setColor(palette.Text, palette.color(palette.WindowText)) bg = palette.color(palette.Highlight) abg = bg.darker(115) fg = palette.color(palette.HighlightedText) sbg = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue()) sabg = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue()) sfg = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue()) style = 'QTreeView::item:hover { '\ ' color: %s;'\ ' background: qlineargradient(x1:0,'\ ' y1:0,'\ ' x2:0,'\ ' y2:1,'\ ' stop: 0 %s,'\ ' stop: 1 %s);'\ '}' % (sfg, sbg, sabg) self._completerTree = QTreeView(self) self._completerTree.setStyleSheet(style) self._completerTree.header().hide() self._completerTree.setFrameShape(QTreeView.Box) self._completerTree.setFrameShadow(QTreeView.Plain) self._completerTree.setPalette(palette) self._completerTree.setEditTriggers(QTreeView.NoEditTriggers) self._completerTree.setWindowFlags(Qt.Popup) self._completerTree.installEventFilter(self) self._completerTree.setRootIsDecorated(False) self._completerTree.setItemsExpandable(False) # create the editing widget layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addStretch() self._scrollWidget.setFrameShape(QScrollArea.NoFrame) self._scrollWidget.setFocusPolicy(Qt.NoFocus) self._scrollWidget.setWidget(self._partsWidget) self._scrollWidget.setWidgetResizable(True) self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight) self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setContentsMargins(0, 0, 0, 0) self._scrollWidget.setViewportMargins(0, 0, 0, 0) self._scrollWidget.move(2, 2) self._partsWidget.setLayout(layout) self._partsWidget.setCursor(Qt.ArrowCursor) self._partsWidget.setAutoFillBackground(True) self._partsWidget.setFixedHeight(self.height() - 12) palette = self._partsWidget.palette() palette.setColor(palette.Background, palette.color(palette.Base)) self._partsWidget.setPalette(palette) # create connections self._completerTree.clicked.connect(self.navigateToIndex) self._buttonGroup.buttonClicked.connect(self.handleButtonClick) self._scrollWidget.horizontalScrollBar().valueChanged.connect( self.scrollParts)
class XNavigationEdit(XLineEdit): """ """ navigationChanged = Signal() __designer_icon__ = projexui.resources.find('img/ui/navigate.png') def __init__(self, parent=None): super(XNavigationEdit, self).__init__(parent) # define custom properties self._separator = '/' self._partsEditingEnabled = True self._originalText = '' self._scrollWidget = QScrollArea(self) self._partsWidget = QWidget(self._scrollWidget) self._buttonGroup = QButtonGroup(self) self._scrollAmount = 0 self._navigationModel = None # create the completer tree palette = self.palette() palette.setColor(palette.Base, palette.color(palette.Window)) palette.setColor(palette.Text, palette.color(palette.WindowText)) bg = palette.color(palette.Highlight) abg = bg.darker(115) fg = palette.color(palette.HighlightedText) sbg = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue()) sabg = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue()) sfg = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue()) style = 'QTreeView::item:hover { '\ ' color: %s;'\ ' background: qlineargradient(x1:0,'\ ' y1:0,'\ ' x2:0,'\ ' y2:1,'\ ' stop: 0 %s,'\ ' stop: 1 %s);'\ '}' % (sfg, sbg, sabg) self._completerTree = QTreeView(self) self._completerTree.setStyleSheet(style) self._completerTree.header().hide() self._completerTree.setFrameShape(QTreeView.Box) self._completerTree.setFrameShadow(QTreeView.Plain) self._completerTree.setPalette(palette) self._completerTree.setEditTriggers(QTreeView.NoEditTriggers) self._completerTree.setWindowFlags(Qt.Popup) self._completerTree.installEventFilter(self) self._completerTree.setRootIsDecorated(False) self._completerTree.setItemsExpandable(False) # create the editing widget layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addStretch() self._scrollWidget.setFrameShape(QScrollArea.NoFrame) self._scrollWidget.setFocusPolicy(Qt.NoFocus) self._scrollWidget.setWidget(self._partsWidget) self._scrollWidget.setWidgetResizable(True) self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight) self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setContentsMargins(0, 0, 0, 0) self._scrollWidget.setViewportMargins(0, 0, 0, 0) self._scrollWidget.move(2, 2) self._partsWidget.setLayout(layout) self._partsWidget.setCursor(Qt.ArrowCursor) self._partsWidget.setAutoFillBackground(True) self._partsWidget.setFixedHeight(self.height() - 12) palette = self._partsWidget.palette() palette.setColor(palette.Background, palette.color(palette.Base)) self._partsWidget.setPalette(palette) # create connections self._completerTree.clicked.connect(self.navigateToIndex) self._buttonGroup.buttonClicked.connect(self.handleButtonClick) self._scrollWidget.horizontalScrollBar().valueChanged.connect( self.scrollParts) def acceptEdit(self): """ Accepts the current text and rebuilds the parts widget. """ if (self._partsWidget.isVisible()): return False use_completion = self.completer().popup().isVisible() completion = self.completer().currentCompletion() self._completerTree.hide() self.completer().popup().hide() if (use_completion): self.setText(completion) else: self.rebuild() return True def cancelEdit(self): """ Rejects the current edit and shows the parts widget. """ if (self._partsWidget.isVisible()): return False self._completerTree.hide() self.completer().popup().hide() self.setText(self._originalText) return True def currentItem(self): """ Returns the current navigation item from the current path. :return <XNavigationItem> || None """ model = self.navigationModel() if (not model): return None return model.itemByPath(self.text()) def eventFilter(self, object, event): """ Filters the events for the inputed object through this edit. :param object | <QObject> event | <QEvent> :return <bool> | consumed """ if (event.type() == event.KeyPress): if (event.key() == Qt.Key_Escape): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() elif (event.key() in (Qt.Key_Return, Qt.Key_Enter)): self.acceptEdit() return True elif (event.key() == Qt.Key_Tab): if (self.completer().popup().isVisible()): text = nativestring(self.completer().currentCompletion()) super(XNavigationEdit, self).setText(text) return True else: self.acceptEdit() return False elif (event.type() == event.MouseButtonPress): if (not self._completerTree.rect().contains(event.pos())): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() return False def focusOutEvent(self, event): """ Overloads the focus out event to cancel editing when the widget loses focus. :param event | <QFocusEvent> """ super(XNavigationEdit, self).focusOutEvent(event) self.cancelEdit() def handleButtonClick(self, button): """ Handle the event when a user clicks on one of the part buttons. :param button | <QToolButton> """ path = button.property('path') is_completer = button.property('is_completer') # popup a completion menu if (unwrapVariant(is_completer)): model = self.navigationModel() if (not model): return sep = self.separator() path = nativestring(unwrapVariant(path)) item = model.itemByPath(path, includeRoot=True) if (not item): return curr_path = nativestring(self.text()).strip(self.separator()) curr_path = curr_path.replace(path, '').strip(self.separator()) child_name = '' if (curr_path): child_name = curr_path.split(self.separator())[0] index = model.indexFromItem(item) self._completerTree.move(QCursor.pos()) self._completerTree.setRootIndex(index) self._completerTree.verticalScrollBar().setValue(0) if (child_name): child_item = None for i in range(item.rowCount()): child = item.child(i) if (child.text() == child_name): child_item = child break if (child_item): child_index = model.indexFromItem(child_item) self._completerTree.setCurrentIndex(child_index) self._completerTree.scrollTo(child_index) self._completerTree.show() self._completerTree.setUpdatesEnabled(True) else: self.setText(unwrapVariant(path)) def keyPressEvent(self, event): """ Overloads the key press event to listen for escape calls to cancel the parts editing. :param event | <QKeyPressEvent> """ if (self.scrollWidget().isHidden()): if (event.key() == Qt.Key_Escape): self.cancelEdit() return elif (event.key() in (Qt.Key_Return, Qt.Key_Enter)): self.acceptEdit() return elif (event.key() == Qt.Key_A and event.modifiers() == Qt.ControlModifier): self.startEdit() super(XNavigationEdit, self).keyPressEvent(event) def mouseDoubleClickEvent(self, event): """ Overloads the system to enable editing when a user double clicks. :param event | <QMouseEvent> """ super(XNavigationEdit, self).mouseDoubleClickEvent(event) self.startEdit() def navigationModel(self): """ Returns the navigation model linked with this edit. :return <XNavigationModel> || None """ return self._navigationModel def navigateToIndex(self, index): """ Navigates to the inputed action's path. :param action | <QAction> """ self._completerTree.hide() item = self._navigationModel.itemFromIndex(index) self.setText(self._navigationModel.itemPath(item)) def parts(self): """ Returns the parts that are used for this system. :return [<str>, ..] """ path = nativestring(self.text()).strip(self.separator()) if (not path): return [] return path.split(self.separator()) def partsWidget(self): """ Returns the widget that contains the parts system. :return <QScrollArea> """ return self._partsWidget def startEdit(self): """ Rebuilds the pathing based on the parts. """ self._originalText = self.text() self.scrollWidget().hide() self.setFocus() self.selectAll() 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() def resizeEvent(self, event): """ Resizes the current widget and its parts widget. :param event | <QResizeEvent> """ super(XNavigationEdit, self).resizeEvent(event) w = self.width() h = self.height() self._scrollWidget.resize(w - 4, h - 4) if (self._scrollWidget.width() < self._partsWidget.width()): self.scrollParts(self._partsWidget.width() - self._scrollWidget.width()) def scrollParts(self, amount): """ Scrolls the parts to offset the scrolling amount. :param amount | <int> """ change = self._scrollAmount - amount self._partsWidget.scroll(change, 0) self._scrollAmount = amount def scrollWidget(self): """ Returns the scrolling widget. :return <QScrollArea> """ return self._scrollWidget def separator(self): """ Returns the separation character that is used for this edit. :return <str> """ return self._separator def setTopLevelItems(self, items): """ Initializes the navigation system to start with the inputed root \ item. :param item | <XNavigationItem> """ if (not self._navigationModel): self.setNavigationModel(XNavigationModel(self)) self._navigationModel.setTopLevelItems(items) def setNavigationModel(self, model): """ Sets the navigation model for this edit. :param model | <XNavigationModel> """ self._navigationModel = model self._completerTree.setModel(model) if (model): model.setSeparator(self.separator()) completer = XNavigationCompleter(model, self) self.setCompleter(completer) completer.popup().installEventFilter(self) else: self.setCompleter(None) self.rebuild() def setParts(self, parts): """ Sets the path for this edit widget by providing the parts to the path. :param parts | [<str>, ..] """ self.setText(self.separator().join(map(str, parts))) def setSeparator(self, separator): """ Sets the separator to the inputed character. :param separator | <str> """ self._separator = separator if (self._navigationModel): self._navigationModel.setSeparator(separator) self.rebuild() def setText(self, text): """ Sets the text for this edit to the inputed text. :param text | <str> """ super(XNavigationEdit, self).setText(text) self.scrollWidget().show() if (text == '' or self._originalText != text): self.rebuild()
class XOrbQueryContainer(QWidget): """ """ entriesUpdated = Signal() enterCompoundRequested = Signal(object, object) exitCompoundRequested = Signal() def __init__(self, parent=None): super(XOrbQueryContainer, self).__init__(parent) # load the user interface projexui.loadUi(__file__, self) # define custom properties self._queryWidget = parent self._entryWidget = QWidget(self) self._currentJoiner = QueryCompound.Op.And layout = QVBoxLayout() layout.addStretch(1) layout.setSpacing(3) self._entryWidget.setLayout(layout) self.uiQueryAREA.setWidget(self._entryWidget) # set default properties self.setContextMenuPolicy(Qt.CustomContextMenu) # create connections (use old-style syntax or PySide errors) self.connect(self.uiBackBTN, SIGNAL('clicked()'), self.exitCompound) self.entriesUpdated.connect(self.refreshEntries) def addEntry(self, query=None, entry=None): if query is None: query = Query() layout = self._entryWidget.layout() index = layout.count() - 1 if entry: index = layout.indexOf(entry) + 1 widget = XOrbQueryEntryWidget(self, self.tableType()) layout.insertWidget(index, widget) widget.setQuery(query) if not self.signalsBlocked(): self.entriesUpdated.emit() return widget def checkedEntries(self): """ Returns the widgets that are checked for this widget. :return [<XOrbQueryEntryWidget>, ..] """ return [entry for entry in self.entries() if entry.isChecked()] def clear(self): """ Clears out the widgets for this query builder. """ layout = self._entryWidget.layout() for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() widget.close() def createCompoundFromChecked(self): """ Creates a new compound query from the checked entry list. :return <orb.QueryCompound> """ checked_entries = self.checkedEntries() if len(checked_entries) <= 1: return QueryCompound() self.setUpdatesEnabled(False) joiner = self.currentJoiner() query = Query() for entry in checked_entries: if joiner == QueryCompound.Op.And: query &= entry.query() else: query |= entry.query() # clear out the existing containers first = checked_entries[0] first.setQuery(query) first.setChecked(False) layout = self._entryWidget.layout() for i in range(len(checked_entries) - 1, 0, -1): w = checked_entries[i] layout.takeAt(layout.indexOf(w)) w.close() self.refreshEntries() self.setUpdatesEnabled(True) if not self.signalsBlocked(): self.enterCompound(first, query) def currentJoiner(self): return self._currentJoiner def enterCompound(self, entry, query): # enter an existing compound if QueryCompound.typecheck(query): self.enterCompoundRequested.emit(entry, query) # create a new compound from the checked entries else: self.createCompoundFromChecked() def entries(self): """ Returns the entry widgets for this widget. :return [<XOrbQueryEntryWidget>, ..] """ layout = self._entryWidget.layout() output = [] for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() if not isinstance(widget, XOrbQueryEntryWidget): continue output.append(widget) return output def exitCompound(self): self.exitCompoundRequested.emit() def isNull(self): """ Returns whether or not any widgets have been defined for this container yet. :return <bool> """ return self._entryWidget.layout().count() <= 1 def moveDown(self, entry): """ Moves the current query down one entry. """ if not entry: return entries = self.entries() next = entries[entries.index(entry) + 1] entry_q = entry.query() next_q = next.query() next.setQuery(entry_q) entry.setQuery(next_q) def moveUp(self, entry): """ Moves the current query down up one entry. """ if not entry: return entries = self.entries() next = entries[entries.index(entry) - 1] entry_q = entry.query() next_q = next.query() next.setQuery(entry_q) entry.setQuery(next_q) def pluginFactory(self): """ Returns the plugin factory for this widget. You can define a custom factory for handling specific columns or column types based on your table type. :return <XOrbQueryPluginFactory> """ return self._queryWidget.pluginFactory() def query(self): """ Returns the query that is defined by this current panel. :return <orb.Query> """ joiner = self.currentJoiner() query = Query() for entry in self.entries(): if joiner == QueryCompound.Op.And: query &= entry.query() else: query |= entry.query() query.setName(self.uiNameTXT.text()) return query def queryWidget(self): """ Returns the query widget linked with this container. :return <XOrbQueryWidget> """ return self._queryWidget def removeEntry(self, entry): if not entry: return layout = self._entryWidget.layout() if layout.count() == 2: entry.setQuery(Query()) return layout.takeAt(layout.indexOf(entry)) entry.close() if not self.signalsBlocked(): self.entriesUpdated.emit() def refreshEntries(self): layout = self._entryWidget.layout() for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() widget.setFirst(i == 0) widget.setLast(i == (layout.count() - 2)) def setCurrentJoiner(self, joiner): self._currentJoiner = joiner layout = self._entryWidget.layout() for i in range(layout.count() - 1): widget = layout.itemAt(i).widget() widget.setJoiner(joiner) def setQuery(self, query): """ Sets the query for this wigdet to the inputed query instance. :param query | <orb.Query> || <orb.QueryCompound> """ if not self.isNull() and hash(query) == hash(self.query()): return # add entries table = self.tableType() self.setUpdatesEnabled(False) self.blockSignals(True) self.clear() if query is None or table is None: self.setEnabled(False) self.setUpdatesEnabled(True) self.blockSignals(False) return else: self.setEnabled(True) # load the queries for this item if QueryCompound.typecheck(query): queries = query.queries() self.setCurrentJoiner(query.operatorType()) else: queries = [query] self.uiNameTXT.setText(query.name()) layout = self._entryWidget.layout() for index, query in enumerate(queries): widget = self.addEntry(query) widget.setFirst(index == 0) widget.setLast(index == (len(queries) - 1)) widget.setJoiner(self.currentJoiner()) self.setUpdatesEnabled(True) self.blockSignals(False) def setShowBack(self, state): # check to see if we're working on the current query self.uiBackBTN.setVisible(state) self.uiNameTXT.setVisible(state) def tableType(self): """ Returns the table type instance for this widget. :return <subclass of orb.Table> """ return self._queryWidget.tableType()
def __init__( self, parent = None ): super(XNavigationEdit, self).__init__( parent ) # define custom properties self._separator = '/' self._partsEditingEnabled = True self._originalText = '' self._scrollWidget = QScrollArea(self) self._partsWidget = QWidget(self._scrollWidget) self._buttonGroup = QButtonGroup(self) self._scrollAmount = 0 self._navigationModel = None # create the completer tree palette = self.palette() palette.setColor(palette.Base, palette.color(palette.Window)) palette.setColor(palette.Text, palette.color(palette.WindowText)) bg = palette.color(palette.Highlight) abg = bg.darker(115) fg = palette.color(palette.HighlightedText) sbg = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue()) sabg = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue()) sfg = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue()) style = 'QTreeView::item:hover { '\ ' color: %s;'\ ' background: qlineargradient(x1:0,'\ ' y1:0,'\ ' x2:0,'\ ' y2:1,'\ ' stop: 0 %s,'\ ' stop: 1 %s);'\ '}' % (sfg, sbg, sabg) self._completerTree = QTreeView(self) self._completerTree.setStyleSheet(style) self._completerTree.header().hide() self._completerTree.setFrameShape(QTreeView.Box) self._completerTree.setFrameShadow(QTreeView.Plain) self._completerTree.setPalette(palette) self._completerTree.setEditTriggers(QTreeView.NoEditTriggers) self._completerTree.setWindowFlags(Qt.Popup) self._completerTree.installEventFilter(self) self._completerTree.setRootIsDecorated(False) self._completerTree.setItemsExpandable(False) # create the editing widget layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addStretch() self._scrollWidget.setFrameShape( QScrollArea.NoFrame ) self._scrollWidget.setFocusPolicy(Qt.NoFocus) self._scrollWidget.setWidget(self._partsWidget) self._scrollWidget.setWidgetResizable(True) self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight) self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setContentsMargins(0, 0, 0, 0) self._scrollWidget.setViewportMargins(0, 0, 0, 0) self._scrollWidget.move(2, 2) self._partsWidget.setLayout(layout) self._partsWidget.setCursor(Qt.ArrowCursor) self._partsWidget.setAutoFillBackground(True) self._partsWidget.setFixedHeight(self.height() - 12) palette = self._partsWidget.palette() palette.setColor(palette.Background, palette.color(palette.Base)) self._partsWidget.setPalette(palette) # create connections self._completerTree.clicked.connect( self.navigateToIndex ) self._buttonGroup.buttonClicked.connect( self.handleButtonClick ) self._scrollWidget.horizontalScrollBar().valueChanged.connect( self.scrollParts )
class XNavigationEdit(XLineEdit): """ """ navigationChanged = Signal() __designer_icon__ = projexui.resources.find('img/ui/navigate.png') def __init__( self, parent = None ): super(XNavigationEdit, self).__init__( parent ) # define custom properties self._separator = '/' self._partsEditingEnabled = True self._originalText = '' self._scrollWidget = QScrollArea(self) self._partsWidget = QWidget(self._scrollWidget) self._buttonGroup = QButtonGroup(self) self._scrollAmount = 0 self._navigationModel = None # create the completer tree palette = self.palette() palette.setColor(palette.Base, palette.color(palette.Window)) palette.setColor(palette.Text, palette.color(palette.WindowText)) bg = palette.color(palette.Highlight) abg = bg.darker(115) fg = palette.color(palette.HighlightedText) sbg = 'rgb(%s, %s, %s)' % (bg.red(), bg.green(), bg.blue()) sabg = 'rgb(%s, %s, %s)' % (abg.red(), abg.green(), abg.blue()) sfg = 'rgb(%s, %s, %s)' % (fg.red(), fg.green(), fg.blue()) style = 'QTreeView::item:hover { '\ ' color: %s;'\ ' background: qlineargradient(x1:0,'\ ' y1:0,'\ ' x2:0,'\ ' y2:1,'\ ' stop: 0 %s,'\ ' stop: 1 %s);'\ '}' % (sfg, sbg, sabg) self._completerTree = QTreeView(self) self._completerTree.setStyleSheet(style) self._completerTree.header().hide() self._completerTree.setFrameShape(QTreeView.Box) self._completerTree.setFrameShadow(QTreeView.Plain) self._completerTree.setPalette(palette) self._completerTree.setEditTriggers(QTreeView.NoEditTriggers) self._completerTree.setWindowFlags(Qt.Popup) self._completerTree.installEventFilter(self) self._completerTree.setRootIsDecorated(False) self._completerTree.setItemsExpandable(False) # create the editing widget layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addStretch() self._scrollWidget.setFrameShape( QScrollArea.NoFrame ) self._scrollWidget.setFocusPolicy(Qt.NoFocus) self._scrollWidget.setWidget(self._partsWidget) self._scrollWidget.setWidgetResizable(True) self._scrollWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setAlignment(Qt.AlignTop | Qt.AlignRight) self._scrollWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._scrollWidget.setContentsMargins(0, 0, 0, 0) self._scrollWidget.setViewportMargins(0, 0, 0, 0) self._scrollWidget.move(2, 2) self._partsWidget.setLayout(layout) self._partsWidget.setCursor(Qt.ArrowCursor) self._partsWidget.setAutoFillBackground(True) self._partsWidget.setFixedHeight(self.height() - 12) palette = self._partsWidget.palette() palette.setColor(palette.Background, palette.color(palette.Base)) self._partsWidget.setPalette(palette) # create connections self._completerTree.clicked.connect( self.navigateToIndex ) self._buttonGroup.buttonClicked.connect( self.handleButtonClick ) self._scrollWidget.horizontalScrollBar().valueChanged.connect( self.scrollParts ) def acceptEdit( self ): """ Accepts the current text and rebuilds the parts widget. """ if ( self._partsWidget.isVisible() ): return False use_completion = self.completer().popup().isVisible() completion = self.completer().currentCompletion() self._completerTree.hide() self.completer().popup().hide() if ( use_completion ): self.setText(completion) else: self.rebuild() return True def cancelEdit( self ): """ Rejects the current edit and shows the parts widget. """ if ( self._partsWidget.isVisible() ): return False self._completerTree.hide() self.completer().popup().hide() self.setText(self._originalText) return True def currentItem( self ): """ Returns the current navigation item from the current path. :return <XNavigationItem> || None """ model = self.navigationModel() if ( not model ): return None return model.itemByPath(self.text()) def eventFilter( self, object, event ): """ Filters the events for the inputed object through this edit. :param object | <QObject> event | <QEvent> :return <bool> | consumed """ if ( event.type() == event.KeyPress ): if ( event.key() == Qt.Key_Escape ): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): self.acceptEdit() return True elif ( event.key() == Qt.Key_Tab ): if ( self.completer().popup().isVisible() ): text = nativestring(self.completer().currentCompletion()) super(XNavigationEdit, self).setText(text) return True else: self.acceptEdit() return False elif ( event.type() == event.MouseButtonPress ): if ( not self._completerTree.rect().contains(event.pos()) ): self._completerTree.hide() self.completer().popup().hide() self.cancelEdit() return False def focusOutEvent( self, event ): """ Overloads the focus out event to cancel editing when the widget loses focus. :param event | <QFocusEvent> """ super(XNavigationEdit, self).focusOutEvent(event) self.cancelEdit() def handleButtonClick( self, button ): """ Handle the event when a user clicks on one of the part buttons. :param button | <QToolButton> """ path = button.property('path') is_completer = button.property('is_completer') # popup a completion menu if ( unwrapVariant(is_completer) ): model = self.navigationModel() if ( not model ): return sep = self.separator() path = nativestring(unwrapVariant(path)) item = model.itemByPath(path, includeRoot = True) if ( not item ): return curr_path = nativestring(self.text()).strip(self.separator()) curr_path = curr_path.replace(path, '').strip(self.separator()) child_name = '' if ( curr_path ): child_name = curr_path.split(self.separator())[0] index = model.indexFromItem(item) self._completerTree.move(QCursor.pos()) self._completerTree.setRootIndex(index) self._completerTree.verticalScrollBar().setValue(0) if ( child_name ): child_item = None for i in range(item.rowCount()): child = item.child(i) if ( child.text() == child_name ): child_item = child break if ( child_item ): child_index = model.indexFromItem(child_item) self._completerTree.setCurrentIndex(child_index) self._completerTree.scrollTo(child_index) self._completerTree.show() self._completerTree.setUpdatesEnabled(True) else: self.setText(unwrapVariant(path)) def keyPressEvent( self, event ): """ Overloads the key press event to listen for escape calls to cancel the parts editing. :param event | <QKeyPressEvent> """ if ( self.scrollWidget().isHidden() ): if ( event.key() == Qt.Key_Escape ): self.cancelEdit() return elif ( event.key() in (Qt.Key_Return, Qt.Key_Enter) ): self.acceptEdit() return elif ( event.key() == Qt.Key_A and event.modifiers() == Qt.ControlModifier ): self.startEdit() super(XNavigationEdit, self).keyPressEvent(event) def mouseDoubleClickEvent( self, event ): """ Overloads the system to enable editing when a user double clicks. :param event | <QMouseEvent> """ super(XNavigationEdit, self).mouseDoubleClickEvent(event) self.startEdit() def navigationModel( self ): """ Returns the navigation model linked with this edit. :return <XNavigationModel> || None """ return self._navigationModel def navigateToIndex( self, index ): """ Navigates to the inputed action's path. :param action | <QAction> """ self._completerTree.hide() item = self._navigationModel.itemFromIndex(index) self.setText(self._navigationModel.itemPath(item)) def parts( self ): """ Returns the parts that are used for this system. :return [<str>, ..] """ path = nativestring(self.text()).strip(self.separator()) if ( not path ): return [] return path.split(self.separator()) def partsWidget( self ): """ Returns the widget that contains the parts system. :return <QScrollArea> """ return self._partsWidget def startEdit( self ): """ Rebuilds the pathing based on the parts. """ self._originalText = self.text() self.scrollWidget().hide() self.setFocus() self.selectAll() 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() def resizeEvent( self, event ): """ Resizes the current widget and its parts widget. :param event | <QResizeEvent> """ super(XNavigationEdit, self).resizeEvent(event) w = self.width() h = self.height() self._scrollWidget.resize(w - 4, h - 4) if ( self._scrollWidget.width() < self._partsWidget.width() ): self.scrollParts( self._partsWidget.width() - self._scrollWidget.width() ) def scrollParts( self, amount ): """ Scrolls the parts to offset the scrolling amount. :param amount | <int> """ change = self._scrollAmount - amount self._partsWidget.scroll(change, 0) self._scrollAmount = amount def scrollWidget( self ): """ Returns the scrolling widget. :return <QScrollArea> """ return self._scrollWidget def separator( self ): """ Returns the separation character that is used for this edit. :return <str> """ return self._separator def setTopLevelItems( self, items ): """ Initializes the navigation system to start with the inputed root \ item. :param item | <XNavigationItem> """ if ( not self._navigationModel ): self.setNavigationModel(XNavigationModel(self)) self._navigationModel.setTopLevelItems(items) def setNavigationModel( self, model ): """ Sets the navigation model for this edit. :param model | <XNavigationModel> """ self._navigationModel = model self._completerTree.setModel(model) if ( model ): model.setSeparator(self.separator()) completer = XNavigationCompleter(model, self) self.setCompleter(completer) completer.popup().installEventFilter(self) else: self.setCompleter(None) self.rebuild() def setParts( self, parts ): """ Sets the path for this edit widget by providing the parts to the path. :param parts | [<str>, ..] """ self.setText(self.separator().join(map(str, parts))) def setSeparator( self, separator ): """ Sets the separator to the inputed character. :param separator | <str> """ self._separator = separator if ( self._navigationModel ): self._navigationModel.setSeparator(separator) self.rebuild() def setText( self, text ): """ Sets the text for this edit to the inputed text. :param text | <str> """ super(XNavigationEdit, self).setText(text) self.scrollWidget().show() if ( text == '' or self._originalText != text ): self.rebuild()
class XQueryBuilderWidget(QWidget): """ """ saveRequested = Signal() resetRequested = Signal() cancelRequested = Signal() def __init__( self, parent = None ): super(XQueryBuilderWidget, self).__init__( parent ) # load the user interface projexui.loadUi(__file__, self) self.setMinimumWidth(470) # define custom properties self._rules = {} self._defaultQuery = [] self._completionTerms = [] self._minimumCount = 1 # set default properties self._container = QWidget(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) layout.addStretch(1) self._container.setLayout(layout) self.uiQueryAREA.setWidget(self._container) # create connections self.uiResetBTN.clicked.connect( self.emitResetRequested ) self.uiSaveBTN.clicked.connect( self.emitSaveRequested ) self.uiCancelBTN.clicked.connect( self.emitCancelRequested ) self.resetRequested.connect( self.reset ) def addLineWidget( self, query = None ): """ Adds a new line widget to the system with the given values. :param query | (<str> term, <str> operator, <str> vlaue) || None """ widget = XQueryLineWidget(self) widget.setTerms(sorted(self._rules.keys())) widget.setQuery(query) index = self._container.layout().count() - 1 self._container.layout().insertWidget(index, widget) widget.addRequested.connect( self.addLineWidget ) widget.removeRequested.connect( self.removeLineWidget ) # update the remove enabled options for these widgets self.updateRemoveEnabled() def addRule( self, rule ): """ Adds a rule to the system. :param rule | <XQueryRule> """ self._rules[rule.term()] = rule self.updateRules() def clear( self ): """ Clears out all the widgets from the system. """ for lineWidget in self.lineWidgets(): lineWidget.setParent(None) lineWidget.deleteLater() def completionTerms( self ): """ Returns the list of terms that will be used as a global override for completion terms when the query rule generates a QLineEdit instance. :return [<str>, ..] """ return self._completionTerms def count( self ): """ Returns the count of the line widgets in the system. :return <int> """ return len(self.lineWidgets()) def currentQuery( self ): """ Returns the current query string for this widget. :return [(<str> term, <str> operator, <str> value), ..] """ widgets = self.lineWidgets() output = [] for widget in widgets: output.append(widget.query()) return output def defaultQuery( self ): """ Returns the default query for the system. :return [(<str> term, <str> operator, <str> value), ..] """ return self._defaultQuery def keyPressEvent( self, event ): """ Emits the save requested signal for this builder for when the enter or return press is clicked. :param event | <QKeyEvent> """ if ( event.key() in (Qt.Key_Enter, Qt.Key_Return) ): self.emitSaveRequested() super(XQueryBuilderWidget, self).keyPressEvent(event) def emitCancelRequested( self ): """ Emits the cancel requested signal. """ if ( not self.signalsBlocked() ): self.cancelRequested.emit() def emitResetRequested( self ): """ Emits the reste requested signal. """ if ( not self.signalsBlocked() ): self.resetRequested.emit() def emitSaveRequested( self ): """ Emits the save requested signal. """ if ( not self.signalsBlocked() ): self.saveRequested.emit() def findRule( self, term ): """ Looks up a rule by the inputed term. :param term | <str> :return <XQueryRule> || None """ return self._rules.get(nativestring(term)) def removeLineWidget( self, widget ): """ Removes the line widget from the query. :param widget | <XQueryLineWidget> """ widget.setParent(None) widget.deleteLater() self.updateRemoveEnabled() def minimumCount( self ): """ Defines the minimum number of query widgets that are allowed. :return <int> """ return self._minimumCount def lineWidgets( self ): """ Returns a list of line widgets for this system. :return [<XQueryLineWidget>, ..] """ return self.findChildren(XQueryLineWidget) def reset( self ): """ Resets the system to the default query. """ self.setCurrentQuery(self.defaultQuery()) def setCompletionTerms( self, terms ): """ Sets the list of terms that will be used as a global override for completion terms when the query rule generates a QLineEdit instance. :param terms | [<str>, ..] """ self._completionTerms = terms def setCurrentQuery( self, query ): """ Sets the query for this system to the inputed query. :param query | [(<str> term, <str> operator, <str> value), ..] """ self.clear() for entry in query: self.addLineWidget(entry) # make sure we have the minimum number of widgets for i in range(self.minimumCount() - len(query)): self.addLineWidget() def setDefaultQuery( self, query ): """ Sets the default query that will be used when the user clicks on the \ reset button or the reset method is called. :param query | [(<str> term, <str> operator, <str> value), ..] """ self._defaultQuery = query[:] def setMinimumCount( self, count ): """ Sets the minimum number of line widgets that are allowed at any \ given time. :param count | <int> """ self._minimumCount = count def setRules( self, rules ): """ Sets all the rules for this builder. :param rules | [<XQueryRule>, ..] """ if ( type(rules) in (list, tuple) ): self._rules = dict([(x.term(), x) for x in rules]) self.updateRules() return True elif ( type(rules) == dict ): self._rules = rules.copy() self.updateRules() return True else: return False def setTerms( self, terms ): """ Sets a simple rule list by accepting a list of strings for terms. \ This is a convenience method for the setRules method. :param rules | [<str> term, ..] """ return self.setRules([XQueryRule(term = term) for term in terms]) def updateRemoveEnabled( self ): """ Updates the remove enabled baesd on the current number of line widgets. """ lineWidgets = self.lineWidgets() count = len(lineWidgets) state = self.minimumCount() < count for widget in lineWidgets: widget.setRemoveEnabled(state) def updateRules( self ): """ Updates the query line items to match the latest rule options. """ terms = sorted(self._rules.keys()) for child in self.lineWidgets(): child.setTerms(terms)