MakeCellVisible() in grid not implemented

This may be related to this, but then that looks rather complicated…

Here’s the problem. I’m trying to revamp the mp3 player I found around here. It’s a simple thing, requires the user to pick a folder from which to load songs. My regular playlist covers many folders, so I stored the filenames in a table, and mean to have it displayed in a grid (a dabo dGrid). The sticking point at the moment is that when the form launches, I load the pk of the last selected song from local preferences, navigate the grid to that record, and then when the form shows, the grid is on the top record, the scrollbar pointer is on the top of the scrollbar. If I press keyDown, it immediately jumps to the next song after the one I selected, the scrollbar takes proper position and everything’s fine.

I’ve tried a dozen tricks, and this actually worked in several other situations, when I’d just have grd.CurrentRow=nnn, and the grid would navigate to the desired row. Not in this case. Don’t know why, perhaps something’s still not working because this is done while the form is still invisible?

I’ve tried grd.SetGridCursor(), does nothing. Tried grd.SelectRow(), and found that it calls MakeCellVisible()… which, after some digging, turned out to be just an idea, there’s no code in the method, just the docstring.

Is there some simple trick to make the current row scroll into view before the user has any chance to do anything?

Of course, while writing this I got a couple more ideas, but… still,

The actual code for MakeCellVisible() (like much of wxPython) is implemented in the wxWidgets C++ layer. It does work in my applications.

Does calling it using wx.CallAfter() work in your case?

Below is a modified version of an example by Mike Driscoll to demonstrate:

import wx
import wx.grid as gridlib

class MyForm(wx.Frame):
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, parent=None, title="Grid Tutorial Two", size=(650,320))
        panel = wx.Panel(self)

        myGrid = gridlib.Grid(panel)
        myGrid.CreateGrid(15, 6)

        myGrid.SetCellValue(0,0, "Hello")
        myGrid.SetCellFont(0, 0, wx.Font(12, wx.ROMAN, wx.ITALIC, wx.NORMAL))
        print(myGrid.GetCellValue(0,0))

        myGrid.SetCellValue(1,1, "I'm in red!")
        myGrid.SetCellTextColour(1, 1, wx.RED)

        myGrid.SetCellBackgroundColour(2, 2, wx.CYAN)

        myGrid.SetCellValue(3, 3, "This cell is read-only")
        myGrid.SetReadOnly(3, 3, True)

        myGrid.SetCellEditor(5, 0, gridlib.GridCellNumberEditor(1,1000))
        myGrid.SetCellValue(5, 0, "123")
        myGrid.SetCellEditor(6, 0, gridlib.GridCellFloatEditor())
        myGrid.SetCellValue(6, 0, "123.34")
        myGrid.SetCellEditor(7, 0, gridlib.GridCellNumberEditor())

        myGrid.SetCellSize(11, 1, 3, 3)
        myGrid.SetCellAlignment(11, 1, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
        myGrid.SetCellValue(11, 1, "This cell is set to span 3 rows and 3 columns")

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(myGrid)
        panel.SetSizer(sizer)

        wx.CallAfter(myGrid.MakeCellVisible, 13, 0)


if __name__ == "__main__":
    app = wx.App()
    frame = MyForm()
    frame.Show()
    app.MainLoop()

The actual code for MakeCellVisible() (like much of wxPython) is implemented in the wxWidgets C++ layer. It does work in my applications.

I thought so, but then seeing so much of it having some python code, came to the wrong conclusion.

Does calling it using wx.CallAfter() work in your case?

Nope, doesn’t, even with 400ms. I’ll see how it goes when the player actually starts working, i.e. the form is fully alive, what will happen when I click NextSong, or the current song finishes.

Your mention of 400ms makes me think you are using wx.CallLater() rather than wx.CallAfter().

Although these functions can often achieve the same result, they work differently, specifically wx.CallLater() uses a timer, whereas wx.CallAfter() waits until the current and pending event handlers have been completed.

For the very simple example I posted above, both wx.CallAfter() and wx.CallLater() do cause the grid to be scrolled, but calling MakeCellVisible() directly in __init__() doesn’t.

What would really help would be if you could post the code for the smallest, runnable example that does demonstrate the problem.

Okay, so far so good, thanks. I can confirm that it works when the form is visible, now it’s only a matter of finding a suitable event which will fire once when the form shows, to move the code there.

Perhaps you could use EVT_SHOW. It’s possible that you could then call MakeCellVisible() directly from the event handler, without using wx.CallLater(), as in the following example:

import wx
import wx.grid as gridlib

class MyForm(wx.Frame):
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, parent=None, title="Grid Tutorial Two", size=(650,320))
        panel = wx.Panel(self)

        self.grid = gridlib.Grid(panel)
        self.grid.CreateGrid(15, 6)

        self.grid.SetCellValue(0,0, "Hello")
        self.grid.SetCellFont(0, 0, wx.Font(12, wx.ROMAN, wx.ITALIC, wx.NORMAL))
        print(self.grid.GetCellValue(0,0))

        self.grid.SetCellValue(1,1, "I'm in red!")
        self.grid.SetCellTextColour(1, 1, wx.RED)

        self.grid.SetCellBackgroundColour(2, 2, wx.CYAN)

        self.grid.SetCellValue(3, 3, "This cell is read-only")
        self.grid.SetReadOnly(3, 3, True)

        self.grid.SetCellEditor(5, 0, gridlib.GridCellNumberEditor(1,1000))
        self.grid.SetCellValue(5, 0, "123")
        self.grid.SetCellEditor(6, 0, gridlib.GridCellFloatEditor())
        self.grid.SetCellValue(6, 0, "123.34")
        self.grid.SetCellEditor(7, 0, gridlib.GridCellNumberEditor())

        self.grid.SetCellSize(11, 1, 3, 3)
        self.grid.SetCellAlignment(11, 1, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
        self.grid.SetCellValue(11, 1, "This cell is set to span 3 rows and 3 columns")

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid)
        panel.SetSizer(sizer)

        self.Bind(wx.EVT_SHOW, self.OnShow)
        
    def OnShow(self, _event):
        # wx.CallAfter(self.grid.MakeCellVisible, 13, 0)
        self.grid.MakeCellVisible(13, 0)


if __name__ == "__main__":
    app = wx.App()
    frame = MyForm()
    frame.Show()
    app.MainLoop()

Good idea! Being a relative newbie here (only four years in Python) I keep forgetting how rich the set of event is in wx (and, by extension, in dabo). Event handling is the tool I keep forgetting to use.

Will try that some time this week… being 69 has the advantage that you’re never in a hurry :).