Рубрики
Без рубрики

WXPYPHON: WX.Listctrl Советы и трюки

Получите практические, реальные навыки Python на наших ресурсах и пути

Автор оригинала: Mike Driscoll.

В прошлом году мы охватываем несколько советов и трюков для контроля сетки. В этой статье мы перейдем на несколько советов и трюков для виджета WX.Listctrl, когда он находится в режиме «Отчет». Посмотрите на советы ниже:

  • Как создать простой listctrl
  • Как сортировать строки listcctrl
  • Как сделать элементы listctrl клетки редактируемыми на месте
  • Связывание объектов со строками listcctrl
  • Чередовать цвета строки listctrl

Как создать простой listctrl

Управление списком – довольно общий виджет. В Windows вы увидите управление списком в Windows Explorer. Он имеет четыре режима: значок, маленький значок, список и отчет. Они примерно совпадают с значками, плитками, списком и представлениями в Windows Explorer соответственно. Мы собираемся сосредоточиться на режиме «ListCtrl» в режиме отчетов, потому что это режим, в котором он использует большинство разработчиков. Вот простой пример того, как создать элемент управления списком:

import wx

########################################################################
class MyForm(wx.Frame):
 
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
 
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        self.index = 0
        
        self.list_ctrl = wx.ListCtrl(panel, size=(-1,100),
                         style=wx.LC_REPORT
                         |wx.BORDER_SUNKEN
                         )
        self.list_ctrl.InsertColumn(0, 'Subject')
        self.list_ctrl.InsertColumn(1, 'Due')
        self.list_ctrl.InsertColumn(2, 'Location', width=125)
        
        btn = wx.Button(panel, label="Add Line")
        btn.Bind(wx.EVT_BUTTON, self.add_line)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
        panel.SetSizer(sizer)
        
    #----------------------------------------------------------------------
    def add_line(self, event):
        line = "Line %s" % self.index
        self.list_ctrl.InsertStringItem(self.index, line)
        self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
        self.list_ctrl.SetStringItem(self.index, 2, "USA")
        self.index += 1
    
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()

Как вы, вероятно, можно сказать из кода выше, это действительно легко создать экземпляр listctrl. Обратите внимание, что мы устанавливаем стиль в режиме отчета, используя wx.lc_report флаг. Чтобы добавить заголовки столбцов, мы называем listctrl’s InsertColumn Метод и передайте целое число, чтобы сообщить ListCTRL, какой столбец какой и строка для удобства пользователя. Да, столбцы основаны на нулевой основе, поэтому первый столбец ноль номер, второй столбец номер один и т. Д.

Следующая важная часть содержится в обработчике событий кнопки, add_line , где мы узнаем, как добавить строки данных в listctrl. Типичный метод использования – это InsertringItem метод. Если вы хотели, чтобы изображение добавлено в каждую строку, то вы будете использовать более сложный метод, такой как INSERTCOLUMNINFO вместе с InseriMagestringitem метод. Вы можете увидеть, как использовать их в демонстрационной демонстрации WXPYPHON. Мы торчаем с легким веществом в этой статье.

Во всяком случае, когда вы вызываете INSERTSTRINGITEM, вы даете ему правильный индекс строки и строку. Вы используете SetStringItem Способ для установки данных для других столбцов строки. Обратите внимание, что метод SetStringItem требует трех параметров: индекс строки, индекс столбца и строка. Наконец, мы увеличиваем индекс строки, поэтому мы ничего не перезаписываем. Теперь вы можете выбраться туда и сделать свой собственный! Давайте продолжим и узнаем, как сортировать строки!

Как сортировать строки listcctrl

Виджет listctrl имел несколько дополнительных сценариев, которые добавляют функциональность к виджету. Эти сценарии называются смесинами. Вы можете прочитать о них здесь Отказ Для этого рецепта мы будем использовать COLINSORTERMIXIN Микс. Ниже приведен код выделенной версии одного из демонстрационных примеров WXPYPHON.

import wx
import wx.lib.mixins.listctrl as listmix

musicdata = {
0 : ("Bad English", "The Price Of Love", "Rock"),
1 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"),
2 : ("George Michael", "Praying For Time", "Rock"),
3 : ("Gloria Estefan", "Here We Are", "Rock"),
4 : ("Linda Ronstadt", "Don't Know Much", "Rock"),
5 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"),
6 : ("Paul Young", "Oh Girl", "Rock"),
}

########################################################################
class TestListCtrl(wx.ListCtrl):
    
    #----------------------------------------------------------------------
    def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)

########################################################################
class TestListCtrlPanel(wx.Panel, listmix.ColumnSorterMixin):
    
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)
        
        self.list_ctrl = TestListCtrl(self, size=(-1,100),
                         style=wx.LC_REPORT
                         |wx.BORDER_SUNKEN
                         |wx.LC_SORT_ASCENDING
                         )
        self.list_ctrl.InsertColumn(0, "Artist")
        self.list_ctrl.InsertColumn(1, "Title", wx.LIST_FORMAT_RIGHT)
        self.list_ctrl.InsertColumn(2, "Genre")
        
        items = musicdata.items()
        index = 0
        for key, data in items:
            self.list_ctrl.InsertStringItem(index, data[0])
            self.list_ctrl.SetStringItem(index, 1, data[1])
            self.list_ctrl.SetStringItem(index, 2, data[2])
            self.list_ctrl.SetItemData(index, key)
            index += 1
            
        # Now that the list exists we can init the other base class,
        # see wx/lib/mixins/listctrl.py
        self.itemDataMap = musicdata
        listmix.ColumnSorterMixin.__init__(self, 3)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list_ctrl)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer)
        
    #----------------------------------------------------------------------
    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
    def GetListCtrl(self):
        return self.list_ctrl
    
    #----------------------------------------------------------------------
    def OnColClick(self, event):
        print "column clicked"
        event.Skip()
    
########################################################################
class MyForm(wx.Frame):
 
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
 
        # Add a panel so it looks the correct on all platforms
        panel = TestListCtrlPanel(self)
        
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()

Этот код немного по нечетной стороне в том, что мы наследуем микс в классе на основе wx.panel, а не класс wx.listctrl. Вы можете сделать это в любом случае, хотя до тех пор, пока вы правильно переставляете код. В любом случае, мы собираемся домой в ключевых различиях между этим примером и предыдущим. Первая разница в основном важности находится в контурной конструкции, где мы вставляем данные управления списком. Здесь мы включаем список элементов управления Setitemdata Способ включить необходимые внутренние ведения, которые позволяют сортировать. Как вы уже догадались, этот метод ассоциирует индекс строки с помощью клавиши Datic Music Data.

Далее мы создали создание COLINSORTERMIXIN И скажите, сколько столбцов в управлении списком. Мы могли бы покинуть Evt_list_col_click Привязка этого примера, так как он не имеет ничего общего с фактической сортировкой рядов, но в интересах повышения ваших знаний оно было оставлено в. Все это отображает, это показывает, как поймать столбец пользователя Column Click. Остальная часть кода является самоснабжением. Если вы хотите узнать о требованиях для этого микса, особенно когда у вас есть изображения в ваших строках, см. В соответствующем разделе в источнике (I.E. listctrl.py). Теперь не так просто? Давайте продолжим наше путешествие и узнай, как сделать клетки редактируемыми!

Как сделать элементы listctrl клетки редактируемыми на месте

Иногда программатор захочет позволить пользователю нажать на ячейку и отредактировать его на место. Это вид легкой версии контроля WX.GRID.GRID. Вот пример:

import wx
import wx.lib.mixins.listctrl  as  listmix

########################################################################
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin):
    ''' TextEditMixin allows any column to be edited. '''
    
    #----------------------------------------------------------------------
    def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0):
        """Constructor"""
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
        listmix.TextEditMixin.__init__(self)
        
########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
        rows = [("Ford", "Taurus", "1996", "Blue"),
                ("Nissan", "370Z", "2010", "Green"),
                ("Porche", "911", "2009", "Red")
                ]
        self.list_ctrl = EditableListCtrl(self, style=wx.LC_REPORT)
        
        self.list_ctrl.InsertColumn(0, "Make")
        self.list_ctrl.InsertColumn(1, "Model")
        self.list_ctrl.InsertColumn(2, "Year")
        self.list_ctrl.InsertColumn(3, "Color")
        
        index = 0
        for row in rows:
            self.list_ctrl.InsertStringItem(index, row[0])
            self.list_ctrl.SetStringItem(index, 1, row[1])
            self.list_ctrl.SetStringItem(index, 2, row[2])
            self.list_ctrl.SetStringItem(index, 3, row[3])
            index += 1
            
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer)
        

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY, "Editable List Control")
        panel = MyPanel(self)
        self.Show()
        
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

В этом скрипте мы поставили TextEditmixin В нашем классе wx.listctrl вместо нашего wx.panel, который является противоположностью предыдущего примера. Сам микс делает весь тяжелый подъем. Опять же, вам придется проверить источник мистина, чтобы действительно понять, как он работает.

Связывание объектов со строками listcctrl

Эта тема возникает много: как связать данные (то есть объекты) со строками моего listctrl? Ну, мы собираемся точно узнать, как сделать это со следующим кодом:

import wx

########################################################################
class Car(object):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, make, model, year, color="Blue"):
        """Constructor"""
        self.make = make
        self.model = model
        self.year = year
        self.color = color
    

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
        rows = [Car("Ford", "Taurus", "1996"),
                Car("Nissan", "370Z", "2010"),
                Car("Porche", "911", "2009", "Red")
                ]
    
        self.list_ctrl = wx.ListCtrl(self, size=(-1,100),
                                style=wx.LC_REPORT
                                |wx.BORDER_SUNKEN
                                )
        self.list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)
        self.list_ctrl.InsertColumn(0, "Make")
        self.list_ctrl.InsertColumn(1, "Model")
        self.list_ctrl.InsertColumn(2, "Year")
        self.list_ctrl.InsertColumn(3, "Color")
        
        index = 0
        self.myRowDict = {}
        for row in rows:
            self.list_ctrl.InsertStringItem(index, row.make)
            self.list_ctrl.SetStringItem(index, 1, row.model)
            self.list_ctrl.SetStringItem(index, 2, row.year)
            self.list_ctrl.SetStringItem(index, 3, row.color)
            self.myRowDict[index] = row
            index += 1
    
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer)
        
    #----------------------------------------------------------------------
    def onItemSelected(self, event):
        """"""
        currentItem = event.m_itemIndex
        car = self.myRowDict[currentItem]
        print car.make
        print car.model
        print car.color
        print car.year

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
        panel = MyPanel(self)
        self.Show()
        
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

Виджет управления списком фактически не имеет встроенного способа выполнить этот подвиг. Если вы этого хотите, то вы захотите проверить ObjectListView Виджет, который оборачивает listctrl и дает ему гораздо больше возможностей. Тем временем мы возьмем минуту и переходите через код выше. Первый кусок просто простой Автомобиль класс с четырьмя атрибутами. Тогда в MyPanel Класс, мы создаем список объектов автомобилей, которые мы будем использовать для данных ListCtrl.

Чтобы добавить данные в listccrl, мы используем для цикл, чтобы повторить список. Мы также связываем каждую строку с объектом автомобиля с помощью словаря Python. Мы используем индекс строки для ключа, и значение DICK заканчивается, являясь объектом автомобиля. Это позволяет нам получить доступ к всеми данными объекта автомобиля/строки позже в OniteMesebed метод. Давайте проверим это!

В OniteMeselected мы возьмем индекс ряд со следующим маленьким трюком: event.m_itemindex Отказ Затем мы используем это значение в качестве ключа для нашего словаря, чтобы мы могли получить доступ к объекту автомобиля, связанного с этой строкой. На данный момент мы просто распечатаны все атрибуты объекта автомобиля, но вы можете делать все, что вы хотите здесь. Эта базовая идея может быть легко расширена для использования результата, установленного из запроса SQLALCHEMY для данных ListCtrl. Надеюсь, вы получите общую идею.

Теперь, если вы платили пристальное внимание, вроде Робин Данн (создатель WxPython), то вы можете заметить некоторые действительно глупые логические ошибки в этом коде. Вы нашли их? Ну, вы не увидите его, если вы не сортируете строки, удалите ряд или вставьте строку. Вы видите это сейчас? Да, я тупо основан на «уникальный» ключ в моем словаре на позиции строки, которая изменится, если какое-либо из этих событий происходит. Итак, давайте посмотрим на лучший пример:

import wx

########################################################################
class Car(object):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, make, model, year, color="Blue"):
        """Constructor"""
        self.id = id(self)
        self.make = make
        self.model = model
        self.year = year
        self.color = color
    

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
        rows = [Car("Ford", "Taurus", "1996"),
                Car("Nissan", "370Z", "2010"),
                Car("Porche", "911", "2009", "Red")
                ]
    
        self.list_ctrl = wx.ListCtrl(self, size=(-1,100),
                                style=wx.LC_REPORT
                                |wx.BORDER_SUNKEN
                                )
        self.list_ctrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)
        self.list_ctrl.InsertColumn(0, "Make")
        self.list_ctrl.InsertColumn(1, "Model")
        self.list_ctrl.InsertColumn(2, "Year")
        self.list_ctrl.InsertColumn(3, "Color")
        
        index = 0
        self.myRowDict = {}
        for row in rows:
            self.list_ctrl.InsertStringItem(index, row.make)
            self.list_ctrl.SetStringItem(index, 1, row.model)
            self.list_ctrl.SetStringItem(index, 2, row.year)
            self.list_ctrl.SetStringItem(index, 3, row.color)
            self.list_ctrl.SetItemData(index, row.id)
            self.myRowDict[row.id] = row
            index += 1
    
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer)
        
    #----------------------------------------------------------------------
    def onItemSelected(self, event):
        """"""
        currentItem = event.m_itemIndex
        car = self.myRowDict[self.list_ctrl.GetItemData(currentItem)]
        print car.make
        print car.model
        print car.color
        print car.year

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
        panel = MyPanel(self)
        self.Show()
        
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

В этом примере мы добавляем новый атрибут на наш класс автомобиля, который создает уникальный идентификатор для каждого экземпляра, созданного с использованием Handy Python ID Встроенный. Затем в цикле, где мы добавляем данные в управление списком, мы называем виджета Setitemdata Способ и дать ему индекс строки и уникальный идентификатор экземпляра автомобиля. Теперь не имеет значения, где ряд заканчивается, потому что у него есть уникальный идентификатор, прикрепленный к нему. Наконец, мы должны изменить OniteMesebed чтобы получить правильный объект. Волшебство происходит в этом коде:

# this code was helpfully provided by Robin Dunn
car = self.myRowDict[self.list_ctrl.GetItemData(currentItem)]

Круто, да? Наш последний пример будет охватывать, как чередовать цвета строки, так что давайте посмотрим!

Чередовать цвета строки listctrl

Поскольку название этого раздела предполагает, что мы рассмотрим, как альтернативные цвета строк listctrl. Вот код:

import wx
import wx.lib.mixins.listctrl  as  listmix

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        
        rows = [("Ford", "Taurus", "1996", "Blue"),
                ("Nissan", "370Z", "2010", "Green"),
                ("Porche", "911", "2009", "Red")
                ]
        self.list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT)
        
        self.list_ctrl.InsertColumn(0, "Make")
        self.list_ctrl.InsertColumn(1, "Model")
        self.list_ctrl.InsertColumn(2, "Year")
        self.list_ctrl.InsertColumn(3, "Color")
        
        index = 0
        for row in rows:
            self.list_ctrl.InsertStringItem(index, row[0])
            self.list_ctrl.SetStringItem(index, 1, row[1])
            self.list_ctrl.SetStringItem(index, 2, row[2])
            self.list_ctrl.SetStringItem(index, 3, row[3])
            if index % 2:
                self.list_ctrl.SetItemBackgroundColour(index, "white")
            else:
                self.list_ctrl.SetItemBackgroundColour(index, "yellow")
            index += 1
            
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer)
        

########################################################################
class MyFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          "List Control w/ Alternate Colors")
        panel = MyPanel(self)
        self.Show()
        
#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

Код выше будет чередовать цвет фона каждой строки. Таким образом, вы должны увидеть желтые и белые ряды. Мы делаем это, позвонив в Isque instance bistctrl Setitembackgroundolour метод. Если вы использовали элемент управления виртуальным списком, то вы хотите переопределить OngetiteMattr метод. Чтобы увидеть пример последнего метода, откройте копию демонстрации WXPYPHON; Там один там.

Обертывание

Мы покрывали много земли здесь. Теперь вы должны быть в состоянии сделать намного больше с вашим wx.listctrl, чем когда вы начали, предполагая, что вы новичок, чтобы использовать его, конечно. Не стесняйтесь задавать вопросы в комментариях или предложить будущие рецепты. Я надеюсь, что вы нашли это полезным!

Примечание. Все примеры были протестированы на Windows XP с Python 2.5 и Wxpython 2.8.10.1. Они также были проверены на Windows 7 Professional с Python 2.6

Дополнительное чтение

Исходный код

  • listctrl.zip
  • listctrl.tar.tar.