I have a schedule created using GridBagSizer. See attached code that
displays the schedule. The first row contains column labels and the
first column contains row labels (time periods).The schedule has
scrollbars as the schedule may be larger than the window.
Appointments may span multiple rows but not columns. How can I freeze
the first row and first column while scrolling? i.e. when scrolling
right, the first column should always remain displayed and when
scrolling down, the first row should always remain displayed.
import wx
import datetime
ROW_HEIGHT = 26
COL_WIDTH = 85
class ColumnWidget(wx.Panel):
def __init__(self, parent, ID=-1, label='',
pos=wx.DefaultPosition, size=(COL_WIDTH,
ROW_HEIGHT)):
wx.Panel.__init__(self, parent, ID, pos, size,
wx.BORDER_NONE, label)
self.label = label
self.SetBackgroundColour("white")
self.SetMinSize(size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
sz = self.GetClientSize()
dc = wx.PaintDC(self)
w,h = dc.GetTextExtent(self.label)
dc.SetFont(self.GetFont())
dc.DrawText(self.label, 3, (sz.height-h)/2)
class ApptWindow(wx.Panel):
def __init__(self, parent, ID=-1, labels=None,
pos=wx.DefaultPosition, size=(COL_WIDTH,
ROW_HEIGHT)):
wx.Panel.__init__(self, parent, ID, pos, size,
wx.BORDER_NONE, labels[0])
self.labels = labels
self.SetBackgroundColour("white")
self.SetMinSize(size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
sz = self.GetClientSize()
dc = wx.PaintDC(self)
dc.SetFont(self.GetFont())
xPos=3; yPos=3
dc.DrawText(self.labels[0], xPos, yPos)
for str in self.labels:
dc.DrawText(str, xPos, yPos)
w,h = dc.GetTextExtent(str)
yPos += h + 1
class PeriodWidget(wx.Panel):
def __init__(self, parent, ID=-1, label='',
pos=wx.DefaultPosition, size=(50, ROW_HEIGHT)):
wx.Panel.__init__(self, parent, ID, pos, size,
wx.BORDER_NONE, label)
self.label = label
self.SetBackgroundColour("white")
self.SetMinSize(size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, evt):
sz = self.GetClientSize()
dc = wx.PaintDC(self)
w,h = dc.GetTextExtent(self.label)
dc.SetFont(self.GetFont())
dc.DrawText(self.label, (sz.width-w-1), 1)
class View(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, None, -1, "Schedule",size=(600, 600))
self.sizer = wx.GridBagSizer(hgap=1, vgap=1)
self.scrolling_window = wx.ScrolledWindow( self )
self.scrolling_window.SetScrollRate(1,1)
self.scrolling_window.EnableScrolling(True,True)
self.Bind(wx.EVT_SIZE, self.OnSize)
def ColumnLabels(self):
for ix in range(30):
cw = ColumnWidget(self.scrolling_window, label='Column ' +
str(ix))
self.sizer.Add(cw, pos=(0, ix + 1))
def PeriodLabels(self, startDateTime, endDateTime):
self.startDateTime = startDateTime
self.endDateTime = endDateTime
periodDuration = datetime.timedelta(minutes=30)
periodStartTime = self.startDateTime
self.periods = [None]
ix = 1
while periodStartTime < self.endDateTime:
self.periods += [periodStartTime]
lbl = str(periodStartTime.strftime('%H:%M'))
pw = PeriodWidget(self.scrolling_window, label=lbl)
self.sizer.Add(pw, pos=(ix, 0))
ix += 1
periodStartTime = periodStartTime + periodDuration
def calEvents(self):
labels = ['Appt Info', 'L2: more info .............', 'L3:
more info........', 'L4: more info...']
place = [
(1, 1, 3), (2, 2, 2), (3, 5, 1), (1, 8, 5), (1, 21,
5), (3, 29, 6)
, (9, 1, 3), (9, 2, 2), (13, 5, 1), (15, 8, 5), (12,
21, 5), (13, 29, 6)
, (27, 8, 2), (17, 12, 6)
]
for (row, col, spa) in place:
aw = ApptWindow(self.scrolling_window, labels=labels)
self.sizer.Add(aw, pos=(row, col), span=(spa,1),
flag=wx.EXPAND)
def OnSize(self, event):
self.scrolling_window.SetSize(self.GetClientSize())
def prepare(self):
self.scrolling_window.SetSizer(self.sizer)
self.Centre()
def mainTest():
app = wx.App(redirect=False)
view = View(None)
today = datetime.date.today()
strToday = str(today.strftime('%Y-%m-%d'))
dateTimeStart = datetime.datetime.strptime(strToday + ' ' +
'08:00', '%Y-%m-%d %H:%M')
dateTimeEnd = datetime.datetime.strptime(strToday + ' ' +
'22:00' , '%Y-%m-%d %H:%M')
view.PeriodLabels(dateTimeStart, dateTimeEnd)
view.ColumnLabels()
view.calEvents()
view.prepare()
view.Show()
app.MainLoop()
if __name__ == "__main__":
mainTest()