How to sync the scrollbars of two grids in wx

2019-05-10 05:37发布

问题:

One custom wx.frame is created to contain a splitter window with two grid controls inside. It's used to compare the data in each of the grid. At this point the scrollbar of two grids need to support sync scroll.

Questions:

  1. How to get these two grid's scroll event? I have tried to bin the wx.EVT_SCROLL event at the frame but failed. I also try to bind the scroll event in the custom grid control, it's failed too.
  2. How to sync scroll the scrollbar of the two grids? An answer of a relative question mentioned to use gridInstance.Scroll(row, col) to scroll the grid client window. But it doesn't contain how to sync the scrollbar.

Many thanks for any suggestion.

The init method of the custom frame

    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, title='', size=(640,480))
        main_panel = wx.Panel(self, -1)
        self.TBFLAGS = ( wx.TB_HORIZONTAL| wx.NO_BORDER| wx.TB_FLAT)
        self.controller = None
        self.isSyncScroll = True

        #hsizer = wx.BoxSizer(wx.VERTICAL)
        gsizer = wx.FlexGridSizer(rows = 1,
                                                    cols = 1,
                                                    vgap = 2,
                                                    hgap = 2)
        gsizer.AddGrowableRow(0)
        gsizer.AddGrowableCol(0)
        self.tb = self.init_toolbar()
        (sub_panel0, sub_panel1) = self.init_splitter(main_panel)
        self.grid0 = self.init_grid(sub_panel0)
        self.grid1 = self.init_grid(sub_panel1)
        self.init_status_bar()

        gsizer.Add(main_panel, 1, wx.EXPAND)
        self.SetSizer(gsizer)

        ico = wx.Icon(u'Compare.ico', wx.BITMAP_TYPE_ICO)
        self.SetIcon(ico)
        self.Maximize()

        #can't catch the scroll event at the frame
        self.Bind(wx.EVT_SCROLL, self.OnScroll, self.grid0)
        #self.Bind(wx.EVT_SCROLL, self.OnScroll)
        #self.Bind(wx.EVT_SCROLL, self.OnScroll, id=self.grid0.GetId())

回答1:

The following code gets 2 scrollbars moving together when you grab one or the other.

Python version 2.7.3 & wxpython 2.9.4.0 & windows Xp.

import wx
import wx.grid as grid


class Frame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Grid", size=(350, 250))
        self.grid = grid.Grid(self)
        self.grid.CreateGrid(20, 20)


class ScrollSync(object):
    def __init__(self, frame1, frame2):
        self.frame1 = frame1
        self.frame2 = frame2
        self.frame1.grid.Bind(wx.EVT_SCROLLWIN, self.onScrollWin1)
        self.frame2.grid.Bind(wx.EVT_SCROLLWIN, self.onScrollWin2)

    def onScrollWin1(self, event):
        if event.Orientation == wx.SB_HORIZONTAL:
            self.frame2.grid.Scroll(event.Position, -1)
        else:
            self.frame2.grid.Scroll(-1, event.Position)
        event.Skip()

    def onScrollWin2(self, event):
        if event.Orientation == wx.SB_HORIZONTAL:
            self.frame1.grid.Scroll(event.Position, -1)
        else:
            self.frame1.grid.Scroll(-1, event.Position)
        event.Skip()

if __name__ == '__main__':
    app = wx.App()
    frame1 = Frame(None)
    frame1.Show()
    frame2 = Frame(None)
    frame2.Show()
    ScrollSync(frame1, frame2)
    app.MainLoop()

Here is another version that uses a timer to check and set scroll positions, so it cover any type of scroll change.

import wx
import wx.grid as grid


class Frame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Grid", size=(350, 250))
        self.grid = grid.Grid(self)
        self.grid.CreateGrid(20, 20)


class ScrollSync(wx.EvtHandler):
    def __init__(self, frame1, frame2):
        super(ScrollSync, self).__init__()
        self.frame1 = frame1
        self.frame2 = frame2
        self.frame1ScrollPos = self.getFrame1Pos()
        self.frame2ScrollPos = self.getFrame2Pos()
        self.Bind(wx.EVT_TIMER, self.onTimer)
        self.timer = wx.Timer(self)
        self.timer.Start(20)

    def onTimer(self, event):
        if not self.frame1 or not self.frame2:
            self.timer.Stop()
            return
        if self.frame1ScrollPos != self.getFrame1Pos():
            self.frame1ScrollPos = self.getFrame1Pos()
            self.frame2.grid.Scroll(self.frame1ScrollPos)
        elif self.frame2ScrollPos != self.getFrame2Pos():
            self.frame2ScrollPos = self.getFrame2Pos()
            self.frame1.grid.Scroll(self.frame2ScrollPos)

    def getFrame1Pos(self):
        horizontal = self.frame1.grid.GetScrollPos(wx.SB_HORIZONTAL)
        vertical = self.frame1.grid.GetScrollPos(wx.SB_VERTICAL)
        return horizontal, vertical

    def getFrame2Pos(self):
        horizontal = self.frame2.grid.GetScrollPos(wx.SB_HORIZONTAL)
        vertical = self.frame2.grid.GetScrollPos(wx.SB_VERTICAL)
        return horizontal, vertical


if __name__ == '__main__':
    app = wx.App()
    frame1 = Frame(None)
    frame1.Show()
    frame2 = Frame(None)
    frame2.Show()
    ScrollSync(frame1, frame2)
    app.MainLoop()