Example #1
0
class SubTitleHeader(TitleHeader):
    @warnWxThread
    def GetSubTitlePanel(self, parent):
        self.subtitle = StaticText(parent)
        return self.subtitle

    @warnWxThread
    def SetSubTitle(self, subtitle):
        if subtitle != self.subtitle.GetLabel():
            self.Freeze()

            self.subtitle.SetLabel(subtitle)
            self.subtitle.Refresh()

            self.Thaw()
Example #2
0
class ChannelResultFooter(ListFooter):
    def GetMidPanel(self, hSizer):
        self.message = StaticText(self)
        font = self.message.GetFont()
        font.SetPointSize(font.GetPointSize()+2)
        font.SetWeight(wx.FONTWEIGHT_BOLD)
        self.message.SetFont(font)
        
        hSizer.Add(self.message, 0, wx.TOP|wx.BOTTOM|wx.ALIGN_BOTTOM, 3)
        hSizer.AddStretchSpacer()
        
        self.channelResults = wx.Button(self, -1, "Channel Results")
        hSizer.Add(self.channelResults, 0, wx.TOP|wx.BOTTOM, 3)
        
    def SetLabel(self, label, nr_channels):
        haveResults = True if nr_channels and nr_channels >= 1 else False
        
        if label != self.message.GetLabel():
            self.message.SetLabel(label)
            
            if haveResults:
                self.HighLight()
            self.Layout()
        
        self.EnableResults(haveResults)
    
    def SetEvents(self, channel):
        #removing old, binding new eventhandler
        self.channelResults.Unbind(wx.EVT_BUTTON)
        self.channelResults.Bind(wx.EVT_BUTTON, channel)
        
    def EnableResults(self, state):
        self.channelResults.Enable(state)
    
    def Reset(self):
        self.EnableResults(False)
        self.message.SetLabel('')
Example #3
0
class AbstractListBody():
    @warnWxThread
    def __init__(self,
                 parent_list,
                 columns,
                 leftSpacer=0,
                 rightSpacer=0,
                 singleExpanded=False,
                 showChange=False,
                 list_item_max=None,
                 hasFilter=True,
                 listRateLimit=LIST_RATE_LIMIT):
        self.columns = columns
        self.leftSpacer = leftSpacer
        self.rightSpacer = rightSpacer
        self.parent_list = parent_list
        self.singleExpanded = singleExpanded
        self.showChange = showChange
        self.list_selected = LIST_SELECTED
        self.listRateLimit = listRateLimit
        if not list_item_max:
            list_item_max = LIST_ITEM_MAX_SIZE
        self.list_item_max = list_item_max
        self.list_cur_max = self.list_item_max

        self.hasFilter = hasFilter

        hSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.listpanel = wx.Panel(self, name="LIST")

        #vertical sizer containing all items
        self.vSizer = wx.BoxSizer(wx.VERTICAL)
        self.listpanel.SetSizer(self.vSizer)
        hSizer.Add(self.listpanel, 1)
        self.SetSizer(hSizer)

        #messagePanel text
        self.messagePanel = wx.Panel(self.listpanel)
        self.messagePanel.SetBackgroundColour(DEFAULT_BACKGROUND)
        self.messagePanel.Show(False)
        messageVSizer = wx.BoxSizer(wx.VERTICAL)

        self.headerText = StaticText(self.messagePanel)
        _set_font(self.headerText, fontweight=wx.FONTWEIGHT_BOLD)
        self.messageText = StaticText(self.messagePanel)
        self.loadNext = wx.Button(self.messagePanel)
        self.loadNext.Bind(wx.EVT_BUTTON, self.OnLoadMore)
        self.loadNext.Hide()

        messageVSizer.Add(self.headerText, 0, wx.EXPAND)
        messageVSizer.Add(self.messageText, 0, wx.EXPAND)
        messageVSizer.Add(self.loadNext, 0, wx.ALIGN_CENTER)
        self.messageText.sizer = messageVSizer
        self.messageText.altControl = None

        messageSizer = wx.BoxSizer(wx.HORIZONTAL)
        messageSizer.AddStretchSpacer()
        messageSizer.Add(messageVSizer, 0, wx.TOP | wx.BOTTOM, 7)
        messageSizer.AddStretchSpacer()
        self.messagePanel.SetSizer(messageSizer)

        #vertical scrollrate
        self.rate = None

        #states
        self.cur_expanded = None

        #quick filter
        self.filter = None
        self.filterMessage = None

        #sorting
        self.sortcolumn = None

        #queue lists
        self.done = True
        self.lastData = 0
        self.dataTimer = None
        self.data = None
        self.raw_data = None
        self.items = {}

        # Allow list-items to store the most recent mouse left-down events:
        self.lastMouseLeftDownEvent = None
        self.curWidth = -1
        self.Bind(wx.EVT_SIZE, self.OnEventSize)

    @warnWxThread
    def SetBackgroundColour(self, colour):
        wx.Panel.SetBackgroundColour(self, DEFAULT_BACKGROUND)
        self.listpanel.SetBackgroundColour(colour)

    @warnWxThread
    def SetStyle(self,
                 font=None,
                 foregroundcolour=None,
                 list_selected=LIST_SELECTED):
        if font:
            self.SetFont(font)
        if foregroundcolour:
            self.SetForegroundColour(foregroundcolour)

        self.list_selected = list_selected

    @warnWxThread
    def OnSort(self, column, reverse):
        self.Scroll(-1, 0)

        #Niels: translating between -1 and None conventions
        if column == -1:
            column = None

        self.sortcolumn = column
        self.sortreverse = reverse

        self.SetData(highlight=False, force=True)

    def DoSort(self):
        def sortby(b, a):
            if a[0] in self.items:
                a = self.items[a[0]].data[self.sortcolumn]
            else:
                a = a[1][self.sortcolumn]

            if b[0] in self.items:
                b = self.items[b[0]].data[self.sortcolumn]
            else:
                b = b[1][self.sortcolumn]

            if isinstance(a, basestring):
                a = a.lower()
            if isinstance(b, basestring):
                b = b.lower()

            return cmp(a, b)

        if self.sortcolumn != None:
            self.data = sorted(self.data, cmp=sortby, reverse=self.sortreverse)

    def SetFilter(self, filter, filterMessage, highlight):
        self.filterMessage = filterMessage

        if self.filter is not None or filter is not None:
            self.filter = filter
            self.Scroll(-1, 0)
            self.SetData(highlight=highlight)

    @warnWxThread
    def OnExpand(self, item, raise_event=False):
        self.Freeze()

        if self.singleExpanded:
            if self.cur_expanded:
                self.OnCollapse(self.cur_expanded, False)

        panel = self.parent_list.OnExpand(item)
        if panel and not isinstance(panel, bool):
            item.Expand(panel)
            self.OnChange()

            #Niels: Windows 7 needs this refresh otherwise it will show some paint errors
            self.Refresh()

        self.cur_expanded = item
        self.Thaw()
        return panel

    @warnWxThread
    def OnCollapse(self, item=None, onchange=True):
        self.Freeze()

        if not item:
            item = self.cur_expanded

        if item:
            panel = item.Collapse()
            self.parent_list.OnCollapse(item, panel)
            self.cur_expanded = None

        if onchange:
            self.OnChange()

            #Niels: Windows 7 needs this refresh otherwise it will show some paint errors
            self.Refresh()

        self.Thaw()

    @warnWxThread
    def OnChange(self, scrollToTop=False):
        self.Freeze()

        self.vSizer.Layout()
        self.listpanel.Layout()
        self.Layout()

        #Determine scrollrate
        if self.rate is None:
            nritems = len(self.vSizer.GetChildren())
            if nritems > 0:
                height = self.vSizer.GetSize()[1]
                self.rate = height / nritems
                if DEBUG:
                    print >> sys.stderr, "ListBody: setting scrollrate to", self.rate

                self.SetupScrolling(scrollToTop=scrollToTop, rate_y=self.rate)
            else:
                if DEBUG:
                    print >> sys.stderr, "ListBody: setting scrollrate to default"

                self.SetupScrolling(scrollToTop=scrollToTop)
        else:
            if DEBUG:
                print >> sys.stderr, "ListBody: using scrollrate", self.rate
            self.SetupScrolling(scrollToTop=scrollToTop, rate_y=self.rate)

        self.Thaw()

    @warnWxThread
    def Reset(self):
        if DEBUG:
            print >> sys.stderr, "ListBody: reset"

        self.Freeze()

        self.filter = None
        self.filterMessage = None
        self.sortcolumn = None
        self.rate = None

        self.vSizer.ShowItems(False)
        self.vSizer.Clear()
        for key in self.items.keys():
            self.items[key].Destroy()

        self.list_cur_max = self.list_item_max

        self.items = {}
        self.data = None
        self.lastData = 0
        self.raw_data = None
        self.ShowLoading()
        self.OnChange()
        self.Thaw()

    def IsEmpty(self):
        return len(self.items) == 0

    def InList(self, key, onlyCreated=True):
        if onlyCreated or not self.data:
            return key in self.items

        if key in self.items:
            return True
        return any(curdata[0] == key for curdata in self.data)

    @warnWxThread
    def ScrollToEnd(self, scroll_to_end):
        if scroll_to_end:
            self.Scroll(-1, self.vSizer.GetSize()[1])
        else:
            self.Scroll(-1, 0)

    @warnWxThread
    def ScrollToId(self, id):
        if id in self.items:
            sy = self.items[id].GetPosition()[1] / self.GetScrollPixelsPerUnit(
            )[1]
            self.Scroll(-1, sy)

    @warnWxThread
    def ShowMessage(self, message, header=None, altControl=None):
        if DEBUG:
            print >> sys.stderr, "ListBody: ShowMessage", message

        self.Freeze()

        if header:
            self.headerText.SetLabel(header)
            self.headerText.Show()
        else:
            self.headerText.Hide()

        self.messageText.SetLabel(message)

        if self.messageText.altControl:
            self.messageText.sizer.Detach(self.messageText.altControl)
            if getattr(self.messageText.altControl, 'ShowItems', False):
                self.messageText.altControl.ShowItems(False)
                self.messageText.altControl.Clear(True)
            self.messageText.altControl = None

        if altControl:
            self.messageText.altControl = altControl
            self.messageText.sizer.Insert(2, altControl, 0, wx.EXPAND)

        self.loadNext.Hide()
        self.vSizer.ShowItems(False)
        self.vSizer.Clear()

        self.vSizer.Add(self.messagePanel, 0, wx.EXPAND | wx.BOTTOM, 1)
        self.messagePanel.Layout()

        if not self.messagePanel.IsShown():
            self.messagePanel.Show()

        self.OnChange()
        self.Thaw()

    def GetMessage(self):
        header = message = None
        if self.headerText.IsShown():
            header = self.headerText.GetLabel()

        if self.messageText.IsShown():
            message = self.messageText.GetLabel()

        return header, message

    @warnWxThread
    def ShowLoading(self):
        self.ShowMessage('Loading, please wait.')

    @warnWxThread
    def RefreshData(self, key, data):
        if key in self.items:
            if DEBUG:
                print >> sys.stderr, "ListBody: refresh item", self.items[key]
            self.items[key].RefreshData(data)

            #forward update to expandedPanel
            panel = self.items[key].GetExpandedPanel()
            if panel and getattr(panel, 'RefreshData', False):
                if DEBUG:
                    print >> sys.stderr, "ListBody: refresh item (Calling expandedPanel refreshdata)", self.items[
                        key]

                panel.RefreshData(data)

    @warnWxThread
    def SetData(self, data=None, highlight=None, force=False):
        if DEBUG:
            nr_items = 0
            if data:
                nr_items = len(data)
            print >> sys.stderr, "ListBody: new data", time(), nr_items

        if data == None:
            data = self.raw_data
        else:
            self.raw_data = data

        assert len(data[0][1]) == len(
            self.columns
        ), 'Data does not have equal amount of columns %d/%d %s' % (len(
            data[0][1]), len(self.columns), type(self.parent_list))

        if highlight is None:
            highlight = not self.IsEmpty()

        def doSetData():
            self.lastData = time()
            self.dataTimer = None

            self.__SetData(highlight)

        if force:
            if self.dataTimer:
                self.dataTimer.Stop()
            doSetData()
        else:
            diff = time() - (self.listRateLimit + self.lastData)
            call_in = -diff * 1000
            if call_in <= 0:
                doSetData()
            else:
                if self.dataTimer == None:
                    self.dataTimer = wx.CallLater(call_in, doSetData)
                else:
                    self.dataTimer.Restart(call_in)

    def __SetData(self, highlight=True):
        if DEBUG:
            print >> sys.stderr, "ListBody: __SetData", time()

        if __debug__ and currentThread().getName() != "MainThread":
            print >> sys.stderr, "ListBody: __SetData thread", currentThread(
            ).getName(), "is NOT MAIN THREAD"
            print_stack()

        self.Freeze()

        #apply quickfilter
        if self.filter:
            data = filter(self.filter, self.raw_data)
            if len(data) != len(self.raw_data):
                self.parent_list.SetFilteredResults(len(data))
            else:
                self.parent_list.SetFilteredResults(None)
        else:
            self.parent_list.SetFilteredResults(None)
            data = self.raw_data

        if not data:
            data = []

        self.highlightSet = set()
        cur_keys = set(self.items.keys())
        for curdata in data[:self.list_cur_max]:
            key = curdata[0]
            if key not in cur_keys:
                if highlight:
                    self.highlightSet.add(key)
            else:
                cur_keys.discard(key)

        #cur_keys now contains all removed items
        for key in cur_keys:
            self.items[key].Show(False)
            self.items[key].Destroy()
            del self.items[key]

        self.data = data
        self.DoSort()
        self.done = False

        if len(data) > 0:
            self.vSizer.ShowItems(False)
            self.vSizer.Clear()

            self.CreateItems(nr_items_to_create=3 * LIST_ITEM_BATCH_SIZE)

            #Try to yield
            try:
                wx.Yield()
            except:
                pass

        elif self.filter:
            self.ShowMessage(self.filterMessage(empty=True) + '.')

        if self.done:
            self.Unbind(
                wx.EVT_IDLE
            )  #unbinding unnecessary event handler seems to improve visual performance
        else:
            self.Bind(wx.EVT_IDLE, self.OnIdle)

        self.Thaw()

    def OnIdle(self, event):
        if not self.done:
            if self.data and len(self.data) > 0:
                self.CreateItems()
            else:
                self.done = True

            #idle event also paints search animation, use request more to show this update
            event.RequestMore(not self.done)
            if self.done:
                self.Unbind(wx.EVT_IDLE)

    def OnLoadMore(self, event):
        self.loadNext.Disable()
        self.list_cur_max += LIST_ITEM_MAX_SIZE

        wx.CallAfter(self.CreateItems)
        self.Bind(wx.EVT_IDLE, self.OnIdle)

    def OnLoadAll(self):
        self.loadNext.Disable()
        self.list_cur_max = sys.maxint

        wx.CallAfter(self.CreateItems)
        self.Bind(wx.EVT_IDLE, self.OnIdle)

    @warnWxThread
    def CreateItem(self, key):
        if not key in self.items and self.data:
            for curdata in self.data:
                if len(curdata) > 3:
                    thiskey, item_data, original_data, create_method = curdata
                else:
                    thiskey, item_data, original_data = curdata
                    create_method = ListItem

                if key == thiskey:
                    self.items[key] = create_method(
                        self.listpanel,
                        self,
                        self.columns,
                        item_data,
                        original_data,
                        self.leftSpacer,
                        self.rightSpacer,
                        showChange=self.showChange,
                        list_selected=self.list_selected)

                    if self.messagePanel.IsShown():
                        before = len(self.vSizer.GetChildren()) - 1
                        self.vSizer.Insert(before, self.items[key], 0,
                                           wx.EXPAND | wx.BOTTOM, 1)
                    else:
                        self.vSizer.Add(self.items[key], 0,
                                        wx.EXPAND | wx.BOTTOM, 1)

                    self.OnChange()
                    break

    @warnWxThread
    def CreateItems(self,
                    nr_items_to_create=LIST_ITEM_BATCH_SIZE,
                    nr_items_to_add=None):
        if not nr_items_to_add:
            nr_items_to_add = self.list_cur_max

        if DEBUG:
            print >> sys.stderr, "ListBody: Creating items", time()

        initial_nr_items_to_add = nr_items_to_add
        done = True

        if len(self.data) > 0:
            t1 = time()
            self.Freeze()

            #Check if we need to clear vSizer
            self.messagePanel.Show(False)
            self.loadNext.Show(False)
            self.vSizer.Remove(self.messagePanel)

            if self.filter:
                message = self.filterMessage() + '.'
            else:
                message = ''

            revertList = []
            #Add created/cached items
            for curdata in self.data:
                if len(curdata) > 3:
                    key, item_data, original_data, create_method = curdata
                else:
                    key, item_data, original_data = curdata
                    create_method = ListItem

                if nr_items_to_add > 0 and nr_items_to_create > 0:
                    if key not in self.items:
                        self.items[key] = create_method(
                            self.listpanel,
                            self,
                            self.columns,
                            item_data,
                            original_data,
                            self.leftSpacer,
                            self.rightSpacer,
                            showChange=self.showChange,
                            list_selected=self.list_selected)
                        nr_items_to_create -= 1

                    elif getattr(self.items[key], 'should_update', False):
                        self.items[key].RefreshData(curdata)

                    item = self.items[key]
                    sizer = self.vSizer.GetItem(item)
                    if not sizer:
                        self.vSizer.Add(item, 0, wx.EXPAND | wx.BOTTOM, 1)
                        item.Show()

                        if key in self.highlightSet:
                            self.highlightSet.remove(key)

                            if item.Highlight(revert=False):
                                revertList.append(key)

                    nr_items_to_add -= 1
                else:
                    done = nr_items_to_add == 0 or initial_nr_items_to_add == sys.maxint

                    if done:
                        if message != '':
                            message = 'Only showing the first %d of %d' % (len(
                                self.vSizer.GetChildren()
                            ), len(self.data)) + message[
                                12:] + '\nFurther specify keywords to reduce the number of items, or click the button below.'
                        else:
                            message = 'Only showing the first %d of %d items in this list.' % (
                                len(self.vSizer.GetChildren()), len(self.data))
                            if self.hasFilter:
                                message += '\nSearch within results to reduce the number of items, or click the button below.'

                        remainingItems = min(
                            LIST_ITEM_MAX_SIZE,
                            len(self.data) - len(self.vSizer.GetChildren()))
                        self.loadNext.SetLabel("Show next %d items" %
                                               remainingItems)
                        self.loadNext.Enable()
                        self.loadNext.Show()
                    break

            if len(message) > 12:
                self.messageText.SetLabel(message)

                self.vSizer.Add(self.messagePanel, 0, wx.EXPAND | wx.BOTTOM, 1)
                self.messagePanel.Layout()
                self.messagePanel.Show()

            self.OnChange()
            self.Thaw()

            if len(revertList) > 0:
                wx.CallLater(1000, self.Revert, revertList)

        if len(revertList) > 0:
            wx.CallLater(1000, self.Revert, revertList)

        self.done = done
        if DEBUG:
            print >> sys.stderr, "List created", len(
                self.vSizer.GetChildren()), "rows of", len(
                    self.data), "took", time() - t1, "done:", self.done

    def GetItem(self, key):
        return self.items[key]

    def GetItemPos(self, key):
        # Returns the index of the ListItem belonging to this key
        for i, data in enumerate(self.data):
            if key == data[0]:
                return i

    @warnWxThread
    def RemoveItem(self, remove):
        for key, item in self.items.iteritems():
            if item == remove:
                self.RemoveKey(key)
                break

    @warnWxThread
    def RemoveKey(self, key):
        item = self.items.get(key, None)
        if item:
            self.items.pop(key)

            self.vSizer.Detach(item)
            item.Destroy()

            self.OnChange()

            for i, curdata in enumerate(self.raw_data):
                if curdata[0] == key:
                    self.raw_data.pop(i)
                    break

    def GetExpandedItem(self):
        return self.cur_expanded

    def GetExpandedItems(self):
        return [(key, item) for key, item in self.items.iteritems()
                if item.expanded]

    @warnWxThread
    def Select(self, key, raise_event=True):
        self.DeselectAll()

        #check if we need to create this item on the spot
        if not key in self.items:
            self.CreateItem(key)

        if key in self.items:
            if raise_event:
                self.items[key].OnClick(None)
            else:
                self.items[key].expanded = True
                self.cur_expanded = self.items[key]

            self.items[key].ShowSelected()

    @warnWxThread
    def DeselectAll(self):
        for _, item in self.items.iteritems():
            item.Deselect()

    def Revert(self, revertList):
        for key in revertList:
            if key in self.items:
                self.items[key].Revert()

    def OnEventSize(self, event):
        width = self.GetSize()[0]
        if width != self.curWidth:
            doOnChange = False

            self.Freeze()
            self.curWidth = width

            for item in self.items.itervalues():
                if item.OnEventSize(width):
                    doOnChange = True

            if doOnChange:
                self.OnChange()

            self.Thaw()
        event.Skip()
Example #4
0
class TitleHeader(ListHeader):
    def __init__(self,
                 parent,
                 parent_list,
                 columns,
                 font_increment=2,
                 fontweight=wx.FONTWEIGHT_BOLD,
                 radius=LIST_RADIUS,
                 spacers=[0, 0]):
        self.font_increment = font_increment
        self.fontweight = fontweight

        ListHeader.__init__(self,
                            parent,
                            parent_list,
                            columns,
                            radius=radius,
                            spacers=spacers)

    @warnWxThread
    def AddComponents(self, columns, spacers):
        vSizer = wx.BoxSizer(wx.VERTICAL)
        vSizer.AddSpacer((-1, 3))

        self.title = StaticText(self)
        _set_font(self.title, self.font_increment, self.fontweight)

        titlePanel = self.GetTitlePanel(self)
        subtitlePanel = self.GetSubTitlePanel(self)
        righttitlePanel = self.GetRightTitlePanel(self)
        belowPanel = self.GetBelowPanel(self)

        if titlePanel:
            subSizer = wx.BoxSizer(wx.HORIZONTAL)
            subSizer.Add(self.title)
            subSizer.Add(titlePanel, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 3)
            titlePanel = subSizer
        else:
            titlePanel = self.title

        if subtitlePanel:
            subSizer = wx.BoxSizer(wx.VERTICAL)
            subSizer.Add(titlePanel, 0, wx.BOTTOM, 3)
            subSizer.Add(subtitlePanel)
            subtitlePanel = subSizer
        else:
            subtitlePanel = titlePanel

        subSizer = wx.BoxSizer(wx.HORIZONTAL)
        subSizer.Add(subtitlePanel)
        if righttitlePanel:
            subSizer.Add(righttitlePanel, 1, wx.LEFT, 3)
        righttitlePanel = subSizer

        vSizer.Add(righttitlePanel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT,
                   self.radius + spacers[0])
        if belowPanel:
            vSizer.Add(belowPanel, 1, wx.EXPAND | wx.TOP, 3)

        vSizer.AddSpacer((-1, 3))

        if len(columns) > 0:
            hSizer = wx.BoxSizer(wx.HORIZONTAL)
            self.AddColumns(hSizer, self, columns)
            vSizer.Add(hSizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT,
                       self.radius + spacers[0])
        self.SetSizer(vSizer)

    def GetTitlePanel(self, parent):
        pass

    def GetSubTitlePanel(self, parent):
        pass

    def GetRightTitlePanel(self, parent):
        pass

    def GetBelowPanel(self, parent):
        pass

    @warnWxThread
    def SetTitle(self, title):
        if title != self.title.GetLabel():
            self.Freeze()

            self.title.SetLabel(title)
            self.title.Refresh()
            self.Layout()
            self.Thaw()

    @warnWxThread
    def SetToolTip(self, tooltip):
        self.title.SetToolTipString(tooltip)