JS.
JS.

Reputation: 16097

Obtaining Column number from wxPython ListCtrl/UltimateListCtrl in Virtual mode

I'm new to wxPython, so please be gentle.

I'm attempting to make virtual list control that is controlled via a context menu popup when the user presses the right mouse button.

From my little bit of experience, it seems that the virtual list control likes to operate with "item" (read: 'row') and "column" numbers. Fair enough.

When I receive a right-click event, I can get the row (item) number easy enough by calling event.GetIndex(). But how do I get the column number of the object that was clicked?

import wx
import wx.lib.agw.ultimatelistctrl as ULC

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Right-click example")
        self.list = MyListCtrl(parent=self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.list, 1, wx.EXPAND)
        self.SetSizer(sizer)

class MyListCtrl(ULC.UltimateListCtrl):
     def __init__(self, parent, *args, **kwargs):
         ULC.UltimateListCtrl.__init__(self, parent, 1, agwStyle=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|wx.LC_VRULES)

        self.InsertColumn(0, "Column0")
        self.InsertColumn(1, "Column1")
        self.SetItemCount(5)
        # Bindings
        self.Bind(ULC.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRightClick)

    def OnGetItemText(self, item, column):
        return "%d, %d" % (item, column)

    def OnGetItemToolTip(self, item, column):
        pass

    def OnGetItemTextColour(self, item, column):
        pass

    def OnRightClick(self, event):
        # Get the index (i.e. which row was clicked)
        print("OnColRightClick: GetIndex = %r\n" %(event.GetIndex()))
        # How can I get which column was clicked?

if __name__ == "__main__":
    # Start the GUI
    app = wx.App()
    frame = MyFrame()
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()

Upvotes: 0

Views: 3228

Answers (6)

user2615588
user2615588

Reputation: 81

this works for me in a listctrl. it isn't perfect but it works. the only limitation is all the column on the left the one we want to select must be visible.

self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
self.col=-1
....

def OnDoubleClick(self, event):
    posx=event.GetX()
    self.col=-1
    while posx>0:
        self.col+=1
        posx=posx-self.GetColumnWidth(self.col)        
    event.Skip()

Upvotes: 1

stesteve
stesteve

Reputation: 327

You can use event.GetColumn() from the EVT_LIST_COL_CLICK or EVT_LIST_COL_RIGHT_CLICK event handler. This returns the m_col value.

Upvotes: 0

amwinter
amwinter

Reputation: 3139

This works for a grid, might be easily adapted to a ListCtrl:

def onclick(self,event):
    rows = cumsum(self.GetRowSize(i) for i in range(self.GetNumberRows()))
    cols = cumsum(self.GetColSize(i) for i in range(self.GetNumberCols()))
    irow = self.GetNumberRows() - sum(event.m_y < y for y in rows)
    icol = self.GetNumberCols() - sum(event.m_x < x for x in cols)
    print irow,icol

Upvotes: 0

SideofClouds
SideofClouds

Reputation: 11

I don't know if you can get the column number in a listctrl when clicking a row (because it selects the entire row, not a single cell) ... however, if you want the number of the column when clicking on the column (like I did and after a lot of searches ended up here) you can bind an event (wx.EVT_LIST_COL_CLICK/wx.EVT_LIST_COL_RIGHT_CLICK) to your listctrl and in the event method use event.m_col ... it stores the column number there

    self.Bind(wx.EVT_LIST_COL_CLICK, self.onColumnClick, self.myListCtrl)
...
def onColumnClick(self, evt):
    column_clicked = evt.m_col

Got my inspiration from here: http://wxpython.org/Phoenix/docs/html/ListCtrl.html , at the 'Events Emitted by this Class' section

Upvotes: 1

Mike Driscoll
Mike Driscoll

Reputation: 33071

Sadly there is not a good way to get the column information. I did find this discussion on the matter though: http://wxpython-users.1045709.n5.nabble.com/Getting-row-col-of-selected-cell-in-ListCtrl-td2360831.html

It sounds like you'd have to calculate it yourself. According to Robin, there's some code in wx.lib.mixins.listctrl.TextEditMixin that might help. I also found the recipe for ObjectListView (a wrapper for ListCtrl) that also might help: http://code.activestate.com/recipes/577543-objectlistview-getcolumnclickedevent-handler/

Upvotes: 1

JS.
JS.

Reputation: 16097

"If at first you don't succeed, try again. Then quit; there's no use being a damn fool about it." -- Mark Twain

I'm embarrassed to admit that I wasted a week on this. To avoid others falling into this same trap, let me simplify things:

If you have a list of things, where each thing has sub-elements that you want to display together but are happy to operate on as a group, then a ListCtrl may work for you.

If you have a list of things, where each thing has sub-elements, but you're interested in operating on those sub-elements independently, ListCtrl appears to not be what you want.

I've started using the Grid widget, and so far it seems to be working for my purposes. This may come back to bite me later, but for now it seems to work. Here's some slightly modified code from the Wx Huge Grid demo to illustrate how a click on the grid widget will return both row and column "addresses".

If anyone has suggestions for how to get the column numbers from ListCtrl, I'm certainly all ears.

import  wx
import  wx.grid as  gridlib

#---------------------------------------------------------------------------
class HugeTable(gridlib.PyGridTableBase):
    def __init__(self, log):
        gridlib.PyGridTableBase.__init__(self)
        self.log = log

        self.odd=gridlib.GridCellAttr()
        self.odd.SetBackgroundColour("sky blue")
        self.even=gridlib.GridCellAttr()
        self.even.SetBackgroundColour("sea green")

    def GetAttr(self, row, col, kind):
        attr = [self.even, self.odd][row % 2]
        attr.IncRef()
        return attr

    # This is all it takes to make a custom data table to plug into a
    # wxGrid.  There are many more methods that can be overridden, but
    # the ones shown below are the required ones.  This table simply
    # provides strings containing the row and column values.

    def GetNumberRows(self):
        return 10000

    def GetNumberCols(self):
        return 10000

    def IsEmptyCell(self, row, col):
        return False

    def GetValue(self, row, col):
        return str( (row, col) )

    def SetValue(self, row, col, value):
        self.log.write('SetValue(%d, %d, "%s") ignored.\n' % (row, col, value))

#---------------------------------------------------------------------------
 class HugeTableGrid(gridlib.Grid):
    def __init__(self, parent, log):
        gridlib.Grid.__init__(self, parent, -1)

        table = HugeTable(log)

        # The second parameter means that the grid is to take ownership of the
        # table and will destroy it when done.  Otherwise you would need to keep
        # a reference to it and call it's Destroy method later.
        self.SetTable(table, True)

        self.Bind(gridlib.EVT_GRID_CELL_RIGHT_CLICK, self.OnCellRightClick)

    def OnCellRightClick(self, event):
        print "OnCellRightClick: (%d,%d)\n" % (event.GetRow(), event.GetCol())

#---------------------------------------------------------------------------
 class TestFrame(wx.Frame):
    def __init__(self, parent, log):
        wx.Frame.__init__(self, parent, -1, "Huge (virtual) Table Demo", size=(640,480))
        grid = HugeTableGrid(self, log)

        grid.SetReadOnly(5,5, True)

#---------------------------------------------------------------------------
if __name__ == '__main__':
    import sys
    app = wx.App()
    frame = TestFrame(None, sys.stdout)
    frame.Show(True)
    app.MainLoop()

Upvotes: 1

Related Questions