Scrolling while freezing first column and first row

438 views
Skip to first unread message

Sam23

unread,
Jun 15, 2009, 11:06:30 PM6/15/09
to wxPython-users
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()

Robin Dunn

unread,
Jun 17, 2009, 4:22:26 PM6/17/09
to wxPytho...@googlegroups.com
Sam23 wrote:
> 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.

Sizers don't support doing that. Scrolled windows have the ability to
be made to work that way, sort of. Basically you would need to use some
extra child windows of the scrolled window. One for the first column,
one for the first row, and one for the scrollable area. You would then
need to call SetTargetWindow to make the scrolled window use the child
for the scrollable area as the one to actually do the scrolling.
(wx.grid.Grid is implemented this way.)

--
Robin Dunn
Software Craftsman
http://wxPython.org

Sam23

unread,
Jun 18, 2009, 11:39:45 PM6/18/09
to wxPython-users
Robin, thanks for looking at this. This is my first attempt at Python
and wxPython programming, and I have been cracking my head on how to
do do the scroll with column and row labels for more than just a few
days. Can you point me to where I can find sample code for
SetTargetWindow?

I have tried looking for info on SetTargetWindow for some time; even
prior to my earlier post for help - saw it described tantalizingly in
the wx documentation as a solution for problems like scrolling
spreadsheet-like scenarios. But I haven't been able to find any
examples of how to do this. I googled this again today, and found
references to the same wx documentation and surprise, surprise! - your
reply to my posting! I have also looked through the 'wxPython in
Action' book and downloaded the wxPython source code to try to find
the source for the wx.grid.Grid but did not seem see it - it looks
like vc code only and not python code.

With this 3-window approach, does it mean that I should draw all the
windows manually i.e. do not use any sizers, but instead use exact
pixel specifications to align the rows and columns? The column and
row labels need to have correct alignment with the appointment windows
- the GridBagSizer seemed to be a good way to get stuff aligned in 2-
dimensions. If I have the main scrolling window and 2 child windows -
what is the best way to ensure that the child windows's cells are
aligned with the main scrolling window? Should I use sizers on all 3
windows, and is there a way to ensure that they 'match' (both
horizontally and vertically) ? The width of corresponding cells need
to match exactly otherwise they will 'run' after any mismatch. Or do I
just keep track of every pixel location without using sizers?

I also considered using wx.Grid instead of writing my own grid-like
solution, but there seems to be a catch. My row labels (the first
column) are in 30 minute interval e.g. 2.30 PM, 3:00 PM, 3.30 PM, but
my appointment windows need to have a resolution of 5 minutes e.g.
appointment from 2:15 to 3:05, so there is a need to draw appointment
windows that span part of the row to reflect this precision. I am not
sure if this can be done easily with wx.grid.Grid.

My apologies for the many questions. This is really my first attempt
at GUI programming - previous GUI experience was in pre .Net VB and
HTML, which are quite different paradigms altogether.

Any pointers or suggestion will be much appreciated. Thanks again.

Mike Driscoll

unread,
Jun 19, 2009, 9:20:57 AM6/19/09
to wxPython-users


On Jun 18, 10:39 pm, Sam23 <qm0...@gmail.com> wrote:
> Robin, thanks for looking at this.  This is my first attempt at Python
> and wxPython programming, and I have been cracking my head on how to
> do do the scroll with column and row labels for more than just a few
> days. Can you point me to where I can find sample code for
> SetTargetWindow?

This seems to be the syntax:

scrolledWindow.SetTargetWindow(self.somePanel)

See also http://207.22.26.166/gtitle.py


>
> I have tried looking for info on SetTargetWindow for some time; even
> prior to my earlier post for help - saw it described tantalizingly in
> the wx documentation as a solution for problems like scrolling
> spreadsheet-like scenarios. But I haven't been able to find any
> examples of how to do this. I googled this again today, and found
> references to the same wx documentation and surprise, surprise! - your
> reply to my posting!  I have also looked through the 'wxPython in
> Action' book and downloaded the wxPython source code to try to find
> the source for the wx.grid.Grid but did not seem see it - it looks
> like vc code only and not python code.


I'm pretty sure that the grid is just wrapped C++...so what you're
seeing is the "swigged" interface. It's rather hard to read.


>
> With this 3-window approach, does it mean that I should draw all the
> windows manually i.e. do not use any sizers, but instead use exact
> pixel specifications to align the rows and columns?  The column and
> row labels need to have correct alignment with the appointment windows
> - the GridBagSizer seemed to be a good way to get stuff aligned in 2-
> dimensions.  If I have the main scrolling window and 2 child windows -
> what is the best way to ensure that the child windows's cells are
> aligned with the main scrolling window? Should I use sizers on all 3
> windows, and is there a way to ensure that they 'match' (both
> horizontally and vertically) ? The width of corresponding cells need
> to match exactly otherwise they will 'run' after any mismatch. Or do I
> just keep track of every pixel location without using sizers?
>
> I also considered using wx.Grid instead of writing my own grid-like
> solution, but there seems to be a catch. My row labels (the first
> column) are in 30 minute interval e.g. 2.30 PM, 3:00 PM, 3.30 PM, but
> my appointment windows need to have a resolution of 5 minutes e.g.
> appointment from 2:15 to 3:05, so there is a need to draw appointment
> windows that span part of the row to reflect this precision. I am not
> sure if this can be done easily with wx.grid.Grid.


The wxPython demo has a bunch of grid demos. In the Simple wx.Grid
demo, it shows how to span rows and columns. I don't think that is
very hard for it to do. Using the grid widget may be simpler for
alignment purposes, although it is a fairly complex widget.


>
> My apologies for the many questions. This is really my first attempt
> at GUI programming - previous GUI experience was in pre .Net VB and
> HTML, which are quite different paradigms altogether.
>
> Any pointers or suggestion will be much appreciated. Thanks again.

------
Mike

Sam23

unread,
Jun 19, 2009, 12:32:22 PM6/19/09
to wxPython-users
Mike, thanks for the reply and example. I have looked at both in
detail. Unfortunately, I could not get to understand the example
enough to figure out how to apply it to what I am trying to achieve.
Firstly, I hit this error: 'raise NoLicenseKey' - the program requires
a license key from Google. I tried to get this, but Google said: "We
are no longer issuing new API keys for the SOAP Search API." I
struggled with the code a bit but there were a lot of classes that I
wasn't familiar with (wxRemotelyScrolledTree, wxTreeCompanion..... and
couldn't get sufficient traction on the code. Any chance of getting a
simpler example that addresses the synchronised scrolling and maybe
also how to ensure alignment of the plain rectangular panels in the 3
different windows. At this point, I would probably be happy to
attempt a struggle with implementing it using wx.grid, but I need to
be able to draw objects that not just span rows, but span partial
rows.. e.g. spanning from the last quarter of the starting row and
first half of ending row. As a last resort, I am also considering just
drawing everything at a low level without sizers, subwindows, etc, and
doing scrolling manually - i.e. redrawing the entire screen in
response to scroll events. Thanks for your patience.

On Jun 19, 9:20 pm, Mike Driscoll <kyoso...@gmail.com> wrote:
> On Jun 18, 10:39 pm, Sam23 <qm0...@gmail.com> wrote:
>
> > Robin, thanks for looking at this.  This is my first attempt at Python
> > and wxPython programming, and I have been cracking my head on how to
> > do do the scroll with column and row labels for more than just a few
> > days. Can you point me to where I can find sample code for
> > SetTargetWindow?
>
> This seems to be the syntax:
>
> scrolledWindow.SetTargetWindow(self.somePanel)
>
> See alsohttp://207.22.26.166/gtitle.py

Robin Dunn

unread,
Jun 19, 2009, 8:41:55 PM6/19/09
to wxPytho...@googlegroups.com
Sam23 wrote:
> Mike, thanks for the reply and example. I have looked at both in
> detail. Unfortunately, I could not get to understand the example
> enough to figure out how to apply it to what I am trying to achieve.
> Firstly, I hit this error: 'raise NoLicenseKey' - the program requires
> a license key from Google. I tried to get this, but Google said: "We
> are no longer issuing new API keys for the SOAP Search API." I
> struggled with the code a bit but there were a lot of classes that I
> wasn't familiar with (wxRemotelyScrolledTree, wxTreeCompanion..... and
> couldn't get sufficient traction on the code. Any chance of getting a
> simpler example that addresses the synchronised scrolling and maybe
> also how to ensure alignment of the plain rectangular panels in the 3
> different windows. At this point, I would probably be happy to
> attempt a struggle with implementing it using wx.grid, but I need to
> be able to draw objects that not just span rows, but span partial
> rows.. e.g. spanning from the last quarter of the starting row and
> first half of ending row. As a last resort, I am also considering just
> drawing everything at a low level without sizers, subwindows, etc, and
> doing scrolling manually - i.e. redrawing the entire screen in
> response to scroll events. Thanks for your patience.

Based on your description of what you are wanting to create, doing it
yourself might be a lot better than trying to pound it into
wx.grid.Grid. You may also want to think about using something like
FloatCanvas as it will handle all of the low-level drawing (and
buffering, scaling, zooming, etc.) for you and let you deal with the
higher level shape objects representing the labels, grid lines,
appointment blocks, etc.

Sam23

unread,
Jun 21, 2009, 11:30:47 PM6/21/09
to wxPython-users
I looked at FloatCanvas, and it seems to be a separate project from
wx.Python. I am very much a python & wx novice and a little worried
about getting into another major new area that might be out of my
depth. These comments on the floatcanvas page didn't help:

'The code is well documented (well, you can figure out what's going
on, at least)'
'The code is sort of special case, but can be used for general
purposes, just as long as you don't mind the FloatCanvas feeling
slightly out of place.'
'Bugs and Limitations: Lots: patches, fixes welcome'.

I had the 'wxPython in action' book to help with learning wxPython.
Without a similar guide, I may have to flounder quite a bit (probably
get lost) in FloatCanvas before getting back on track.

I am still hoping to be able to use the basic tools in core wx.Python
to do the job. At least I have some familiarity with these. Also I am
thinking that using the more mature wx.Python library should result in
a more stable product than a relatively new project.

I have actually been attempting to modify my BagGridSizer code to do
the 'scroll with titles'; and over the weekend, have had some limited
success. Now I am able (with some hopefully minor bugs) to scroll
horizontally while keeping the first column 'frozen'.

I created my own scrollbars (wx.Scrollbar) instead of using
wx.ScrolledWindow. When the horizontal scroll event occurs, I
reshuffle all the affected windows in the BagGridSizer as follows:
Detach all window except those in the first column (the period
labels), and then put the detached windows back with the necessary
offsets, e.g. shift those that have scrolled out of visibility to a
far right end of the sizer (out of sight), and those to be shown are
shifted to the correct positions e.g those in column 3 are shifted to
column 2, those in column 4 are shifted to column 3, etc. It seems to
be working and I get to stick to using basic controls that I have
supporting documentation for.

It did take me a while to do this though.. I made a lot of mistakes at
first - mostly with the manual scrollbars.

Scrollbars didn't respond to events, then scrollbars would be of funny
size and position (left & top instead of right and bottom), then
scrollbars would not appear in visible part of window (forced out by
the appointment window), then scrollbars would always be one-step
behind in appearing with correct size and position during resizing
etc... I had to re-read the chapter on sizers (twice) to get these
fixed...

One fix was that I had to call the Layout() method after resizing the
appointment window - not doing this this was the cause of the out-of-
step scrollbars (it was getting wrong info on appt window size).
Another was to created another GridBagSizer containing the first
GridBagSizer - this outer GridBagSizer is for positioning the
scrollbars.

Anyhow, it seems to be working to some extent now. Hopefully the
vertical scrolling which is more complex (because appointments can
span rows and part of rows) can be done similarly.

What do you think of this approach? Am I doing something very klutzy?
Again, thanks for the patience and support.

Robin Dunn

unread,
Jun 24, 2009, 7:37:28 PM6/24/09
to wxPytho...@googlegroups.com
Sam23 wrote:
> I looked at FloatCanvas, and it seems to be a separate project from
> wx.Python.

There is a version of it distributed with wxPython. See
wx.lib.floatcanvas and the samples in the demo.

[...]

>
> What do you think of this approach? Am I doing something very klutzy?

Not the ideal approach, but if it works for you then go for it.

Sam23

unread,
Jun 27, 2009, 10:04:13 AM6/27/09
to wxPython-users
Using the 'window-shuffling' approach, and with much struggles, I have
managed to get the vertical scroll working as well, and also the
ability for the appointments to span partial rows. I agree that this
solution for scrolling seems less than ideal. There are a few
concerns. One is the code is quite complicated - I have to calculate a
lot of stuff when scrolling and when resizing the window. I also have
to have special coding for situations when an appointment is partially
scrolled off-the top of the screen (need to show lower part of
appointment). I am wondering if this approach is not efficient from a
performance point of view - the scrolling doesn't seem to be as fast
or as smooth as a scroll using wx.ScrolledWindow.

Unfortunately, this less then ideal approach is the only way I know to
meet the requirements at the moment.

Fortunately, it seems meets all the requirements; and to the casual
observer looking at the schedule, the scrolling with intact row and
column labels will look correct - albeit maybe a bit sluggish. At
least, I have a way forward.

Eventually, I hope to improve the code using a better approach. I am
still not certain about the correct approach for this problem. The
alternatives seems to be:
(a) SetTargetWindows with 3 sub-windows
(b) Pixel-by-pixel placement & complete redraw with scrolling or
resizing.
(c) FloatCanvas

I describe below my understanding (but mostly lack of understanding)
of the 3 approaches. Any enlightenment will be much appreciated.

------------(a) SetTargetWindows with 3 sub-windows
I have experimented with this further. I put all the column labels
into 1 panel, all the row labels into another panel, and all the
appointment labels into a 3rd panel. All these 3 panels are children
of a wx.ScrolledWindow. Positions and sizes (including VirtualSize)
are manually calculated (or just estimated and arbitrarily assigned)
at the pixel-level. I then used SetTargetWindows to point the
scrolling events to the appointment panel. After struggles (mostly
involving virtual and client sizes to get the scrollbars to show), I
got the SetTargetWindows scrolling to work. When I scrolled
vertically the appointments scrolled vertically smoothly and when I
scrolled horizontally, the appointments scrolled horizontally
smoothly. The row and column labels did not move and stayed
visible.

But this doesn't meet the requirements. When I scroll vertically, I
need the row labels to scroll vertically as well. Similarly, when I
scroll horizontally, I need the column labels to scroll horizontally
as well i.e. I need 2 windows to scroll, at any time, and in exact
alignment. But I can't figure out how to do this with SetTargetWindows
which seems to specify only 1 window as the recipient of the scroll
event.

One possibility is to intercept the scroll event, and then send
command to scroll the other window involved. But it is not clear to me
how the other 2 windows can be made scrollable without making them
scrollable windows with scrollbars showing (there shouldn't be
scrollbars on these 2 label subwindows; only on wx.ScrolledWindow).

The other problem is that, even if I can get 2 windows to scroll
together, there doesn't seem to be any easy way to ensure that the row
and column labels are aligned correctly with the appointment labels.
Because they are in 3 separate windows, I think it may be difficult to
ensure correct alignments - positioning is referenced to each window.
Do I need to find a way to reference positioning to a common reference
e.g. the containing frame?

------------(b) Pixel-by-pixel placement & complete redraw with
scrolling or resizing.

With this approach, there is only 1 panel, and all objects (row
labels, column labels and appointments) are manually placed. Because
they are all in the same frame, the calculations all have the same
reference. Recalculation and placing of objects is redone and the
entire screen repainted during scrolling events. The disadvantage of
this is that it will be very tedious to code, and the scrolling will
probably not be very efficient?

------------(c) FloatCanvas
If I am not mistaken, this approach will be more similar to (b: pixel-
by-pixel) than (a: SetTargetWindow) except that this uses that a
logical coordinate system rather than a pixel-by-pixel calculation.
Is this correct?

Sam23

unread,
Aug 15, 2009, 12:34:42 AM8/15/09
to wxPython-users
Christopher Barker wrote the following on 8/15/2009 12:11 AM:
> For those of us that haven't followed the whole thread, please post (or
> re-post) a description of what you are trying to achieve, and maybe we
> can offer something.
>
Chris,
Thanks. It will be great if I can get some help on doing this in a less onerous manner.  I have re-posted this reply in the original discussion regarding the requirements: "Scrolling while freezing first column and first row".

Not sure if I am doing something wrong with the search, but it looks like search in Google Group seems to be limited to recent months, so a search for the original discussion topic (prior to this re-post) doesn't find it.  Hopefully this re-post will work and bring it back to view.

I also re-attach a copy of the requirements implemented using GridBagSizer, lots of wx.Panels and lots and lots of coding to implement what appears to be fairly simple requirements.  If you run the code, you will see quickly what I am trying to do:
(a) Have a spreadsheet-like view of rows and columns. Scrollbars should appear when contents exceed available window space. Row titles and column titles should never scroll out of view during scrolling but should scroll itself accordingly e.g. like the row and column titles in wx.Grid

(b) Contents may span rows or parts of rows.  Rows are time periods of half an each: e.g. 2pm to 2:30pm, 2:30pm to 3pm etc, but appointments (the contents) may be from 2:15 pm to 3:45pm.

I have implemented these requirements as per the attached code.  I only managed to get it working after a lot of difficulties, but this is also in part due to the fact that this was my first wx.Python attempt - I tripped over a lof of timing issues related to sizers, Destroy() etc. 

This code now 'seems' to be working and with 'reasonable' performance but is probably not a good way to do it, and I suspect will require a lot more coding or hit some limitations as I try to finish up other requirements.

This is how the code was implemented using GridBagSizer and wx.Panels:
    (a) The Appointments are aligned in the correct position with respect to rows and columns using grid positions in GridBagSizers e.g. an appointment for column 3 will have the same the xposition as the xposition for the cell for column 3.
    (b) Scrolling is achieved by shuffling windows that have scrolled out of view to the far side, out of view (e.g. for row titles, they are shuffled to far bottom and for column titles they are shuffled to far right (out of view).
    (c) Ability to span partial rows is achieved by creating a column of cells that represent 5-minute intervals. Each visible 'period label' row of half an hour spans 6 of the 5-minute interval cells; and appointments are placed by referencing the y-position of the 5-minute interval cells and not the 30-minute period labels. i.e. appointments can be displayed with 5-minute resolution.

The sample code also demonstrates one of the functions that the schedule should implement: change appointment. Click on an appointment, and it will be highlighted, and at the top, an option to shift the appointment is shown.

A look at this discussion thread will show the various options being discussed.

Cheers
Sam

cAps.zip

C M

unread,
Aug 15, 2009, 2:11:43 AM8/15/09
to wxPytho...@googlegroups.com
Not sure if I am doing something wrong with the search, but it looks like search in Google Group seems to be limited to recent months, so a search for the original discussion topic (prior to this re-post) doesn't find it. 

For previous posts, you can search Nabble:
http://www.nabble.com/wxPython-users-f35699.html

Che

Sam23

unread,
Aug 15, 2009, 3:32:55 AM8/15/09
to wxPython-users
I originally posted this in www.nabble.com, but shortly after,
realised that the forum had moved over to Google Groups, so I re-
posted the message in Google Group's wxPython. I then deleted the
posting in www.nabble.com. Subsequent replies and postings were all
done in Google Groups.

Strangely, even now, after 2 posts today on this thread, a search
using the subject "Scrolling while freezing first column and first
row" doesn't find this thread.

So it appears to be not related to the fact that it wasn't active for
a few months. If I search using Google, I can find this thread, but if
I search within Google Groups I can't find it! There is something
strange going on with this message.. Maybe it has to do with the fact
that it was posted during the a period when the group had already
transitioned to Google Group was already up, but Nabble was still
around.

C M

unread,
Aug 15, 2009, 3:41:28 AM8/15/09
to wxPytho...@googlegroups.com

I originally posted this in www.nabble.com, but shortly after,
realised that the forum had moved over to Google Groups, so I re-
posted the message in Google Group's wxPython. I then deleted the
posting in www.nabble.com.  Subsequent replies and postings were all
done in Google Groups.

FWIW, based on reading the Python list via Google Groups vs.
reading this list via email, I much prefer to be subscribed to the
list and post via sending it an email.  Aside the from Groups' heinous
spam being filtered out so well in Gmail (which you also use) one then
has a searchable archive in the copious Gmail account.  I can star
and label messages and search for things I sent, attach a .py file
easily, etc.  It's been a valuable resource for me as I learn.

Che

Sam23

unread,
Aug 15, 2009, 4:55:22 AM8/15/09
to wxPython-users
On Aug 15, 3:41 pm, C M <cmpyt...@gmail.com> wrote:
> FWIW, based on reading the Python list via Google Groups vs.
> reading this list via email, I much prefer to be subscribed to the
> list and post via sending it an email.  Aside the from Groups' heinous
> spam being filtered out so well in Gmail (which you also use) one then
> has a searchable archive in the copious Gmail account.  I can star
> and label messages and search for things I sent, attach a .py file
> easily, etc.  It's been a valuable resource for me as I learn.
>
I subscribe only to the digest and those related to my postings. I was
concerned about being overwhelmed with email. Maybe I should get all
of it. But the search is strange - it means others may have difficulty
searching within the group, possibly not just affecting my message but
others as well.

Sam23

unread,
Aug 15, 2009, 4:57:39 AM8/15/09
to wxPython-users
On Aug 15, 12:34 pm, Sam23 <qm0...@gmail.com> wrote:
> If you run the code, you will see quickly what I am trying to do:

I forgot to mention:
Run the cAps.py file. This is the ‘controller’ that runs other
necessary files.

Christopher Barker

unread,
Aug 17, 2009, 1:48:23 PM8/17/09
to wxPytho...@googlegroups.com
Sam23 wrote:
> Thanks. It will be great if I can get some help on doing this in a less
> onerous manner.

we'll see!

> I also re-attach a copy of the requirements implemented using
> GridBagSizer, lots of wx.Panels and lots and lots of coding to implement
> what appears to be fairly simple requirements.

Actually, I don't think these requirements are simple at all.

> (a) Have a spreadsheet-like view of rows and columns. Scrollbars should
> appear when contents exceed available window space. Row titles and
> column titles should never scroll out of view during scrolling but
> should scroll itself accordingly e.g. like the row and column titles in
> wx.Grid
>
> (b) Contents may span rows or parts of rows.

hmmm - I guess what you really want is a wxGrid they allows spanning of
rows. I haven't really used wx.Grid, but I take it doesn't support that.

> Rows are time periods of
> half an each: e.g. 2pm to 2:30pm, 2:30pm to 3pm etc, but appointments
> (the contents) may be from 2:15 pm to 3:45pm.
>
> I have implemented these requirements as per the attached code. I only
> managed to get it working after a lot of difficulties, but this is also
> in part due to the fact that this was my first wx.Python attempt

Well, you have jumped into the deep end!

> This code now 'seems' to be working and with 'reasonable' performance

Is there something that the Chandler project developed that you could
use? I imagine they had a Calendar control that might be able to be used
like this.

> but is probably not a good way to do it, and I suspect will require a
> lot more coding or hit some limitations as I try to finish up other
> requirements.
>
> This is how the code was implemented using GridBagSizer and wx.Panels:

Well, there may be tweaks you can do to this, but the other option is
really to draw the whole thing yourself, with wxDC or wxGraphicsContext.
If you want to be able to zoom it , then FloatCanvas could help.
Otherwise, I'd probably just do it from scratch, which would give you
full control, but it would be a fair bit of of work. If this approach is
working for you, I'd probably just stick with it. If you do go that
route, I've been experimenting with using wxHtml to layout an render
text to an off-screen bitmap, which can then be drawn with a DC -- that
might be a way to do your text boxes.

> (b) Scrolling is achieved by shuffling windows that have scrolled
> out of view to the far side, out of view (e.g. for row titles, they are
> shuffled to far bottom and for column titles they are shuffled to far
> right (out of view).

It seems you should be able to use a wxScrolledWindow to do this for
you, I know I've seen examples of putting lots of widgets on a
ScrolledPanel around somewhere. That would require that the row an
column labels be on different Panels, so I'm not sure how you'd keep
them scrolled correctly, but I'll bet its possible.

> (c) Ability to span partial rows is achieved by creating a column of
> cells that represent 5-minute intervals. Each visible 'period label' row
> of half an hour spans 6 of the 5-minute interval cells; and appointments
> are placed by referencing the y-position of the 5-minute interval cells
> and not the 30-minute period labels. i.e. appointments can be displayed
> with 5-minute resolution.

nice trick!

> The sample code also demonstrates one of the functions that the schedule
> should implement: change appointment. Click on an appointment, and it
> will be highlighted, and at the top, an option to shift the appointment
> is shown.

Hmm -- FloatCanvas could help with this too. It supports binding to objects.

You also could pop up a "editor" window right on top of the selected
app. instead.

As I think about it, you are doing a fair bit of work keeping track of
layout and all yourself, so it may not be that much more work to draw
stuff yourself, too.

I don't know it this helped any,

-Chris


--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris....@noaa.gov

Sam23

unread,
Aug 18, 2009, 9:46:03 AM8/18/09
to wxPytho...@googlegroups.com
Chris,
Thanks for the detailed look at this issue. My replies below.

From: Christopher Barker <Chris....@noaa.gov>
Date: 2009/8/18
Subject: [wxPython-users] Re: Scrolling while freezing first column and
first row
To: wxPytho...@googlegroups.com
  
Actually, I don't think these requirements are simple at all.

  
That is good to know. I having been labouring under the impression that an experienced wx.Python programmer could have done this in in just a few hours and much fewer lines of code.  I still have a suspicion that my solution is too much code.

  
hmmm - I guess what you really want is a wxGrid they allows spanning of
rows. I haven't really used wx.Grid, but I take it doesn't support that.

  
Mike Driscoll pointed out that the wx demo shows that wx.Grid can span rows and columns. The problem is that I need to span partial rows (better than 30 minute resolution), which is not supported by wx.Grid.  I also suspect that I will eventually probably need to have some control over the appearance of the column and row titles that wx.Grid may not provide.

  
Rows are time periods of
half an each: e.g. 2pm to 2:30pm, 2:30pm to 3pm etc, but appointments
(the contents) may be from 2:15 pm to 3:45pm.

I have implemented these requirements as per the attached code.  I only
managed to get it working after a lot of difficulties, but this is also
in part due to the fact that this was my first wx.Python attempt
    
Well, you have jumped into the deep end!
  
Yes, I suspected as much.  Not the best screen for a newbie (new to Python, wxPython & fairly new to OO) to attempt.  I was hoping I could do it with the basic tools in wx like sizers, Scrollbars, etc, but it turned out to be much trickier than I expected.

  
Is there something that the Chandler project developed that you could
use? I imagine they had a Calendar control that might be able to be used
like this.

  
I am not familiar with the Chandler project. I Googled it, but didn't find something that I could get a handle on. I suspect it is probably too big to fit easily into my application and that I will get lost in the code since it appears to cover many areas.

 
Well, there may be tweaks you can do to this, but the other option is
really to draw the whole thing yourself, with wxDC or wxGraphicsContext.
  
Drawing the whole using wx.DC is quite an attractive option for me, since I already know how to do this. I use Paint for the wx.Panel classes. 

If you want to be able to zoom it , then FloatCanvas could help.
  
The full featured zoom in FloatCanvas would be nice, but is not essential

Otherwise, I'd probably just do it from scratch, which would give you
full control, but it would be a fair bit of of work. If this approach is
working for you, I'd probably just stick with it. 
I have 2 critical questions regarding these 2 alternatives:
(a) sticking with the present approach (using Sizers & lots of wx.Panels)
(b) redrawing table as a wx.Panel (start from scratch)

(a) sticking with the present approach (using Sizers & lots of wx.Panels)
Currently, I think I have about 400+ wx.Panels. At the most, this will increase to 500 wx.Panels.  Am I reaching any sorts of limits in wx.Python with these numbers? i.e will the application start to slow down or become unstable with these types of numbers of wx.Panels?  Or when other TLW are opened concurrently? (albeit much simpler ones).

Most of the panels are fairly simple - rectangular displays with some background colour and text, and possibly some simple graphics (coloured boxes).  The 5-minute interval windows (almost 200) are even simpler, just background colour with no contents and no events.  Event handling is also fairly simple;  no events are coded for the wx.Panels, the click is handled at the Application level (traced back to the originating wx.Panel).

Currently, the performance seems to be acceptable; nothing takes longer than a second, even on an old laptop that is about 6 years old - a Celeron 1000Hz with 512 MB memory with shared video memory. 

(b) Redrawing table as a wx.Panel (start from scratch)
Is this significantly faster and more stable than the current approach? I was under the impression that (a) had some advantage e.g. with (b) a Paint Event may frequently repaint the full table, whereas with (b) it  is is sometimes only necessary to repaint one of the wx.Panels.  I did some testing and it looks like I can repaint just part of the window with (a).

As an example, when the user clicks on the time slot, it is highlighted with a yellow border. With the current many wx.Panels approach, I only need to redraw the selected wx.Panel. With the single large wx.Panel approach would I need to repaint the entire window? It looks like I can specify which part of the screen to repaint.  (using BufferedDC? and specifying where to paint?)


It seems you should be able to use a wxScrolledWindow to do this for
you, I know I've seen examples of putting lots of widgets on a
ScrolledPanel around somewhere. That would require that the row an
column labels be on different Panels, so I'm not sure how you'd keep
them scrolled correctly, but I'll bet its possible.
  
My first posting in this thread had a simple single wx.ScrolledWindow containing the schedule. This scrolled fine, both horizontally and vertically - the catch was of course, with a single panel, the row and columns titles scrolled out of sight...

I think SetTarget is supposed to allow an inner window to be the target of scrolling whilst the scrollbars show in the 'outer window'. But I am not sure how to use this, especially with 2 'outer' windows. I suspect the solution is to place all 3 panels using absolute positioning, trapping scroll events, and then redrawing the column and row labels i..e SetTarget takes care of scrolling of the contents, but the titles still have to be redrawn manually to simulate 'scrolling'.   If not done correctly then the positioning of the appointment may drift with respect to the labels, e.g. a 3 pm appointment may slide to match up against a 3.30pm row label.  I suspect the answer should be to use absolute positioning for all 3 panels.  Anyway, I am just speculating.

(c) Ability to span partial rows is achieved by creating a column of
cells that represent 5-minute intervals.    
nice trick!
  
Initially, I thought it was good enough to have a display resolution of 30 minutes.  When I realised that it wasn't, I was stumped for quite a while. I had already done quite a lot of code based on scrolling with resolution of 30 minutes.  Luckily I figured out this way around it.

  
The sample code also demonstrates one of the functions that the schedule
should implement: change appointment. Click on an appointment, and it
will be highlighted, and at the top, an option to shift the appointment
is shown.
    
Hmm -- FloatCanvas could help with this too. It supports binding to objects.

You also could pop up a "editor" window right on top of the selected
app. instead.
  
I have an editor pop up for adding, editing or deleting appointments. For shift, I thought it was more intuitive for the user to simply point and click to do the shift.

As I think about it, you are doing a fair bit of work keeping track of
layout and all yourself, so it may not be that much more work to draw
stuff yourself, too.
  
Actually, it will probably be less work than what I am doing currently. If you look at the code, you will see that there is a lot of it.  The catch is of course, the 'harder way' is already done (after much blood) and 'appears' to be stable  and the 'easier way' isn't.

I don't know it this helped any,
  
Thanks, it definitely helps. At least I have some perspective of the effort so far, and an evaluation the possible paths forward. Any further insights will be much appreciated; especially on the pros and cons of the 2 approaches.
 

Phil Mayes

unread,
Aug 18, 2009, 11:14:52 AM8/18/09
to wxPython-users
On Aug 18, 6:46 am, Sam23 <qm0...@gmail.com> wrote:
> ...
> Drawing the whole using wx.DC is quite an attractive option for me,
> since I already know how to do this. I use Paint for the wx.Panel classes.

I would try this approach, using a ScrolledWindow and a buffered DC.
+: no risk of slow-down due to many wx components
+: complete control of look and feel
-: have to write code to mimic the decorations supplied by wx, eg
borders, buttons, ...
-: needs a data structure to describe location on screen that can
be used by a) drawing code b) mouse handling code

I would have separate windows for the 1st row and column and update
them in response to the Draw events caused by scrolling.

Phil

Chris Barker

unread,
Aug 19, 2009, 2:17:41 AM8/19/09
to wxpytho...@googlegroups.com
Sam23 wrote:
> Drawing the whole using wx.DC is quite an attractive opaybe you tion for me,
> since I already know how to do this. I use Paint for the wx.Panel classes.
>
>> If you want to be able to zoom it , then FloatCanvas could help.
>>
> The full featured zoom in FloatCanvas would be nice,

Well, you get it for free, so why not?

> increase to 500 wx.Panels. Am I reaching any sorts of limits in
> wx.Python with these numbers?

I'm not sure, but that does seem like a lot.

> boxes). The 5-minute interval windows (almost 200) are even simpler,
> just background colour with no contents and no events.

maybe you don't need windows for that -- StaticBitmaps are not real
Windows on all platforms. Or is that what you are using anyway?

> Currently, the performance seems to be acceptable; nothing takes longer
> than a second,

a second is a pretty long time, actually.

> some testing and it looks like I can repaint just part of the window
> with (a).

yes, you only need to paint what has changed.

> As an example, when the user clicks on the time slot, it is highlighted
> with a yellow border. With the current many wx.Panels approach, I only
> need to redraw the selected wx.Panel. With the single large wx.Panel
> approach would I need to repaint the entire window?

nope, only what's been damaged, though you need to figure out what that is.

> It looks like I can
> specify which part of the screen to repaint. (using BufferedDC? and
> specifying where to paint?)

Buffering is a good idea too -- you can buffer the Calendar, then draw
the highlighted event over the buffer. You only need to re-blit the
buffer to restore it. FloatCanvas does something like this with the
"foreground".

> Actually, it will probably be less work than what I am doing currently.

maybe, but it adds up.

The more I think about it, the more I think FloatCanvas may help. I'm
going to see if I can whip up a bit of a demo...

Sam23

unread,
Aug 19, 2009, 9:49:40 AM8/19/09
to wxpytho...@googlegroups.com
Phil,
Thanks for the coherent and concise summary of the pros and cons of
going with ScrolledWindow and a bufferedDC. My replies below.

Phil Mayes wrote the following on 8/18/2009 11:14 PM:
> I would try this approach, using a ScrolledWindow and a buffered DC.
> +: no risk of slow-down due to many wx components
>

Yes, nice.


> +: complete control of look and feel
>

Yes, very nice.


> -: have to write code to mimic the decorations supplied by wx, eg
> borders, buttons, ...
>

Not too bad. Only the Calendar window is self-drawn. Other parts of the
frame can have panels using wx Buttons, Menu, status bar, etc. The
calendar is a table with text and some simple graphic objects (coloured
boxes), so it probaly won't look too out of place.


> -: needs a data structure to describe location on screen that can
> be used by a) drawing code b) mouse handling code
>

Yes, this will be the bulk of the work.


> I would have separate windows for the 1st row and column and update
> them in response to the Draw events caused by scrolling.
>

I think there may be some advantages with not using ScrolledWindow i.e.
draw everything using buffered DC only. If I use ScrolledWindow, I will
need to coordinate the scrolling events of the ScrolledWindow with that
of the row and column titles. This means that I am handling scrolling
using 2 different ways (one via ScrolledWindow and another via manual
redrawing). I am not sure I know how to do this; have to try. Thing is,
since I am going to have to 'scroll' the row and column labels, I might
as well handle the 'scroll' of the contents - i.e. only 1 set of logic
to code for; and no worries about scrolling becoming un-cordinated.


Sam23

unread,
Aug 19, 2009, 9:57:42 AM8/19/09
to wxPython-users
Chris,
On Aug 19, 2:17 pm, Chris Barker <Chris.Bar...@noaa.gov> wrote:
> The full featured zoom in FloatCanvas would be nice,
>> Well, you get it for free, so why not?
>>
Yes, it certainly would be a very nice bonus.
> boxes). The 5-minute interval windows (almost 200) are even simpler, just background colour with no contents and no events.
>> maybe you don't need windows for that -- StaticBitmaps are not real Windows on all platforms.
>> Or is that what you are using anyway?
I am using wx.Panels for everything... I copied the sample code for
sizers, - I think from the wx demo - and just modified it using
whatever was in it - in this case, wx.Panels :-)

But if using StaticBitmaps will conserve a lot of resources, I can
change all the 5-minute intervals to StaticBitMaps. All I need is to
be able to set the background colours - they don't need to respond to
events or have contents. So this can cut the number of wx.Panels
required by almost 200.

> a second is a pretty long time, actually.
>
I agree a second is an eternity for modern processors. On the old
laptop, I think it does take up to a second to show up, but on my
newer 1 year old laptop (also fairly modest specs), it is well under a
second. I didn't measure the exact time, but it seems reasonably quick
- quick enough for us non-nanosecond humans anyway. But yes, if it is
chewing a lot of resources to compute the screen, then I think it
would be good to fix it - especially if the design is flawed.
> The more I think about it, the more I think FloatCanvas may help. I'm going to see if
> I can whip up a bit of a demo...
>
That would be really, really great. If you can give a simple of demo
of what is a better way to do this. It would really help. It doesn't
have to be elaborate, just enough to give me some sample code to get
started with. You have seen what I did with a simple sample containing
just wx.Panels - albeit more like 'overdone' :-)

Robin Dunn

unread,
Aug 19, 2009, 2:43:05 PM8/19/09
to wxpytho...@googlegroups.com
Chris Barker wrote:
> Sam23 wrote:

>> increase to 500 wx.Panels. Am I reaching any sorts of limits in
>> wx.Python with these numbers?
>
> I'm not sure, but that does seem like a lot.

There is no explicit limit, other than resources that the OS provides.
But consider the difference between 500 paint events vs 1 paint event.

wxPython-us...@9ox.net

unread,
Aug 20, 2009, 7:40:49 AM8/20/09
to +wxpython-users+comverse+dcb368af...@spamgourmet.com
Hi,

What would you recommend I should use to give explanations of grid column headings ?

I have grids where the headings aren't obvious, and I need to explain what possible values the column cells could display.

In a similar situation, which wxPython widget would you use ?

Thanks,
Ron.

Mike Driscoll

unread,
Aug 20, 2009, 9:03:02 AM8/20/09
to wxPython-users
Ron,
I'd probably use tooltips of some sort or some kind of legend above
the grid. If it's really complicated, include a "legend" button that
will display a frame with directions for each column that can be re-
positioned so they can see it and your grid.

- Mike
Reply all
Reply to author
Forward
0 new messages