[wxPython] Line Benchmark, Tkinter vs. wxPython

Hello All,

In November of 1999 I did some benchmark tests to gauge the performance
difference between Tkinter and wxPython when large numbers of lines are
plotted. At that time wxPython outperformed Tkinter by a large margin as
can be seen in the results that are repeated below. As a full year has
passed and there are new versions of Python, Tkinter and wxPython, I felt
that it was time to rerun the tests to see if there were any significant
changes for better or worst.
While Tkinter has improved significantly, it is still far behind wxPython in
this aspect by a margin of 20 to 30 times.

The same computer was used to generate the new set of results, so the
results are directly comparable. As described below, a repeating sine wave
was created and put into a horizontal scrollable window. These are the
results
for the line plotting time only. Time in Seconds. (W95, Python 2.0,
wxPython 2.2.2)

Points Tkinter Tkinter Tkinter wxPython wxPython
                (standard) (mod 1) (mod 2) (direct draw) (bitmap)
1000 0.163 0.162 0.157 0.0078 0.028
2000 0.324 -- -- 0.0130
0.041
5000 0.817 -- -- 0.0390
0.092
10000 1.62 1.63 1.64 0.0590 0.157

See the notes below for more information on the configuration for each run.

Conclusions:
1) The biggest change is in the performance of Tkinter (standard).
Previously, the time to plot lines using the standard Tkinter was quadratic
with the
number of points (ugh!). Now it is linear and the results are very similar
to both of the modified Tkinter runs. An improvement was made to speed up
the_flatten method where most of the time was spent previously.

2) Tkinter mod 1 and 2 give almost the same results. There is a slight
improvement over the last years results by 15 to 20%. Some other changes
must have been made to the code to get these efficiencies.

3) wxPython results have changed very little. Maybe they have gotten 5 to
10% slower, but it is difficult to say without doing a more though study.
Direct drawing onto to screen is still roughly 3 times faster than using a
bitmap.

4) wxPython still beats Tkinter by a wide margin. Although the number are
not as huge as they were a year ago, wx still goes 20 - 30 times faster.

If you need raw speed to get large numbers of lines out to the screen,
wxPython is the way to go.

It would be interesting to have this repeated on a Unix box to get
comparable results.

Regards,

Gordon Williams
Dec. 2000

···

########################################################
November 1999 (Windows 95, Python 1.5.2, wxPython ?)

I have done some benchmark testing to see the difference in speed between
plotting multi point lines using Tkinter and wxPython. I have looked at 3
different methods for doing it in Tk and 2 different methods in wx. The
differences are astounding for the 1000 to 10000 point lines that I
examined. Generally, wx was found to be in the order of 200 to 1800 times
faster than the standard Tkinter. Tk can be sped up by ten to 50 times by
changing Tkinter so it bypasses the _flatten method or by making the Tk
calls directly.

A repeating sine wave was created and put into a horizontal scrollable
window of about 500 points wide and 300 points high. These are the results
for the line plotting time only.Time in Seconds on a P166 with 96MB RAM.

Points Tkinter Tkinter Tkinter wxPython wxPython
                (standard) (mod 1) (mod 2) (direct draw) (bitmap)
1000 1.190 0.192 .202 0.007 0.026
2000 4.406 0.385 -- 0.014 0.042
5000 25.170 0.967 -- 0.027 0.080
10000 100.260 1.941 2.064 0.054 0.148

Notes:
1) Tk standard is the standard usage with no modifications.
2) Tk mod 1 has Tkinter modified so a flat tuple of points is passed to
create_line and _flatten is by-passed.
3) Tk mod 2 used the apply method e.g.
apply(self.can.tk.call,(self.can._w,'create','line') + points +
('-width',1,'-fill',colour,'-tags',"tag"))
4) wx direct draw draws to the window directly.
5) wx bitmap draws to a bitmap and then blit

Conclusions:
1) The time to plot lines using the standard Tkinter is quadratic with the
number of points (ugh!). This is due to the time spent in _flatten.
_flatten is very versatile but agonizingly slow for large numbers of lines.
2) Tkinter mod 1 and 2 give the same results. Times are linear with the
number of points plotted as both cases dont use the _flatten method. This
means that a flat tuple of points has to be used (a small constraint). The
speed increase in doing this is from 10 to 50 times over the standard.
3) wxPython beats Tk by a very! large margin even when Tk is called
directly. Direct drawing onto to screen is roughly 3 times faster than
using a bitmap.

Thanks to:
Greg Landrum for providing some information on direct calls to tk.
Ionel Simionescu for providing the wxPython code that I was able to modify.

############################################################
I know I am going to get questions on where is the code, so here it is. I
have not supplied my version of Tkinter (Tkinter_gw.py) because it is large.

############################################################
#Ionel2_test_spd.py (for draw to bitmap)
__author__ = "Ionel Simionescu" # messed up by Gordon Williams
#---------------------------------------------------------------------------
from wxPython.wx import *
import time
from math import sin

#---------------------------------------------------------------------------
t0 = time.clock()

N = 1000 # the number of line points

points = [] # here we store the points
for k in range(N):
    points.append( (k, 50+ 50*sin(k/10.)) )
#print points
#print points

t1 = time.clock()
print 'Data is ready! Total data preparation time: %f ' % (t1 - t0)

#---------------------------------------------------------------------------

bmpW = N #changed
bmpH = 300

#---------------------------------------------------------------------------
class myCanvas(wxScrolledWindow):
    def __init__(self, parent, ID, position, size):
        wxScrolledWindow.__init__(self, parent, ID, position, size,
wxSUNKEN_BORDER)
        self.SetBackgroundColour(wxNamedColor("WHITE"))

        self.maxWidth = 500
        self.maxHeight = 300
        self.SetScrollbars(20, 0, N/20, 0) #self.maxWidth/20,
self.maxHeight/20)

        self.bmp = wxEmptyBitmap(bmpW, bmpH, -1)
        #self.bmp.Create(bmpW, bmpH, -1)
        #
        self.bmp_w = self.bmp.GetWidth()
        self.bmp_h = self.bmp.GetHeight()

        # flag used to redraw only when necessary
        self.new_data = 1
        EVT_PAINT(self, self.OnPaint)

    def OnPaint(self, event):
        dc = wxPaintDC(self)
        self.PrepareDC(dc)
        self.DoDrawing(dc)

    def DoDrawing(self, dc):
        t0 = time.clock()
        pen1 = wxPen(wxNamedColour('RED'))

        tmp_dc = wxMemoryDC()
        tmp_dc.SelectObject(self.bmp)

        # if new data, update the bitmap drawing
        if self.new_data:
            tmp_dc.BeginDrawing()
            tmp_dc.Clear()
            tmp_dc.SetPen(pen1)
            # draw the lines
            tmp_dc.DrawLines( points ) #changed
            tmp_dc.EndDrawing()
            # reset the new_data flag
            self.new_data = 0

        # copy the bitmap to the canvas
        dc.Blit( 0, 0, self.bmp_w, self.bmp_h, tmp_dc, 0, 0 )

        t1 = time.clock()
        print 'Plotting is ready! Total drawing time: %f ' % (t1 - t0)

#---------------------------------------------------------------------------
class myFrame(wxFrame):
    def __init__(self):
        wxFrame.__init__(
            self, NULL, -1, "wx_plot_lines",
            wxDefaultPosition, size= (500,300)#wxSize(500, 300)
        )
        myCanvas(self, -1, wxPoint(0,0), self.GetClientSize())

#---------------------------------------------------------------------------
class myApp(wxApp):
    def OnInit(self):
        myFrame().Show(TRUE)
        return TRUE

#---------------------------------------------------------------------------
if __name__ == '__main__':
    app = myApp(0)
    app.MainLoop()

#######################################################
#Ionel1_test_spd.py (for direct draw method)
__author__ = "Ionel Simionescu" # messed up by Gordon Williams
#---------------------------------------------------------------------------
from wxPython.wx import *
from math import sin
import time

#---------------------------------------------------------------------------
t0 = time.clock()

N = 10000 # the number of line points

points = [] # here we store the points
for k in range(N):
    points.append( (k, 50+ 50*sin(k/10.)) )
#print points

t1 = time.clock()
print 'Data is ready! Total data preparation time: %f ' % (t1 - t0)

#---------------------------------------------------------------------------
class myCanvas(wxScrolledWindow):
    def __init__(self, parent, ID, position, size):
        wxScrolledWindow.__init__(self, parent, ID, position, size,
wxSUNKEN_BORDER)
        self.SetBackgroundColour(wxNamedColor("WHITE"))

        self.maxWidth = 500
        self.maxHeight = 300
        self.SetScrollbars(20, 0, N/20, 0) #self.maxWidth/20,
self.maxHeight/20)
        EVT_PAINT(self, self.OnPaint)

    def OnPaint(self, event):
      dc = wxPaintDC(self)
      self.PrepareDC(dc)
      self.DoDrawing1(dc)

    def DoDrawing1(self, dc):
        t0 = time.clock()

        dc.BeginDrawing()
        pen1 = wxPen(wxNamedColour('RED'))
        dc.SetPen(pen1)
        #
        # draw line_points
        dc.DrawLines(points, 0)
        #
        dc.EndDrawing()

        t1 = time.clock()
        print 'Plotting is ready! Total drawing time: %f ' % (t1 - t0)

class myFrame(wxFrame):
    def __init__(self):
        wxFrame.__init__(
            self, NULL, -1, "wx_plot_lines",
            wxDefaultPosition,wxSize(500,300)
        )

        myCanvas(self, -1, wxPoint(0,0), self.GetClientSize())

#---------------------------------------------------------------------------
class myApp(wxApp):
    def OnInit(self):
        myFrame().Show(TRUE)
        return TRUE

#---------------------------------------------------------------------------
def test():
    app = myApp(0)
    app.MainLoop()

if __name__ == '__main__':
    test()

##########################################################
# File for benchmarking multi-point lines
# in wxPython and Tkinter.
#
# This file is for Tkinter
# Change the commented sections as required.
#
# Author: Gordon Williams 99/11/15
#

import time
from math import sin
#from Tkinter_gw import *
from Tkinter import *

class LayoutFrames:
    def __init__(self,master):
        # ********** Frame Layouts ***************
        # Main Frame Layout

        self.data= Frame(master)
        self.data.pack(side=TOP,fill=X)

        self.scroll= Frame(self.data)
        self.scroll.pack(side=BOTTOM,fill=X)

class Plot:

    def __init__(self, master, master_scroll=None):

# dataframe with canvas and scollbar
        self.scrollbar = Scrollbar(master_scroll, orient=HORIZONTAL)
        self.can = Canvas(master,cursor="crosshair", width=500,
height=300,relief=RIDGE, bd=2,bg="lightyellow",
xscrollcommand=self.scrollbar.set)
        self.scrollbar.config(command=self.can.xview)
        self.scrollbar.pack(side=BOTTOM, fill=X)
        self.can.pack(side=LEFT)

    def makelines(self,points, tag="misc",colour="black"):
        t0 = time.clock()
#>>>> comment out as required (use for standard and mod1 runs)
        #self.can.create_line(points,width= 1, tags=tag, fill=colour)

#>>>> comment out as required (use for mod2 runs)
        apply(self.can.tk.call,(self.can._w,'create','line') + points +
            ('-width',1,'-fill',colour,'-tags',"tag"));

        self.can.config(scrollregion=self.can.bbox(ALL))

        t1 = time.clock()
        print 'Plotting is ready! Total drawing time: %f ' % (t1 - t0)

class PlotResults:
    def __init__(self, dataFrame, scrollFrame):

    # ********** PLOTTING DATA ***************
        self.analog = Plot(dataFrame,scrollFrame)

    def analogPlot(self):
        self.analog.makelines(linePoints, "dataA","black" )

#************ start of main **************************
def main():

    root= Tk()
    frame=LayoutFrames(root)

    analog=PlotResults( frame.data, frame.scroll)
    analog.analogPlot()

    root.mainloop()
    try:
        root.destroy() # removes root
    except:
        pass

# make a bit of data
N= 1000 # number of points
points = [] # here we store the points
for k in range(N):
    points.extend( [k, 50+ 50*sin(k/10.)] )
#print points

linePoints= tuple(points) # must be in tupple form for Tkinter_gw
main()
#######################################################
Enjoy!!

_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users

I'm trying to set up a toolbar where I have some toggle buttons with
bitmaps on them.

From the demo and the docs, I have figured out how to get the buttons

there, and how to set up the events when they get clicked. My question
is this:

How can I tell the toggle status of a given button? The button changes
toggle status each time it's pressed, but I don't know how to know where
its status when the button was clicked.

How can I set the toggle status? I need to be able to turn the toggle
mode on or off when other things happen.

-thanks, Chris

···

--
Christopher Barker,
Ph.D.
cbarker@jps.net --- --- ---
http://www.jps.net/cbarker -----@@ -----@@ -----@@
                                   ------@@@ ------@@@ ------@@@
Water Resources Engineering ------ @ ------ @ ------ @
Coastal and Fluvial Hydrodynamics ------- --------- --------
------------------------------------------------------------------------
------------------------------------------------------------------------

_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users

I'm having trouble figuring out how to use a toggle button in wxToolbar.
(wxPython, but if someone can tell me what I need to know in C++, I'll
be able to translate)

I put the button there with:

tool = tb.AddTool(ID_ZOOM_IN_BUTTON,
                  wxBitmap('Mag_plus2.bmp', wxBITMAP_TYPE_BMP),
                  toggle=true)

(tb is a wxToolbar)

It works as advertised: each time I click the button, the toggle state
changes, and the function I tied to that event gets called. I can use:

tool.IsToggled()

to find out the toggle state.

How do I change the toggle state? I have tried using tool.SetToggle(1)
and tool.SetToggle(0) and it sort of works, in that the answer changes
when I call tool.IsToggled(), but the displayed state doesn't change. Is
this a bug? or am I missing something? I really want to be able to do
this!

This is wxWindows -2.1.16

if something relevant has changed since then, this may be my reason for
upgrading.

Note: what I really want is for a set of tools on the toolbar to act
like radio buttons. They will be used to set and indicate the mode that
the window is in. When one is pressed, it should look pressed, and the
others need to all be un-pressed. Is there a better way to do this?

Thanks,

-Chris

···

--
Christopher Barker,
Ph.D.
cbarker@jps.net --- --- ---
http://www.jps.net/cbarker -----@@ -----@@ -----@@
                                   ------@@@ ------@@@ ------@@@
Water Resources Engineering ------ @ ------ @ ------ @
Coastal and Fluvial Hydrodynamics ------- --------- --------
------------------------------------------------------------------------
------------------------------------------------------------------------

_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users

How do I change the toggle state? I have tried using tool.SetToggle(1)
and tool.SetToggle(0)

Have you tried using wxToolBar::ToggleTool()?

This is wxWindows -2.1.16

if something relevant has changed since then, this may be my reason for
upgrading.

The main reason is that you can be pretty sure that any bug reports
against 2.1.16 will be just ignored (speaking for myself anyhow).

Note: what I really want is for a set of tools on the toolbar to act
like radio buttons. They will be used to set and indicate the mode that
the window is in. When one is pressed, it should look pressed, and the
others need to all be un-pressed. Is there a better way to do this?

The radio toolbar buttons are in the TODO list but it hasn't been done
yet. Anyhow, this is how it would work inside wxWin too.

Regards,
VZ

···

On Thu, 28 Dec 2000, Chris Barker wrote:

_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users

Vadim Zeitlin wrote:

> How do I change the toggle state? I have tried using tool.SetToggle(1)
> and tool.SetToggle(0)

Have you tried using wxToolBar::ToggleTool()?

Thanks, I've got it working now.

The radio toolbar buttons are in the TODO list but it hasn't been done
yet. Anyhow, this is how it would work inside wxWin too.

Great. I look forward to seeing them. In the meantime, it's not hard to
figure out how to make it work.

-Chris

···

--
Christopher Barker,
Ph.D.
cbarker@jps.net --- --- ---
http://www.jps.net/cbarker -----@@ -----@@ -----@@
                                   ------@@@ ------@@@ ------@@@
Water Resources Engineering ------ @ ------ @ ------ @
Coastal and Fluvial Hydrodynamics ------- --------- --------
------------------------------------------------------------------------
------------------------------------------------------------------------

_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users