LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-11-2009, 01:30 PM   #1
browny_amiga
Member
 
Registered: Dec 2001
Location: /mnt/UNV/Mlkway/Earth/USA/California/Silicon Valley
Distribution: Kubuntu, Debian Buster Stable, Windoze 7
Posts: 684

Rep: Reputation: 56
wxwidgets / wxpython drawing problems with onPaint event


Hi there

This is a question for both the wxwidgets and wxpythong crowd, since my problem probably stems from not understanding how wxwidgets work (and because wxpython is based on wxwidgets)

I am writing a python program in which I want to draw several circles (filled, actually musical notes) to a wx.PaintDC, then wait some seconds and then display some other circles and so on.

Now the problem is of course that the frame gets wiped out by the refresh immediately, so I need to use (and register) the onPaint event, which I do. (I am pretty sure that is the same in Python and C for WX) But putting the code in there creates a total mess. The onPaint should only refresh what is currently displayed, not run the code displaying some circles, waiting 3 seconds, then erasing everything, displaying some other circles and then wait another 3 and go on.

How can I create a frame (and panel) that I can draw to, erase it as wanted and that stays displayed (meaning that it automatically gets refreshed?)

I have tried out working with a memoryDC, in which I draw it there and then blit it to the normal dc with the onPaint function. It works fine, if I only draw one set of circles, but when I use time.sleep(3) to wait, nothing gets drawn (even though I call the blit manually) and only the last version gets displayed.

I would very much like to avoid onPaint at all, since I don't see the need for it and separating the event for painting and the code generating the drawing is complicated and error prone as I found.

Markus

Last edited by browny_amiga; 02-11-2009 at 01:31 PM.
 
Old 02-11-2009, 03:07 PM   #2
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
AFAICS you cannot do without an onPaint event handler, since it gets called by the system when your window is shown again after e.g. some other window had partly covered it.

So to solve this drawing-with-delays, I think you could maintain a python list where you append the circle positions as (x,y) tuples or so. And then in the onPaint iterate through the list and draw the circles.

Then instead of sleep(3) use a timer with 3 secs delay. The timer will call you OnTimer method and there you append the new circle positions to the list, and call self.refresh().

Hope this helps.
 
Old 02-12-2009, 12:04 PM   #3
browny_amiga
Member
 
Registered: Dec 2001
Location: /mnt/UNV/Mlkway/Earth/USA/California/Silicon Valley
Distribution: Kubuntu, Debian Buster Stable, Windoze 7
Posts: 684

Original Poster
Rep: Reputation: 56
that is somewhere to start. I have not worked with timers yet, so I so far fail to understand what they are for at all. (main purpose) But will look into it.

I am having another issue with that I have a frame that has buttons and stuff inside (main menu) and then I need to clean it all up to go to the section that the user chose with the button. So far I am doing this with frame.DestroyChildren(), but this might not be the proper way to do it. After I destroy everything like this, I add on the menus and relevant buttons again.

Markus
 
Old 02-12-2009, 12:31 PM   #4
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Quote:
Originally Posted by browny_amiga View Post
that is somewhere to start. I have not worked with timers yet, so I so far fail to understand what they are for at all. (main purpose) But will look into it.
With a sleep() your program really pauses and does not continue to run. Within the sleep-time the user might want to click some button or menu. The action your program should then take is delayed at least until the sleep() is done. Imagine you click a menu-item or button, but the program takes 3 seconds to react to that...

Or your window is covered and revealed within the sleep time. The contents of your window will then remain 'damaged' or not visible at all until the sleep() is over and your program runs again and the onpaint() function is called.

Not so with a timer. By setting a timer you tell the system: "Call this function in 3 seconds". After just telling that, your program continues to run: reacting to user actions, repaint the windows and so on. Then after 3 seconds the function will be run (make sure it does not take too long) automatically.

See?

Another way of achieving the same effect is running another thread which is sleeping and take some action after that. But threads are less easy to handle and understand correctly and it gets messy more easily.

Quote:
Originally Posted by browny_amiga View Post
I am having another issue with that I have a frame that has buttons and stuff inside (main menu) and then I need to clean it all up to go to the section that the user chose with the button. So far I am doing this with frame.DestroyChildren(), but this might not be the proper way to do it. After I destroy everything like this, I add on the menus and relevant buttons again.
You could use two or more panels in the same window and hide one behind the other.

Last edited by Hko; 02-12-2009 at 12:33 PM.
 
Old 02-12-2009, 02:00 PM   #5
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Here's a complete example program that implements a demo of the idea:
Code:
#!/usr/bin/env python

import wx
import random

class MainWindow(wx.Frame):
    def __init__(self, parent, title, size=wx.DefaultSize):
        wx.Frame.__init__(self, parent, wx.ID_ANY, title, wx.DefaultPosition, size)

        self.circles = list()
        self.displaceX = 30
        self.displaceY = 30

        self.timer = wx.Timer(self)
        self.timer.Start(1000) # 1000 milliseconds = 1 second

        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.Bind(wx.EVT_LEFT_UP, self.OnClick)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, e):
        print "OnPaint called"
        dc = wx.PaintDC(self)
        dc.SetPen(wx.Pen(wx.BLUE))
        dc.SetBrush(wx.Brush(wx.BLUE))

        # Go through the list of circles to draw all of them
        for circle in self.circles:
            dc.DrawCircle(circle[0], circle[1], 10)

    def OnTimer(self, e):
        print "OnTimer called"
        circlePos = (self.displaceX, self.displaceY)
        self.circles.append(circlePos)

        # Change position of the next circle that
        # we want to append to the list next time
        windowSize = self.GetClientSizeTuple()
        maxX = windowSize[0] - 30
        maxY = windowSize[1] - 30
        self.displaceX += 40
        if self.displaceX > maxX:
            self.displaceX = 30
            self.displaceY += 40
            if self.displaceY > maxY:
                self.timer.Stop()        
                print "Timer Stopped"
        self.Refresh()

    def OnClick(self, e):
        print "Window clicked"
        # Do something here to show the click was received.
        # Here we remove a random circle.
        n = len(self.circles)
        if n <= 1: # then dont do it
            return
        i = random.randrange(n)
        del self.circles[i]
        print "Removed %dth circle" % (i,)
        self.Refresh()
        
def main():
    app = wx.App()
    win = MainWindow(None, "Draw delayed circles", size=(620,460))
    win.Show()
    app.MainLoop()
   
if __name__ == "__main__":
    random.seed()
    main()
 
Old 02-13-2009, 01:56 PM   #6
browny_amiga
Member
 
Registered: Dec 2001
Location: /mnt/UNV/Mlkway/Earth/USA/California/Silicon Valley
Distribution: Kubuntu, Debian Buster Stable, Windoze 7
Posts: 684

Original Poster
Rep: Reputation: 56
Thanks a lot for the code! Working examples are always easiest for me to understand and analyze.

Cheers!

Markus
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
installation of wxwidgets and wxpython on suse 10.1 noorudin Linux - Distributions 1 11-06-2006 03:49 AM
[wxWidgets] how to unfroze the GUI while the event is running? sylvaticus Programming 10 10-24-2006 02:57 PM
Problems with wxWidgets DonBilbo Linux - Software 3 10-12-2005 09:21 AM
torsmo - problems drawing to root ingerul Linux - Software 1 02-16-2005 07:33 PM
java drawing problems/questions titanium_geek Programming 11 11-19-2003 05:12 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:21 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration