Example #1
0
 def loadRecords(self, records):
     """
     Loads the inputed records as children to this item.
     
     :param      records | [<orb.Table>, ..] || {<str> sub: <variant>, .. }
     """
     self.setChildIndicatorPolicy(self.DontShowIndicatorWhenChildless)
     self._loaded = True
     
     if records is None:
         return
     
     # load sub-groups if desired
     if self._nextLevels and RecordSet.typecheck(records):
         level = self._nextLevels[0]
         sublevels = self._nextLevels[1:]
         
         records = records.grouped(level)
         
     elif RecordSet.typecheck(records):
         sublevels = None
         records = records.all()
     
     else:
         sublevels = None
     
     # load a child set of groups
     if type(records) == dict:
         try:
             generator = self.treeWidget().createGroupItem
             cls = None
         except AttributeError:
             generator = None
             cls = type(self)
             
         for subgroup, subrecords in records.items():
             if generator:
                 generator(subgroup, subrecords, sublevels, self)
             elif cls:
                 cls(self, subgroup, subrecords, sublevels)
     
     # load records
     else:
         try:
             generator = self.treeWidget().createRecordItem
             cls = None
         except AttributeError:
             generator = None
             cls = XOrbRecordItem
         
         cls = self.treeWidget().createRecordItem
         for record in records:
             if generator:
                 generator(record, self)
             elif cls:
                 cls(self, record)
Example #2
0
    def loadRecords(self, records):
        """
        Loads the inputed records as children to this item.
        
        :param      records | [<orb.Table>, ..] || {<str> sub: <variant>, .. }
        """
        self.setChildIndicatorPolicy(self.DontShowIndicatorWhenChildless)
        self._loaded = True

        if records is None:
            return

        # load sub-groups if desired
        if self._nextLevels and RecordSet.typecheck(records):
            level = self._nextLevels[0]
            sublevels = self._nextLevels[1:]

            records = records.grouped(level)

        elif RecordSet.typecheck(records):
            sublevels = None
            records = records.all()

        else:
            sublevels = None

        # load a child set of groups
        if type(records) == dict:
            try:
                generator = self.treeWidget().createGroupItem
                cls = None
            except AttributeError:
                generator = None
                cls = type(self)

            for subgroup, subrecords in records.items():
                if generator:
                    generator(subgroup, subrecords, sublevels, self)
                elif cls:
                    cls(self, subgroup, subrecords, sublevels)

        # load records
        else:
            try:
                generator = self.treeWidget().createRecordItem
                cls = None
            except AttributeError:
                generator = None
                cls = XOrbRecordItem

            cls = self.treeWidget().createRecordItem
            for record in records:
                if generator:
                    generator(record, self)
                elif cls:
                    cls(self, record)
Example #3
0
    def __init__(self, parent=None):
        super(XOrbBrowserWidget, self).__init__(parent)

        # load the user interface
        projexui.loadUi(__file__, self)

        # define custom properties
        self._hint = ''
        self._query = Q()
        self._advancedGrouping = []
        self._records = RecordSet()
        self._groupBy = XOrbBrowserWidget.GroupByAdvancedKey
        self._factory = XOrbBrowserFactory()
        self._queryWidget = XOrbQueryWidget(self, self._factory)
        self._thumbnailSize = QSize(128, 128)

        # set default properties
        self.uiSearchTXT.addButton(self.uiQueryBTN)
        self.uiQueryBTN.setCentralWidget(self._queryWidget)
        self.uiThumbLIST.installEventFilter(self)

        self.uiQueryACT.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.uiQueryBTN.setDefaultAction(self.uiQueryACT)

        self.uiViewModeWGT.addAction(self.uiDetailsACT)
        self.uiViewModeWGT.addAction(self.uiCardACT)
        self.uiViewModeWGT.addAction(self.uiThumbnailACT)

        # create connections
        self.uiGroupOptionsBTN.clicked.connect(self.showGroupMenu)
        self.uiSearchTXT.returnPressed.connect(self.refresh)
        self.queryChanged.connect(self.refresh)
        self.uiGroupBTN.toggled.connect(self.refreshResults)

        self.uiDetailsACT.triggered.connect(self.setDetailMode)
        self.uiCardACT.triggered.connect(self.setCardMode)
        self.uiThumbnailACT.triggered.connect(self.setThumbnailMode)

        self.uiQueryBTN.popupAboutToShow.connect(self.prepareQuery)
        self.uiQueryBTN.popupAccepted.connect(self.acceptQuery)
        self.uiQueryBTN.popupReset.connect(self.resetQuery)

        self.uiRefreshBTN.clicked.connect(self.refresh)

        self.uiRecordsTREE.itemDoubleClicked.connect(self.handleDetailDblClick)
        self.uiRecordsTREE.currentItemChanged.connect(
            self.emitCurrentRecordChanged)

        self.uiThumbLIST.itemDoubleClicked.connect(self.handleThumbDblClick)
        self.uiThumbLIST.currentItemChanged.connect(
            self.emitCurrentRecordChanged)

        self.uiCardTREE.itemDoubleClicked.connect(self.handleCardDblClick)
        self.uiCardTREE.currentItemChanged.connect(
            self.emitCurrentRecordChanged)
Example #4
0
 def childRecords(self):
     """
     Returns a record set of children for this item based on the record.  If
     no record set is manually set for this instance, then it will use the
     hierarchyColumn value from the tree widget with this record.  If no
     hierarchyColumn is speified, then a blank record set is returned.
     
     :return     <orb.RecordSet>
     """
     if self._childRecords is not None:
         return self._childRecords
     
     tree = self.treeWidget()
     try:
         table, column = tree.hierarchyLookup(self.record())
     except AttributeError:
         table = None
         column = ''
     
     # returns the children for this information
     if table and column:
         return table.select(where=Q(column) == self.record())
     
     # returns a blank record set if no other records can be found
     return RecordSet()
Example #5
0
 def refresh(self, records):
     """
     Refreshs the current user interface to match the latest settings.
     """
     self._loaded = True
     
     if self.isLoading():
         return
     
     # load the information
     if RecordSet.typecheck(records):
         table = records.table()
         self.setTableType(table)
         
         if self.order():
             records.setOrder(self.order())
         
         # load specific data for this record box
         if self.specifiedColumnsOnly():
             records.setColumns(map(lambda x: x.name(),
                                    self.specifiedColumns()))
         
         # load the records asynchronously
         if self.isThreadEnabled() and \
            table and \
            table.getDatabase().isThreadEnabled():
             # assign ordering based on tree table
             if self.showTreePopup():
                 tree = self.treePopupWidget()
                 if tree.isSortingEnabled():
                     col = tree.sortColumn()
                     colname = tree.headerItem().text(col)
                     column = table.schema().column(colname)
                     
                     if column:
                         if tree.sortOrder() == Qt.AscendingOrder:
                             sort_order = 'asc'
                         else:
                             sort_order = 'desc'
                         
                         records.setOrder([(column.name(), sort_order)])
             
             self.loadRequested.emit(records)
             return
     
     # load the records synchronously
     self.loadingStarted.emit()
     curr_record = self.currentRecord()
     self.blockSignals(True)
     self.setUpdatesEnabled(False)
     self.clear()
     use_dummy = not self.isRequired() or self.isCheckable()
     if use_dummy:
         self.addItem('')
     self.addRecords(records)
     self.setUpdatesEnabled(True)
     self.blockSignals(False)
     self.setCurrentRecord(curr_record)
     self.loadingFinished.emit()
Example #6
0
    def refresh(self, records):
        """
        Refreshs the current user interface to match the latest settings.
        """
        self._loaded = True

        if self.isLoading():
            return

        # load the information
        if RecordSet.typecheck(records):
            table = records.table()
            self.setTableType(table)

            if self.order():
                records.setOrder(self.order())

            # load specific data for this record box
            if self.specifiedColumnsOnly():
                records.setColumns(
                    map(lambda x: x.name(), self.specifiedColumns()))

            # load the records asynchronously
            if self.isThreadEnabled() and \
               table and \
               table.getDatabase().isThreadEnabled():
                # assign ordering based on tree table
                if self.showTreePopup():
                    tree = self.treePopupWidget()
                    if tree.isSortingEnabled():
                        col = tree.sortColumn()
                        colname = tree.headerItem().text(col)
                        column = table.schema().column(colname)

                        if column:
                            if tree.sortOrder() == Qt.AscendingOrder:
                                sort_order = 'asc'
                            else:
                                sort_order = 'desc'

                            records.setOrder([(column.name(), sort_order)])

                self.loadRequested.emit(records)
                return

        # load the records synchronously
        self.loadingStarted.emit()
        curr_record = self.currentRecord()
        self.blockSignals(True)
        self.setUpdatesEnabled(False)
        self.clear()
        use_dummy = not self.isRequired() or self.isCheckable()
        if use_dummy:
            self.addItem('')
        self.addRecords(records)
        self.setUpdatesEnabled(True)
        self.blockSignals(False)
        self.setCurrentRecord(curr_record)
        self.loadingFinished.emit()
Example #7
0
    def refreshRecords(self):
        """
        Refreshes the records being loaded by this browser.
        """
        table_type = self.tableType()
        if (not table_type):
            self._records = RecordSet()
            return False

        search = str(self.uiSearchTXT.text())

        query = self.query().copy()
        terms, search_query = Q.fromSearch(search)

        if (search_query):
            query &= search_query

        self._records = table_type.select(where=query).search(terms)
        return True
 def __init__( self, parent = None ):
     super(XOrbBrowserWidget, self).__init__( parent )
     
     # load the user interface
     projexui.loadUi(__file__, self)
     
     # define custom properties
     self._hint              = ''
     self._query             = Q()
     self._advancedGrouping  = []
     self._records           = RecordSet()
     self._groupBy           = XOrbBrowserWidget.GroupByAdvancedKey
     self._factory           = XOrbBrowserFactory()
     self._queryWidget       = XOrbQueryWidget(self, self._factory)
     self._thumbnailSize     = QSize(128, 128)
     
     # set default properties
     self.uiSearchTXT.addButton(self.uiQueryBTN)
     self.uiQueryBTN.setCentralWidget(self._queryWidget)
     self.uiThumbLIST.installEventFilter(self)
     
     self.uiQueryACT.setShortcutContext(Qt.WidgetWithChildrenShortcut)
     self.uiQueryBTN.setDefaultAction(self.uiQueryACT)
     
     self.uiViewModeWGT.addAction(self.uiDetailsACT)
     self.uiViewModeWGT.addAction(self.uiCardACT)
     self.uiViewModeWGT.addAction(self.uiThumbnailACT)
     
     # create connections
     self.uiGroupOptionsBTN.clicked.connect(self.showGroupMenu)
     self.uiSearchTXT.returnPressed.connect(self.refresh)
     self.queryChanged.connect(self.refresh)
     self.uiGroupBTN.toggled.connect(self.refreshResults)
     
     self.uiDetailsACT.triggered.connect(self.setDetailMode)
     self.uiCardACT.triggered.connect(self.setCardMode)
     self.uiThumbnailACT.triggered.connect(self.setThumbnailMode)
     
     self.uiQueryBTN.popupAboutToShow.connect(self.prepareQuery)
     self.uiQueryBTN.popupAccepted.connect(self.acceptQuery)
     self.uiQueryBTN.popupReset.connect(self.resetQuery)
     
     self.uiRefreshBTN.clicked.connect(self.refresh)
     
     self.uiRecordsTREE.itemDoubleClicked.connect(self.handleDetailDblClick)
     self.uiRecordsTREE.currentItemChanged.connect(
                                              self.emitCurrentRecordChanged)
     
     self.uiThumbLIST.itemDoubleClicked.connect(self.handleThumbDblClick)
     self.uiThumbLIST.currentItemChanged.connect(
                                             self.emitCurrentRecordChanged)
     
     self.uiCardTREE.itemDoubleClicked.connect(self.handleCardDblClick)
     self.uiCardTREE.currentItemChanged.connect(
                                             self.emitCurrentRecordChanged)
 def refreshRecords( self ):
     """
     Refreshes the records being loaded by this browser.
     """
     table_type = self.tableType()
     if ( not table_type ):
         self._records = RecordSet()
         return False
     
     search = str(self.uiSearchTXT.text())
     
     query = self.query().copy()
     terms, search_query = Q.fromSearch(search)
     
     if ( search_query ):
         query &= search_query
     
     self._records = table_type.select(where = query).search(terms)
     return True
Example #10
0
    def loadRecords(self, records):
        """
        Loads the record set for this instance.
        
        :param      records | <orb.RecordSet> || <list>
        """
        try:
            if self._running:
                return

            self._cancelled = False
            self._running = True

            try:
                self.setDatabase(records.database())
            except AttributeError:
                pass

            self.startLoading()

            # make sure the orb module is loaded, or there is really no point
            if RecordSet is None:
                logger.error('Orb was not loaded.')

            # lookup a group of results
            if RecordSet.typecheck(records) and records.groupBy():
                levels = records.groupBy()
                next_levels = levels[1:]

                for key, records in records.grouped(levels[0]).items():
                    if self._cancelled:
                        break

                    # PySide Hack! Emitting None across threads will crash Qt
                    #              when in PySide mode.
                    if key == None:
                        key = 'None'

                    self.loadedGroup.emit(key, records, next_levels)

            # lookup a list of results, in batched mode
            elif self.isBatched():
                self.loadBatch(records)

            # lookup a list of results, not in batched mode
            else:
                records = list(records)
                if self._preloadColumns:
                    for record in curr_records:
                        record.recordValues(self._preloadColumns)

                self.loadedRecords[object].emit(records)

            self._running = False
            self.finishLoading()
        except ConnectionLostError:
            self.finishLoading()
            self.connectionLost.emit()
        except Interruption:
            self.finishLoading()
        finally:
            self.finishLoading()
Example #11
0
class XOrbBrowserWidget(QWidget):
    """ """
    __designer_group__ = 'ProjexUI - ORB'

    currentRecordChanged = Signal()
    queryChanged = Signal(PyObject)  # orb.Query
    recordDoubleClicked = Signal(PyObject)  # orb.Table

    GroupByAdvancedKey = '__ADVANCED__'
    Mode = enum('Detail', 'Card', 'Thumbnail')

    def __init__(self, parent=None):
        super(XOrbBrowserWidget, self).__init__(parent)

        # load the user interface
        projexui.loadUi(__file__, self)

        # define custom properties
        self._hint = ''
        self._query = Q()
        self._advancedGrouping = []
        self._records = RecordSet()
        self._groupBy = XOrbBrowserWidget.GroupByAdvancedKey
        self._factory = XOrbBrowserFactory()
        self._queryWidget = XOrbQueryWidget(self, self._factory)
        self._thumbnailSize = QSize(128, 128)

        # set default properties
        self.uiSearchTXT.addButton(self.uiQueryBTN)
        self.uiQueryBTN.setCentralWidget(self._queryWidget)
        self.uiThumbLIST.installEventFilter(self)

        self.uiQueryACT.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.uiQueryBTN.setDefaultAction(self.uiQueryACT)

        self.uiViewModeWGT.addAction(self.uiDetailsACT)
        self.uiViewModeWGT.addAction(self.uiCardACT)
        self.uiViewModeWGT.addAction(self.uiThumbnailACT)

        # create connections
        self.uiGroupOptionsBTN.clicked.connect(self.showGroupMenu)
        self.uiSearchTXT.returnPressed.connect(self.refresh)
        self.queryChanged.connect(self.refresh)
        self.uiGroupBTN.toggled.connect(self.refreshResults)

        self.uiDetailsACT.triggered.connect(self.setDetailMode)
        self.uiCardACT.triggered.connect(self.setCardMode)
        self.uiThumbnailACT.triggered.connect(self.setThumbnailMode)

        self.uiQueryBTN.popupAboutToShow.connect(self.prepareQuery)
        self.uiQueryBTN.popupAccepted.connect(self.acceptQuery)
        self.uiQueryBTN.popupReset.connect(self.resetQuery)

        self.uiRefreshBTN.clicked.connect(self.refresh)

        self.uiRecordsTREE.itemDoubleClicked.connect(self.handleDetailDblClick)
        self.uiRecordsTREE.currentItemChanged.connect(
            self.emitCurrentRecordChanged)

        self.uiThumbLIST.itemDoubleClicked.connect(self.handleThumbDblClick)
        self.uiThumbLIST.currentItemChanged.connect(
            self.emitCurrentRecordChanged)

        self.uiCardTREE.itemDoubleClicked.connect(self.handleCardDblClick)
        self.uiCardTREE.currentItemChanged.connect(
            self.emitCurrentRecordChanged)

    def _loadCardGroup(self, groupName, records, parent=None):
        if (not groupName):
            groupName = 'None'

        cards = self.cardWidget()
        factory = self.factory()

        # create the group item
        group_item = QTreeWidgetItem(parent, [groupName])
        font = group_item.font(0)
        font.setBold(True)
        font.setPointSize(font.pointSize() + 2)
        group_item.setFont(0, font)
        group_item.setFlags(Qt.ItemIsEnabled)

        # load sub-groups
        if (type(records) == dict):
            for subgroup, records in sorted(records.items()):
                self._loadCardGroup(subgroup, records, group_item)
        else:
            for record in records:
                widget = factory.createCard(cards, record)
                if (not widget):
                    continue

                widget.adjustSize()

                # create the card item
                item = QTreeWidgetItem(group_item)
                item.setSizeHint(0, QSize(0, widget.height()))
                cards.setItemWidget(item, 0, widget)

        group_item.setExpanded(True)

    def _loadThumbnailGroup(self, groupName, records):
        if (not groupName):
            groupName = 'None'

        widget = self.thumbnailWidget()
        factory = self.factory()

        # create the group item
        GroupListWidgetItem(groupName, widget)

        # load sub-groups
        if (type(records) == dict):
            for subgroup, records in sorted(records.items()):
                self._loadThumbnailGroup(subgroup, records)
        else:
            # create the record items
            for record in records:
                thumbnail = factory.thumbnail(record)
                text = factory.thumbnailText(record)
                RecordListWidgetItem(thumbnail, text, record, widget)

    def acceptQuery(self):
        """
        Accepts the changes made from the query widget to the browser.
        """
        self.setQuery(self._queryWidget.query())

    def advancedGrouping(self):
        """
        Returns the advanced grouping options for this widget.
        
        :return     [<str> group level, ..]
        """
        return ['[lastName::slice(0, 1)]']
        return self._advancedGrouping

    def cardWidget(self):
        """
        Returns the card widget for this browser.
        
        :return     <QTreeWidget>
        """
        return self.uiCardTREE

    def controlsWidget(self):
        """
        Returns the controls widget for this browser.  This is the widget that
        contains the various control mechanisms.
        
        :return     <QWidget>
        """
        return self._controlsWidget

    def currentGrouping(self):
        """
        Returns the current grouping for this widget.
        
        :return     [<str> group level, ..]
        """
        groupBy = self.groupBy()
        if (groupBy == XOrbBrowserWidget.GroupByAdvancedKey):
            return self.advancedGrouping()
        else:
            table = self.tableType()
            if (not table):
                return []

            for column in table.schema().columns():
                if (column.displayName() == groupBy):
                    return [column.name()]

            return []

    def currentRecord(self):
        """
        Returns the current record from this browser.
        
        :return     <orb.Table> || None
        """
        if (self.currentMode() == XOrbBrowserWidget.Mode.Detail):
            return self.detailWidget().currentRecord()

        elif (self.currentMode() == XOrbBrowserWidget.Mode.Thumbnail):
            item = self.thumbnailWidget().currentItem()
            if (isinstance(item, RecordListWidgetItem)):
                return item.record()
            return None

        else:
            item = self.uiCardTREE.currentItem()
            widget = self.uiCardTREE.itemWidget(item, 0)
            if (isinstance(widget, XAbstractCardWidget)):
                return widget.record()

            return None

    def currentMode(self):
        """
        Returns the current mode for this widget.
        
        :return     <XOrbBrowserWidget.Mode>
        """
        if (self.uiCardACT.isChecked()):
            return XOrbBrowserWidget.Mode.Card
        elif (self.uiDetailsACT.isChecked()):
            return XOrbBrowserWidget.Mode.Detail
        else:
            return XOrbBrowserWidget.Mode.Thumbnail

    def detailWidget(self):
        """
        Returns the tree widget used by this browser.
        
        :return     <XOrbTreeWidget>
        """
        return self.uiRecordsTREE

    def emitCurrentRecordChanged(self):
        """
        Emits the current record changed signal.
        """
        if (not self.signalsBlocked()):
            self.currentRecordChanged.emit()

    def emitRecordDoubleClicked(self, record):
        """
        Emits the record double clicked signal.
        
        :param      record | <orb.Table>
        """
        if (not self.signalsBlocked()):
            self.recordDoubleClicked.emit(record)

    def enabledModes(self):
        """
        Returns the binary value of the enabled modes.
        
        :return     <XOrbBrowserWidget.Mode>
        """
        output = 0
        for i, action in enumerate(
            (self.uiDetailsACT, self.uiCardACT, self.uiThumbnailACT)):
            if (action.isEnabled()):
                output |= int(math.pow(2, i))
        return output

    def eventFilter(self, object, event):
        """
        Processes resize events on the thumbnail widget to update the group
        items to force a proper sizing.
        
        :param      object | <QObject>
                    event  | <QEvent>
        
        :return     <bool> | consumed
        """
        if ( event.type() == event.Resize and \
             self.currentMode() == XOrbBrowserWidget.Mode.Thumbnail and \
             self.isGroupingActive() ):
            size = QSize(event.size().width() - 20, 22)
            for row in range(object.count()):
                item = object.item(row)
                if (isinstance(item, GroupListWidgetItem)):
                    item.setSizeHint(size)
        return False

    def factory(self):
        """
        Returns the factory assigned to this browser for generating card and
        thumbnail information for records.
        
        :return     <XOrbBrowserFactory>
        """
        return self._factory

    def groupBy(self):
        """
        Returns the group by key for this widget.  If GroupByAdvancedKey
        is returned, then the advanced grouping options will be used.  
        Otherwise, a column will be used for grouping.
        
        :return     <str>
        """
        return self._groupBy

    def handleCardDblClick(self, item):
        """
        Handles when a card item is double clicked on.
        
        :param      item | <QTreeWidgetItem>
        """
        widget = self.uiCardTREE.itemWidget(item, 0)
        if (isinstance(widget, XAbstractCardWidget)):
            self.emitRecordDoubleClicked(widget.record())

    def handleDetailDblClick(self, item):
        """
        Handles when a detail item is double clicked on.
        
        :param      item | <QTreeWidgetItem>
        """
        if (isinstance(item, XOrbRecordItem)):
            self.emitRecordDoubleClicked(item.record())

    def handleThumbDblClick(self, item):
        """
        Handles when a thumbnail item is double clicked on.
        
        :param      item | <QListWidgetItem>
        """
        if (isinstance(item, RecordListWidgetItem)):
            self.emitRecordDoubleClicked(item.record())

    def hint(self):
        """
        Returns the hint for this widget.
        
        :return     <str>
        """
        return self._hint

    def isGroupingActive(self):
        """
        Returns if the grouping is currently on or not.
        
        :return     <bool>
        """
        return self.uiGroupBTN.isChecked()

    def isModeEnabled(self, mode):
        """
        Returns whether or not the inputed mode is enabled.
        
        :param      mode | <XOrbBrowserWidget.Mode>
        
        :return     <bool>
        """
        return (self.enabledModes() & mode) != 0

    def modeWidget(self):
        """
        Returns the mode widget for this instance.
        
        :return     <projexui.widgets.xactiongroupwidget.XActionGroupWidget>
        """
        return self.uiViewModeWGT

    def prepareQuery(self):
        """
        Prepares the popup widget with the query data.
        """
        self._queryWidget.setQuery(self.query())

    def query(self):
        """
        Returns the fixed query that is assigned via programmatic means.
        
        :return     <orb.Query> || None
        """
        return self._query

    def queryWidget(self):
        """
        Returns the query building widget.
        
        :return     <XOrbQueryWidget>
        """
        return self._queryWidget

    def records(self):
        """
        Returns the record set for the current settings of this browser.
        
        :return     <orb.RecordSet>
        """
        if (self.isGroupingActive()):
            self._records.setGroupBy(self.currentGrouping())
        else:
            self._records.setGroupBy(None)
        return self._records

    def refresh(self):
        """
        Refreshes the interface fully.
        """
        self.refreshRecords()
        self.refreshResults()

    def refreshRecords(self):
        """
        Refreshes the records being loaded by this browser.
        """
        table_type = self.tableType()
        if (not table_type):
            self._records = RecordSet()
            return False

        search = str(self.uiSearchTXT.text())

        query = self.query().copy()
        terms, search_query = Q.fromSearch(search)

        if (search_query):
            query &= search_query

        self._records = table_type.select(where=query).search(terms)
        return True

    def refreshResults(self):
        """
        Joins together the queries from the fixed system, the search, and the
        query builder to generate a query for the browser to display.
        """
        if (self.currentMode() == XOrbBrowserWidget.Mode.Detail):
            self.refreshDetails()
        elif (self.currentMode() == XOrbBrowserWidget.Mode.Card):
            self.refreshCards()
        else:
            self.refreshThumbnails()

    def refreshCards(self):
        """
        Refreshes the results for the cards view of the browser.
        """
        cards = self.cardWidget()
        factory = self.factory()

        self.setUpdatesEnabled(False)
        self.blockSignals(True)

        cards.setUpdatesEnabled(False)
        cards.blockSignals(True)

        cards.clear()
        QApplication.instance().processEvents()

        if (self.isGroupingActive()):
            grouping = self.records().grouped()
            for groupName, records in sorted(grouping.items()):
                self._loadCardGroup(groupName, records, cards)

        else:
            for record in self.records():
                widget = factory.createCard(cards, record)
                if (not widget):
                    continue

                widget.adjustSize()

                # create the card item
                item = QTreeWidgetItem(cards)
                item.setSizeHint(0, QSize(0, widget.height()))
                cards.setItemWidget(item, 0, widget)

        cards.setUpdatesEnabled(True)
        cards.blockSignals(False)

        self.setUpdatesEnabled(True)
        self.blockSignals(False)

    def refreshDetails(self):
        """
        Refreshes the results for the details view of the browser.
        """
        # start off by filtering based on the group selection
        tree = self.uiRecordsTREE
        tree.blockSignals(True)
        tree.setRecordSet(self.records())
        tree.blockSignals(False)

    def refreshThumbnails(self):
        """
        Refreshes the thumbnails view of the browser.
        """
        # clear existing items
        widget = self.thumbnailWidget()
        widget.setUpdatesEnabled(False)
        widget.blockSignals(True)

        widget.clear()
        widget.setIconSize(self.thumbnailSize())

        factory = self.factory()

        # load grouped thumbnails (only allow 1 level of grouping)
        if (self.isGroupingActive()):
            grouping = self.records().grouped()
            for groupName, records in sorted(grouping.items()):
                self._loadThumbnailGroup(groupName, records)

        # load ungrouped thumbnails
        else:
            # load the records into the thumbnail
            for record in self.records():
                thumbnail = factory.thumbnail(record)
                text = factory.thumbnailText(record)
                RecordListWidgetItem(thumbnail, text, record, widget)

        widget.setUpdatesEnabled(True)
        widget.blockSignals(False)

    def resetQuery(self):
        """
        Resets the popup query widget's query information
        """
        self._queryWidget.clear()

    def setCardMode(self):
        """
        Sets the mode for this widget to the Card mode.
        """
        self.setCurrentMode(XOrbBrowserWidget.Mode.Card)

    def setCurrentMode(self, mode):
        """
        Sets the current mode for this widget to the inputed mode.  This will
        check against the valid modes for this browser and return success.
        
        :param      mode | <XOrbBrowserWidget.Mode>
        
        :return     <bool> | success
        """
        if (not self.isModeEnabled(mode)):
            return False

        if (mode == XOrbBrowserWidget.Mode.Detail):
            self.uiModeSTACK.setCurrentIndex(0)
            self.uiDetailsACT.setChecked(True)
        elif (mode == XOrbBrowserWidget.Mode.Card):
            self.uiModeSTACK.setCurrentIndex(1)
            self.uiCardACT.setChecked(True)
        else:
            self.uiModeSTACK.setCurrentIndex(2)
            self.uiThumbnailACT.setChecked(True)

        self.refreshResults()

        return True

    def setCurrentRecord(self, record):
        """
        Sets the current record for this browser to the inputed record.
        
        :param      record | <orb.Table> || None
        """
        mode = self.currentMode()
        if (mode == XOrbBrowserWidget.Mode.Detail):
            self.detailWidget().setCurrentRecord(record)

        elif (mode == XOrbBrowserWidget.Mode.Thumbnail):
            thumbs = self.thumbnailWidget()
            for row in range(thumbs.count()):
                item = thumbs.item(row)
                if ( isinstance(item, RecordListWidgetItem) and \
                     item.record() == item ):
                    thumbs.setCurrentItem(item)
                    break

    def setDetailMode(self):
        """
        Sets the mode for this widget to the Detail mode.
        """
        self.setCurrentMode(XOrbBrowserWidget.Mode.Detail)

    def setFactory(self, factory):
        """
        Sets the factory assigned to this browser for generating card and
        thumbnail information for records.
        
        :param      factory | <XOrbBrowserFactory>
        """
        self._factory = factory
        self._queryWidget.setFactory(factory)

    def setGroupByAdvanced(self):
        """
        Sets the groupBy key for this widget to GroupByAdvancedKey signaling 
        that the advanced user grouping should be used.
        """
        self.setGroupBy(XOrbBrowserWidget.GroupByAdvancedKey)

    def setGroupBy(self, groupBy):
        """
        Sets the group by key for this widget.  This should correspond to a 
        display name for the columns, or the GroupByAdvancedKey keyword.  It is
        recommended to use setGroupByAdvanced for setting advanced groupings.
        
        :param      groupBy | <str>
        """
        self._groupBy = groupBy

    def setGroupingActive(self, state):
        """
        Sets whether or not the grouping should be enabled for the widget.
        
        :param      state | <bool>
        """
        self.uiGroupBTN.setChecked(state)

    def setHint(self, hint):
        """
        Sets the hint for this widget.
        
        :param      hint | <str>
        """
        self._hint = hint
        self.detailWidget().setHint(hint)

    def setModeEnabled(self, mode, state):
        """
        Sets whether or not the mode should be enabled.
        
        :param      mode  | <XOrbBrowserWidget.Mode>
                    state | <bool>
        """
        if (mode == XOrbBrowserWidget.Mode.Detail):
            self.uiDetailsACT.setEnabled(state)
        elif (mode == XOrbBrowserWidget.Mode.Card):
            self.uiCardACT.setEnabled(state)
        else:
            self.uiThumbnailACT.setEnabled(state)

    def setQuery(self, query):
        """
        Sets the fixed lookup query for this widget to the inputed query.
        
        :param      query | <orb.Query>
        """
        self._query = query
        if (not self.signalsBlocked()):
            self.queryChanged.emit(query)

    def setTableType(self, tableType):
        """
        Sets the table type for this widget to the inputed type.
        
        :param      tableType | <orb.Table>
        """
        self.detailWidget().setTableType(tableType)
        self.queryWidget().setTableType(tableType)

    def setThumbnailMode(self):
        """
        Sets the mode for this widget to the thumbnail mode.
        """
        self.setCurrentMode(XOrbBrowserWidget.Mode.Thumbnail)

    def setThumbnailSize(self, size):
        """
        Sets the size that will be used for the thumbnails in this widget.
        
        :param      size | <QSize>
        """
        self._thumbnailSize = QSize(size)

    def showGroupMenu(self):
        """
        Displays the group menu to the user for modification.
        """
        group_active = self.isGroupingActive()
        group_by = self.groupBy()

        menu = XMenu(self)
        menu.setTitle('Grouping Options')
        menu.setShowTitle(True)
        menu.addAction('Edit Advanced Grouping')

        menu.addSeparator()

        action = menu.addAction('No Grouping')
        action.setCheckable(True)
        action.setChecked(not group_active)

        action = menu.addAction('Advanced')
        action.setCheckable(True)
        action.setChecked(group_by == self.GroupByAdvancedKey and group_active)
        if (group_by == self.GroupByAdvancedKey):
            font = action.font()
            font.setBold(True)
            action.setFont(font)

        menu.addSeparator()

        # add dynamic options from the table schema
        tableType = self.tableType()
        if (tableType):
            columns = tableType.schema().columns()
            columns.sort(key=lambda x: x.displayName())
            for column in columns:
                action = menu.addAction(column.displayName())
                action.setCheckable(True)
                action.setChecked(group_by == column.displayName()
                                  and group_active)

                if (column.displayName() == group_by):
                    font = action.font()
                    font.setBold(True)
                    action.setFont(font)

        point = QPoint(0, self.uiGroupOptionsBTN.height())
        action = menu.exec_(self.uiGroupOptionsBTN.mapToGlobal(point))

        if (not action):
            return
        elif (action.text() == 'Edit Advanced Grouping'):
            print 'edit advanced grouping options'
        elif (action.text() == 'No Grouping'):
            self.setGroupingActive(False)

        elif (action.text() == 'Advanced'):
            self.uiGroupBTN.blockSignals(True)
            self.setGroupBy(self.GroupByAdvancedKey)
            self.setGroupingActive(True)
            self.uiGroupBTN.blockSignals(False)

            self.refreshResults()

        else:
            self.uiGroupBTN.blockSignals(True)
            self.setGroupBy(str(action.text()))
            self.setGroupingActive(True)
            self.uiGroupBTN.blockSignals(False)

            self.refreshResults()

    def stackWidget(self):
        """
        Returns the stack widget linked with this browser.  This contains the
        different views linked with the view mode.
        
        :return     <QStackWidget>
        """
        return self.uiModeSTACK

    def tableType(self):
        """
        Returns the table type for this widget.
        
        :return     <orb.Table>
        """
        return self.detailWidget().tableType()

    def thumbnailSize(self):
        """
        Returns the size that will be used for displaying thumbnails for this
        widget.
        
        :return     <QSize>
        """
        return self._thumbnailSize

    def thumbnailWidget(self):
        """
        Returns the thumbnail widget for this widget.
        
        :return     <QListWidget>
        """
        return self.uiThumbLIST

    x_hint = Property(str, hint, setHint)
Example #12
0
 def loadRecords(self, records):
     """
     Loads the record set for this instance.
     
     :param      records | <orb.RecordSet> || <list>
     """
     try:
         if self._running:
             return
         
         self._cancelled = False
         self._running = True
         
         try:
             self.setDatabase(records.database())
         except AttributeError:
             pass
         
         self.startLoading()
         
         # make sure the orb module is loaded, or there is really no point
         if RecordSet is None:
             logger.error('Orb was not loaded.')
         
         # lookup a group of results
         if RecordSet.typecheck(records) and records.groupBy():
             levels = records.groupBy()
             next_levels = levels[1:]
             
             for key, records in records.grouped(levels[0]).items():
                 if self._cancelled:
                     break
                 
                 # PySide Hack! Emitting None across threads will crash Qt
                 #              when in PySide mode.
                 if key == None:
                     key = 'None'
                 
                 self.loadedGroup.emit(key, records, next_levels)
         
         # lookup a list of results, in batched mode
         elif self.isBatched():
             self.loadBatch(records)
             
         # lookup a list of results, not in batched mode
         else:
             records = list(records)
             if self._preloadColumns:
                 for record in curr_records:
                     record.recordValues(self._preloadColumns)
             
             self.loadedRecords[object].emit(records)
         
         self._running = False
         self.finishLoading()
     except ConnectionLostError:
         self.finishLoading()
         self.connectionLost.emit()
     except Interruption:
         self.finishLoading()
     finally:
         self.finishLoading()
Example #13
0
 def recordSet( self ):
     """
     Returns the record set that is associated with this widget.
     
     :return     <orb.RecordSet> || None
     """
     if ( not self.table() ):
         return None
     
     recordSet = RecordSet(self.table())
     recordSet.setQuery(self.query())
     
     # set the grouping options
     grouping = nativestring(self.uiGroupingTXT.text()).split(',')
     while ( '' in grouping ):
         grouping.remove('')
     
     recordSet.setGroupBy( grouping )
     
     # set the sorting options
     sorting = nativestring(self.uiSortingTXT.text()).split(',')
     while ( '' in sorting ):
         sorting.remove('')
     
     recordSet.setOrder([i.split('|') for i in sorting])
     
     # set the paged options
     recordSet.setPaged(self.uiPagedCHK.isChecked())
     recordSet.setPageSize(self.uiPagedSPN.value())
     
     return recordSet
class XOrbBrowserWidget(QWidget):
    """ """
    __designer_group__ = 'ProjexUI - ORB'
    
    currentRecordChanged = Signal()
    queryChanged         = Signal(PyObject) # orb.Query
    recordDoubleClicked  = Signal(PyObject) # orb.Table
    
    GroupByAdvancedKey = '__ADVANCED__'
    Mode = enum('Detail', 'Card', 'Thumbnail')
    
    def __init__( self, parent = None ):
        super(XOrbBrowserWidget, self).__init__( parent )
        
        # load the user interface
        projexui.loadUi(__file__, self)
        
        # define custom properties
        self._hint              = ''
        self._query             = Q()
        self._advancedGrouping  = []
        self._records           = RecordSet()
        self._groupBy           = XOrbBrowserWidget.GroupByAdvancedKey
        self._factory           = XOrbBrowserFactory()
        self._queryWidget       = XOrbQueryWidget(self, self._factory)
        self._thumbnailSize     = QSize(128, 128)
        
        # set default properties
        self.uiSearchTXT.addButton(self.uiQueryBTN)
        self.uiQueryBTN.setCentralWidget(self._queryWidget)
        self.uiThumbLIST.installEventFilter(self)
        
        self.uiQueryACT.setShortcutContext(Qt.WidgetWithChildrenShortcut)
        self.uiQueryBTN.setDefaultAction(self.uiQueryACT)
        
        self.uiViewModeWGT.addAction(self.uiDetailsACT)
        self.uiViewModeWGT.addAction(self.uiCardACT)
        self.uiViewModeWGT.addAction(self.uiThumbnailACT)
        
        # create connections
        self.uiGroupOptionsBTN.clicked.connect(self.showGroupMenu)
        self.uiSearchTXT.returnPressed.connect(self.refresh)
        self.queryChanged.connect(self.refresh)
        self.uiGroupBTN.toggled.connect(self.refreshResults)
        
        self.uiDetailsACT.triggered.connect(self.setDetailMode)
        self.uiCardACT.triggered.connect(self.setCardMode)
        self.uiThumbnailACT.triggered.connect(self.setThumbnailMode)
        
        self.uiQueryBTN.popupAboutToShow.connect(self.prepareQuery)
        self.uiQueryBTN.popupAccepted.connect(self.acceptQuery)
        self.uiQueryBTN.popupReset.connect(self.resetQuery)
        
        self.uiRefreshBTN.clicked.connect(self.refresh)
        
        self.uiRecordsTREE.itemDoubleClicked.connect(self.handleDetailDblClick)
        self.uiRecordsTREE.currentItemChanged.connect(
                                                 self.emitCurrentRecordChanged)
        
        self.uiThumbLIST.itemDoubleClicked.connect(self.handleThumbDblClick)
        self.uiThumbLIST.currentItemChanged.connect(
                                                self.emitCurrentRecordChanged)
        
        self.uiCardTREE.itemDoubleClicked.connect(self.handleCardDblClick)
        self.uiCardTREE.currentItemChanged.connect(
                                                self.emitCurrentRecordChanged)
    
    def _loadCardGroup( self, groupName, records, parent = None ):
        if ( not groupName ):
            groupName = 'None'
        
        cards  = self.cardWidget()
        factory = self.factory()
        
        # create the group item
        group_item = QTreeWidgetItem(parent, [groupName])
        font = group_item.font(0)
        font.setBold(True)
        font.setPointSize(font.pointSize() + 2)
        group_item.setFont(0, font)
        group_item.setFlags(Qt.ItemIsEnabled)
        
        # load sub-groups
        if ( type(records) == dict ):
            for subgroup, records in sorted(records.items()):
                self._loadCardGroup(subgroup, records, group_item)
        else:
            for record in records:
                widget = factory.createCard(cards, record)
                if ( not widget ):
                    continue
                
                widget.adjustSize()
                
                # create the card item
                item = QTreeWidgetItem(group_item)
                item.setSizeHint(0, QSize(0, widget.height()))
                cards.setItemWidget(item, 0, widget)
        
        group_item.setExpanded(True)
    
    def _loadThumbnailGroup( self, groupName, records ):
        if ( not groupName ):
            groupName = 'None'
        
        widget  = self.thumbnailWidget()
        factory = self.factory()
        
        # create the group item
        GroupListWidgetItem(groupName, widget)
        
        # load sub-groups
        if ( type(records) == dict ):
            for subgroup, records in sorted(records.items()):
                self._loadThumbnailGroup(subgroup, records)
        else:
            # create the record items
            for record in records:
                thumbnail = factory.thumbnail(record)
                text      = factory.thumbnailText(record)
                RecordListWidgetItem(thumbnail, text, record, widget)
    
    def acceptQuery( self ):
        """
        Accepts the changes made from the query widget to the browser.
        """
        self.setQuery(self._queryWidget.query())
    
    def advancedGrouping( self ):
        """
        Returns the advanced grouping options for this widget.
        
        :return     [<str> group level, ..]
        """
        return ['[lastName::slice(0, 1)]']
        return self._advancedGrouping
    
    def cardWidget( self ):
        """
        Returns the card widget for this browser.
        
        :return     <QTreeWidget>
        """
        return self.uiCardTREE
    
    def controlsWidget( self ):
        """
        Returns the controls widget for this browser.  This is the widget that
        contains the various control mechanisms.
        
        :return     <QWidget>
        """
        return self._controlsWidget
    
    def currentGrouping( self ):
        """
        Returns the current grouping for this widget.
        
        :return     [<str> group level, ..]
        """
        groupBy = self.groupBy()
        if ( groupBy == XOrbBrowserWidget.GroupByAdvancedKey ):
            return self.advancedGrouping()
        else:
            table = self.tableType()
            if ( not table ):
                return []
            
            for column in table.schema().columns():
                if ( column.displayName() == groupBy ):
                    return [column.name()]
            
            return []
    
    def currentRecord( self ):
        """
        Returns the current record from this browser.
        
        :return     <orb.Table> || None
        """
        if ( self.currentMode() == XOrbBrowserWidget.Mode.Detail ):
            return self.detailWidget().currentRecord()
        
        elif ( self.currentMode() == XOrbBrowserWidget.Mode.Thumbnail ):
            item = self.thumbnailWidget().currentItem()
            if ( isinstance(item, RecordListWidgetItem) ):
                return item.record()
            return None
        
        else:
            item = self.uiCardTREE.currentItem()
            widget = self.uiCardTREE.itemWidget(item, 0)
            if ( isinstance(widget, XAbstractCardWidget) ):
                return widget.record()
            
            return None
    
    def currentMode( self ):
        """
        Returns the current mode for this widget.
        
        :return     <XOrbBrowserWidget.Mode>
        """
        if ( self.uiCardACT.isChecked() ):
            return XOrbBrowserWidget.Mode.Card
        elif ( self.uiDetailsACT.isChecked() ):
            return XOrbBrowserWidget.Mode.Detail
        else:
            return XOrbBrowserWidget.Mode.Thumbnail
    
    def detailWidget( self ):
        """
        Returns the tree widget used by this browser.
        
        :return     <XOrbTreeWidget>
        """
        return self.uiRecordsTREE
        
    def emitCurrentRecordChanged( self ):
        """
        Emits the current record changed signal.
        """
        if ( not self.signalsBlocked() ):
            self.currentRecordChanged.emit()
    
    def emitRecordDoubleClicked( self, record ):
        """
        Emits the record double clicked signal.
        
        :param      record | <orb.Table>
        """
        if ( not self.signalsBlocked() ):
            self.recordDoubleClicked.emit(record)
    
    def enabledModes( self ):
        """
        Returns the binary value of the enabled modes.
        
        :return     <XOrbBrowserWidget.Mode>
        """
        output = 0
        for i, action in enumerate((self.uiDetailsACT,
                                    self.uiCardACT,
                                    self.uiThumbnailACT)):
            if ( action.isEnabled() ):
                output |= int(math.pow(2, i))
        return output
    
    def eventFilter( self, object, event ):
        """
        Processes resize events on the thumbnail widget to update the group
        items to force a proper sizing.
        
        :param      object | <QObject>
                    event  | <QEvent>
        
        :return     <bool> | consumed
        """
        if ( event.type() == event.Resize and \
             self.currentMode() == XOrbBrowserWidget.Mode.Thumbnail and \
             self.isGroupingActive() ):
            size = QSize(event.size().width() - 20, 22)
            for row in range(object.count()):
                item = object.item(row)
                if ( isinstance(item, GroupListWidgetItem) ):
                    item.setSizeHint(size)
        return False
    
    def factory( self ):
        """
        Returns the factory assigned to this browser for generating card and
        thumbnail information for records.
        
        :return     <XOrbBrowserFactory>
        """
        return self._factory
    
    def groupBy( self ):
        """
        Returns the group by key for this widget.  If GroupByAdvancedKey
        is returned, then the advanced grouping options will be used.  
        Otherwise, a column will be used for grouping.
        
        :return     <str>
        """
        return self._groupBy
    
    def handleCardDblClick( self, item ):
        """
        Handles when a card item is double clicked on.
        
        :param      item | <QTreeWidgetItem>
        """
        widget = self.uiCardTREE.itemWidget(item, 0)
        if ( isinstance(widget, XAbstractCardWidget) ):
            self.emitRecordDoubleClicked(widget.record())
    
    def handleDetailDblClick( self, item ):
        """
        Handles when a detail item is double clicked on.
        
        :param      item | <QTreeWidgetItem>
        """
        if ( isinstance(item, XOrbRecordItem) ):
            self.emitRecordDoubleClicked(item.record())
    
    def handleThumbDblClick( self, item ):
        """
        Handles when a thumbnail item is double clicked on.
        
        :param      item | <QListWidgetItem>
        """
        if ( isinstance(item, RecordListWidgetItem) ):
            self.emitRecordDoubleClicked(item.record())
    
    def hint( self ):
        """
        Returns the hint for this widget.
        
        :return     <str>
        """
        return self._hint
    
    def isGroupingActive( self ):
        """
        Returns if the grouping is currently on or not.
        
        :return     <bool>
        """
        return self.uiGroupBTN.isChecked()
    
    def isModeEnabled( self, mode ):
        """
        Returns whether or not the inputed mode is enabled.
        
        :param      mode | <XOrbBrowserWidget.Mode>
        
        :return     <bool>
        """
        return (self.enabledModes() & mode) != 0
    
    def modeWidget( self ):
        """
        Returns the mode widget for this instance.
        
        :return     <projexui.widgets.xactiongroupwidget.XActionGroupWidget>
        """
        return self.uiViewModeWGT
    
    def prepareQuery( self ):
        """
        Prepares the popup widget with the query data.
        """
        self._queryWidget.setQuery(self.query())
    
    def query( self ):
        """
        Returns the fixed query that is assigned via programmatic means.
        
        :return     <orb.Query> || None
        """
        return self._query
    
    def queryWidget( self ):
        """
        Returns the query building widget.
        
        :return     <XOrbQueryWidget>
        """
        return self._queryWidget
    
    def records( self ):
        """
        Returns the record set for the current settings of this browser.
        
        :return     <orb.RecordSet>
        """
        if ( self.isGroupingActive() ):
            self._records.setGroupBy(self.currentGrouping())
        else:
            self._records.setGroupBy(None)
        return self._records
    
    def refresh( self ):
        """
        Refreshes the interface fully.
        """
        self.refreshRecords()
        self.refreshResults()
    
    def refreshRecords( self ):
        """
        Refreshes the records being loaded by this browser.
        """
        table_type = self.tableType()
        if ( not table_type ):
            self._records = RecordSet()
            return False
        
        search = str(self.uiSearchTXT.text())
        
        query = self.query().copy()
        terms, search_query = Q.fromSearch(search)
        
        if ( search_query ):
            query &= search_query
        
        self._records = table_type.select(where = query).search(terms)
        return True
    
    def refreshResults( self ):
        """
        Joins together the queries from the fixed system, the search, and the
        query builder to generate a query for the browser to display.
        """
        if ( self.currentMode() == XOrbBrowserWidget.Mode.Detail ):
            self.refreshDetails()
        elif ( self.currentMode() == XOrbBrowserWidget.Mode.Card ):
            self.refreshCards()
        else:
            self.refreshThumbnails()
    
    def refreshCards( self ):
        """
        Refreshes the results for the cards view of the browser.
        """
        cards = self.cardWidget()
        factory = self.factory()
        
        self.setUpdatesEnabled(False)
        self.blockSignals(True)
        
        cards.setUpdatesEnabled(False)
        cards.blockSignals(True)
        
        cards.clear()
        QApplication.instance().processEvents()
        
        if ( self.isGroupingActive() ):
            grouping = self.records().grouped()
            for groupName, records in sorted(grouping.items()):
                self._loadCardGroup(groupName, records, cards)
            
        else:
            for record in self.records():
                widget = factory.createCard(cards, record)
                if ( not widget ):
                    continue
                
                widget.adjustSize()
                
                # create the card item
                item = QTreeWidgetItem(cards)
                item.setSizeHint(0, QSize(0, widget.height()))
                cards.setItemWidget(item, 0, widget)
        
        cards.setUpdatesEnabled(True)
        cards.blockSignals(False)
        
        self.setUpdatesEnabled(True)
        self.blockSignals(False)
    
    def refreshDetails( self ):
        """
        Refreshes the results for the details view of the browser.
        """
        # start off by filtering based on the group selection
        tree = self.uiRecordsTREE
        tree.blockSignals(True)
        tree.setRecordSet(self.records())
        tree.blockSignals(False)
    
    def refreshThumbnails( self ):
        """
        Refreshes the thumbnails view of the browser.
        """
        # clear existing items
        widget = self.thumbnailWidget()
        widget.setUpdatesEnabled(False)
        widget.blockSignals(True)
        
        widget.clear()
        widget.setIconSize(self.thumbnailSize())
        
        factory = self.factory()
        
        # load grouped thumbnails (only allow 1 level of grouping)
        if ( self.isGroupingActive() ):
            grouping = self.records().grouped()
            for groupName, records in sorted(grouping.items()):
                self._loadThumbnailGroup(groupName, records)
        
        # load ungrouped thumbnails
        else:
            # load the records into the thumbnail
            for record in self.records():
                thumbnail = factory.thumbnail(record)
                text      = factory.thumbnailText(record)
                RecordListWidgetItem(thumbnail, text, record, widget)
        
        widget.setUpdatesEnabled(True)
        widget.blockSignals(False)
    
    def resetQuery( self ):
        """
        Resets the popup query widget's query information
        """
        self._queryWidget.clear()
    
    def setCardMode( self ):
        """
        Sets the mode for this widget to the Card mode.
        """
        self.setCurrentMode(XOrbBrowserWidget.Mode.Card)
    
    def setCurrentMode( self, mode ):
        """
        Sets the current mode for this widget to the inputed mode.  This will
        check against the valid modes for this browser and return success.
        
        :param      mode | <XOrbBrowserWidget.Mode>
        
        :return     <bool> | success
        """
        if ( not self.isModeEnabled(mode) ):
            return False
        
        if ( mode == XOrbBrowserWidget.Mode.Detail ):
            self.uiModeSTACK.setCurrentIndex(0)
            self.uiDetailsACT.setChecked(True)
        elif ( mode == XOrbBrowserWidget.Mode.Card ):
            self.uiModeSTACK.setCurrentIndex(1)
            self.uiCardACT.setChecked(True)
        else:
            self.uiModeSTACK.setCurrentIndex(2)
            self.uiThumbnailACT.setChecked(True)
        
        self.refreshResults()
        
        return True
    
    def setCurrentRecord( self, record ):
        """
        Sets the current record for this browser to the inputed record.
        
        :param      record | <orb.Table> || None
        """
        mode = self.currentMode()
        if ( mode == XOrbBrowserWidget.Mode.Detail ):
            self.detailWidget().setCurrentRecord(record)
        
        elif ( mode == XOrbBrowserWidget.Mode.Thumbnail ):
            thumbs = self.thumbnailWidget()
            for row in range(thumbs.count()):
                item = thumbs.item(row)
                if ( isinstance(item, RecordListWidgetItem) and \
                     item.record() == item ):
                    thumbs.setCurrentItem(item)
                    break
    
    def setDetailMode( self ):
        """
        Sets the mode for this widget to the Detail mode.
        """
        self.setCurrentMode(XOrbBrowserWidget.Mode.Detail)
    
    def setFactory( self, factory ):
        """
        Sets the factory assigned to this browser for generating card and
        thumbnail information for records.
        
        :param      factory | <XOrbBrowserFactory>
        """
        self._factory = factory
        self._queryWidget.setFactory(factory)
    
    def setGroupByAdvanced( self ):
        """
        Sets the groupBy key for this widget to GroupByAdvancedKey signaling 
        that the advanced user grouping should be used.
        """
        self.setGroupBy(XOrbBrowserWidget.GroupByAdvancedKey)
    
    def setGroupBy( self, groupBy ):
        """
        Sets the group by key for this widget.  This should correspond to a 
        display name for the columns, or the GroupByAdvancedKey keyword.  It is
        recommended to use setGroupByAdvanced for setting advanced groupings.
        
        :param      groupBy | <str>
        """
        self._groupBy = groupBy
    
    def setGroupingActive( self, state ):
        """
        Sets whether or not the grouping should be enabled for the widget.
        
        :param      state | <bool>
        """
        self.uiGroupBTN.setChecked(state)
    
    def setHint( self, hint ):
        """
        Sets the hint for this widget.
        
        :param      hint | <str>
        """
        self._hint = hint
        self.detailWidget().setHint(hint)
    
    def setModeEnabled( self, mode, state ):
        """
        Sets whether or not the mode should be enabled.
        
        :param      mode  | <XOrbBrowserWidget.Mode>
                    state | <bool>
        """
        if ( mode == XOrbBrowserWidget.Mode.Detail ):
            self.uiDetailsACT.setEnabled(state)
        elif ( mode == XOrbBrowserWidget.Mode.Card ):
            self.uiCardACT.setEnabled(state)
        else:
            self.uiThumbnailACT.setEnabled(state)
    
    def setQuery( self, query ):
        """
        Sets the fixed lookup query for this widget to the inputed query.
        
        :param      query | <orb.Query>
        """
        self._query = query
        if ( not self.signalsBlocked() ):
            self.queryChanged.emit(query)
    
    def setTableType( self, tableType ):
        """
        Sets the table type for this widget to the inputed type.
        
        :param      tableType | <orb.Table>
        """
        self.detailWidget().setTableType(tableType)
        self.queryWidget().setTableType(tableType)
    
    def setThumbnailMode( self ):
        """
        Sets the mode for this widget to the thumbnail mode.
        """
        self.setCurrentMode(XOrbBrowserWidget.Mode.Thumbnail)
    
    def setThumbnailSize( self, size ):
        """
        Sets the size that will be used for the thumbnails in this widget.
        
        :param      size | <QSize>
        """
        self._thumbnailSize = QSize(size)
    
    def showGroupMenu( self ):
        """
        Displays the group menu to the user for modification.
        """
        group_active = self.isGroupingActive()
        group_by     = self.groupBy()
        
        menu = XMenu(self)
        menu.setTitle('Grouping Options')
        menu.setShowTitle(True)
        menu.addAction('Edit Advanced Grouping')
        
        menu.addSeparator()
        
        action = menu.addAction('No Grouping')
        action.setCheckable(True)
        action.setChecked(not group_active)
        
        action = menu.addAction('Advanced')
        action.setCheckable(True)
        action.setChecked(group_by == self.GroupByAdvancedKey and group_active)
        if ( group_by == self.GroupByAdvancedKey ):
            font = action.font()
            font.setBold(True)
            action.setFont(font)
        
        menu.addSeparator()
        
        # add dynamic options from the table schema
        tableType = self.tableType()
        if ( tableType ):
            columns = tableType.schema().columns()
            columns.sort(key = lambda x: x.displayName())
            for column in columns:
                action = menu.addAction(column.displayName())
                action.setCheckable(True)
                action.setChecked(group_by == column.displayName() and
                                  group_active)
                
                if ( column.displayName() == group_by ):
                    font = action.font()
                    font.setBold(True)
                    action.setFont(font)
        
        point = QPoint(0, self.uiGroupOptionsBTN.height())
        action = menu.exec_(self.uiGroupOptionsBTN.mapToGlobal(point))
        
        if ( not action ):
            return
        elif ( action.text() == 'Edit Advanced Grouping' ):
            print 'edit advanced grouping options'
        elif ( action.text() == 'No Grouping' ):
            self.setGroupingActive(False)
            
        elif ( action.text() == 'Advanced' ):
            self.uiGroupBTN.blockSignals(True)
            self.setGroupBy(self.GroupByAdvancedKey)
            self.setGroupingActive(True)
            self.uiGroupBTN.blockSignals(False)
            
            self.refreshResults()
        
        else:
            self.uiGroupBTN.blockSignals(True)
            self.setGroupBy(str(action.text()))
            self.setGroupingActive(True)
            self.uiGroupBTN.blockSignals(False)
            
            self.refreshResults()
    
    def stackWidget( self ):
        """
        Returns the stack widget linked with this browser.  This contains the
        different views linked with the view mode.
        
        :return     <QStackWidget>
        """
        return self.uiModeSTACK
    
    def tableType( self ):
        """
        Returns the table type for this widget.
        
        :return     <orb.Table>
        """
        return self.detailWidget().tableType()
    
    def thumbnailSize( self ):
        """
        Returns the size that will be used for displaying thumbnails for this
        widget.
        
        :return     <QSize>
        """
        return self._thumbnailSize
    
    def thumbnailWidget( self ):
        """
        Returns the thumbnail widget for this widget.
        
        :return     <QListWidget>
        """
        return self.uiThumbLIST
    
    x_hint = Property(str, hint, setHint)
    def recordSet(self):
        """
        Returns the record set that is associated with this widget.
        
        :return     <orb.RecordSet> || None
        """
        if (not self.table()):
            return None

        recordSet = RecordSet(self.table())
        recordSet.setQuery(self.query())

        # set the grouping options
        grouping = str(self.uiGroupingTXT.text()).split(',')
        while ('' in grouping):
            grouping.remove('')

        recordSet.setGroupBy(grouping)

        # set the sorting options
        sorting = str(self.uiSortingTXT.text()).split(',')
        while ('' in sorting):
            sorting.remove('')

        recordSet.setOrder([i.split('|') for i in sorting])

        # set the paged options
        recordSet.setPaged(self.uiPagedCHK.isChecked())
        recordSet.setPageSize(self.uiPagedSPN.value())

        return recordSet