Wx.floatcanvas blog or forums

I pared the code down with the same problem, slow to draw lines and freezes at 13 lines. I have running it in both python 3.6 and 3.8 with wxpython 4.1.0 and get the same results. I have also tried it in VS Code and in terminal. Any help would be appreciated

import time
import string
import wx
from wx.lib.floatcanvas import NavCanvas, FloatCanvas
import wx.lib.colourdb


class InputForm(wx.Frame):
    '''set up the form and draw axis'''
    def __init__(self):
        super(InputForm, self).__init__(None, wx.ID_ANY, title='Plot Lines', size=(1300, 830))

        # set dictionary of points; key node letter, value tuple of point,
        self.pts = {}
        self.draw_repetitions = 0
        # create the form level sizer
        Main_Sizer = wx.BoxSizer(wx.HORIZONTAL)

        # add the sizer for the left side widgets
        sizerL = wx.BoxSizer(wx.VERTICAL)
        # add the grid and then set it ot he left panel

        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
        drw = wx.Button(self, -1, "Draw\nLines")
        btnsizer.Add(drw, 0, wx.ALL|wx.ALIGN_CENTER, 5)

        # bind the button events to handlers
        self.Bind(wx.EVT_BUTTON, self.OnDraw, drw)

        sizerL.Add((10, 20))
        sizerL.Add(btnsizer, 1, wx.ALIGN_CENTER)

        # add the draw panel
        self.rght = NavCanvas.NavCanvas(self,
                                   ProjectionFun=None,
                                   Debug=0,
                                   BackgroundColor="LIGHT GREY",
                                   )
        self.Canvas = self.rght.Canvas
        self.Canvas.ClearAll()

        Main_Sizer.Add(sizerL, 0, wx.EXPAND)
        Main_Sizer.Add((10, 10))
        Main_Sizer.Add(self.rght, 1, wx.EXPAND)
        self.SetSizer(Main_Sizer)

    def OnDraw(self, evt):
        pts1 = (0, 0)

        x = [i for i in range(5, 50, 2)]
        y = x[::-1]
        pts2 = [(x[i], y[i]) for i in range(0, len(x))]
        alph = string.ascii_uppercase

        LnLbls = [alph[i] for i in range(0, len(x))]

        New_EndPt = True
        n = 0
        for pt in pts2:
            points = []
            points.append(pts1)
            points.append(pt)
            LnLbl = LnLbls[n]
            New_EndPt = True
            n += 1
            self.DrawLine(points, LnLbl, New_EndPt)

    def DrawLine(self, points, LnLbl, New_EndPt):
        '''Draws the line object as specified in the VarifyData() function'''
        self.draw_repetitions += 1
        # label the end point of the line in lower case
        if New_EndPt is True:
            new_end = self.Canvas.AddScaledTextBox(LnLbl.lower(), tuple(points[1]),
                                                   Color='black',
                                                   Size=.5,
                                                   PadSize=.2,
                                                   Width=None,
                                                   LineColor=None,
                                                   Family=wx.MODERN,
                                                   Position='cc',
                                                   Alignment='bottom',
                                                   InForeground=True)

            new_end.Bind(FloatCanvas.EVT_FC_LEFT_DOWN,
                         lambda evt, selctEnd=LnLbl.lower():
                         self.EvtLeftDown(evt, selctEnd))

        # define the new line
        self.Canvas.AddLine(points, LineWidth=2, LineColor='red')
        # add the new line to the list of lines

        self.Canvas.AddPoint(tuple(points[1]), 'black', 8)

        # locate the center of the new line for the label location
        lncntr = ((int(points[0][0])+int(points[1][0]))//2,
                  (int(points[0][1])+int(points[1][1]))//2)

        # place the new line lable
        new_line = self.Canvas.AddScaledTextBox(LnLbl, lncntr,
                                                Color='red',
                                                Size=.5,
                                                PadSize=None,
                                                Width=None,
                                                LineColor=None,
                                                Family=wx.MODERN,
                                                Position='tc',
                                                Alignment='bottom',
                                                InForeground=True)
        new_line.Name = LnLbl

        tic = time.perf_counter()
        new_line.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.ObjLeftDown)
        toc = time.perf_counter()
        print(f'time to execute BIND function for DrawLine line ',LnLbl, toc-tic)
        print(f'Draw repetitions ',self.draw_repetitions)

       # wx.CallAfter(self.Canvas.ZoomToBB)
        self.Canvas.ZoomToBB()

    def ObjLeftDown(self, object):
        lbl = object.Name
        print('you have selected line ', lbl)

    def EvtLeftDown(self, evt, lbl):
        if lbl == 'Origin':
            print('you have selected the origin')
        elif 97 <= ord(lbl) <= 122:
            print('you have selected node ', lbl)


# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = InputForm()
    frame.Center()
    frame.Show()
    app.MainLoop()

Hi Paul,

  In the future, please attach code rather than pasting it in. 

It’s easier to run that way, since you’ve lost all your
underscores, etc.

  When I run your code, all lines get drawn in the range of 0.00013

to 0.00017 seconds, which does not seem unreasonable to me. What
are you expecting?

David

When i run the code I get the following print out of times then the program freezes after ploting only half the lines (13 repetitions), note the 2.7 seconds to draw line I at repetition 8. If I remove both floatcanvas Bind statements everything works fine. If I remove either one of the floatcanvas Bind statements the program plots all the lines (26) but has two, 2.5 second delays; one again at line I repetition 8 and the second at line R repetition 18.

time to execute BIND function for DrawLine line A 0.00010048300001130883
Draw repetitions 1
time to execute BIND function for DrawLine line B 0.00013951700000802703
Draw repetitions 2
time to execute BIND function for DrawLine line C 0.019158390999990615
Draw repetitions 3
time to execute BIND function for DrawLine line D 0.00030530699999076205
Draw repetitions 4
time to execute BIND function for DrawLine line E 8.94349999782662e-05
Draw repetitions 5
time to execute BIND function for DrawLine line F 0.013527425999996012
Draw repetitions 6
time to execute BIND function for DrawLine line G 0.00012891299999751027
Draw repetitions 7
time to execute BIND function for DrawLine line H 0.00011505499998065716
Draw repetitions 8
time to execute BIND function for DrawLine line I 2.7392678239999952
Draw repetitions 9
time to execute BIND function for DrawLine line J 0.00012510099998053192
Draw repetitions 10
time to execute BIND function for DrawLine line K 8.792299999527131e-05
Draw repetitions 11
time to execute BIND function for DrawLine line L 0.012627690999977403
Draw repetitions 12
time to execute BIND function for DrawLine line M 0.00017860200000541226
Draw repetitions 13

Is it possible I have an issue with my path or remnants of an older wxpython. I am new at this and am now at the guessing stage. Nothing I have tried works.

Didn’t notice the reply flag on the email and responded at the original
posting.

I’m using Windows 10, python 3.7.8, and wxPython
4.1.1a1.dev4845+73f75ea3 msw (phoenix) wxWidgets 3.1.4 and macOS
Catalina with a slightly newer wxPython daily build.

Here’s what I get on Windows:

  time to execute BIND function for DrawLine line A

0.00017120000000048208
Draw repetitions 1

  time to execute BIND function for DrawLine line B

0.0002449000000002144

  Draw repetitions 2

  time to execute BIND function for DrawLine line C

0.00024529999999955976

  Draw repetitions 3

  time to execute BIND function for DrawLine line D

0.0002448999999993262

  Draw repetitions 4

  time to execute BIND function for DrawLine line E

0.0001328999999996583

  Draw repetitions 5

  time to execute BIND function for DrawLine line F

0.00013390000000068625

  Draw repetitions 6

  time to execute BIND function for DrawLine line G

0.00013230000000064024

  Draw repetitions 7

  time to execute BIND function for DrawLine line H

0.0001335000000004527

  Draw repetitions 8

  time to execute BIND function for DrawLine line I

0.00013299999999993872

  Draw repetitions 9

  time to execute BIND function for DrawLine line J

0.00013299999999993872

  Draw repetitions 10

  time to execute BIND function for DrawLine line K

0.00013910000000016964

  Draw repetitions 11

  time to execute BIND function for DrawLine line L

0.00013290000000054647

  Draw repetitions 12

  time to execute BIND function for DrawLine line M

0.00013459999999998473

  Draw repetitions 13

  time to execute BIND function for DrawLine line N

0.00013419999999975118

  Draw repetitions 14

  time to execute BIND function for DrawLine line O

0.00013970000000007587

  Draw repetitions 15

  time to execute BIND function for DrawLine line P

0.00014019999999970167

  Draw repetitions 16

  time to execute BIND function for DrawLine line Q

0.00013430000000003162

  Draw repetitions 17

  time to execute BIND function for DrawLine line R

0.00013310000000021915

  Draw repetitions 18

  time to execute BIND function for DrawLine line S

0.00013320000000049959

  Draw repetitions 19

  time to execute BIND function for DrawLine line T

0.00013410000000035893

  Draw repetitions 20

  time to execute BIND function for DrawLine line U

0.0001324000000000325

  Draw repetitions 21

  time to execute BIND function for DrawLine line V

0.00013799999999974943

  Draw repetitions 22

  time to execute BIND function for DrawLine line W

0.00013380000000040582

  Draw repetitions 23

I get essentially the same results on macOS.

David

OK thanks for your input, my run times and freeze ups are consistent with both my computers. Its time to surrender, think I’ll learn matplotlib.

… the program plots all the lines (26) …

Actually, the lines are 23 because of course

>>> len(range(5, 50, 2))
23

That said… I really wouldn’t know… on my windows box the code you posted runs smoothly, and I can’t see a reason why it shouldn’t. Could there possibly be something you left out of the code you posted? I see a few loose ends and unused bits that make me think that perhaps your real code does a few other things… or your execution environment could be messed up for some reason… but I can’t guess why…

AFAIK, floatcanvas has no particular problem binding many objects at a time. For instance, this will draw and bind 200 lines, and you can increase the number at will:

class MainFrame(wx.Frame):
    def __init__(self, *a, **k):
        wx.Frame.__init__(self, *a, **k)
        self.wincanvas = NavCanvas.NavCanvas(self)
        self.canvas = self.wincanvas.Canvas
        self.draw()
        wx.CallAfter(self.canvas.ZoomToBB)

    def draw(self):
        center = (0, 0)
        step = 200 # will draw 200 lines
        radius = 500
        points = [(center[0] + (math.cos(2 * math.pi / step * x) * radius), 
                   center[1] + (math.sin(2 * math.pi / step * x) * radius)) 
                  for x in range(0, step)]
        for n, point in enumerate(points):
            self.canvas.AddLine([center, point], LineWidth=2)
            label = self.canvas.AddScaledTextBox(str(n), point, Size=5)
            label.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.onclic)

    def onclic(self, obj):
        print(obj.String)

if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame(None)
    frame.Maximize()
    frame.Show()
    app.MainLoop()

It simply has to be something in my environment, your code runs perfectly when the label.Bind statement is remarked out but will not run with it executing. I don’t have the skill set to be diving into my environmental set-up so I will just have to leave it and hope it doesn’t present itself somewhere else. It is strange though that this same issue is on both my ubuntu computers. Thanks for the time and effort.

I see… It could be a bug of course… floatcanvas does a lot of event manipulation under the hood, to allow linking every single object to its own event.
However, it’s not easy to pinpoint where the problem is, if you can’t reproduce it exactly. You should start over with a fresh virtual environment, install the current wxPython distribution, and run (from a shell session only!) some very easy code, possibly just this one:

import wx
from wx.lib.floatcanvas import NavCanvas, FloatCanvas

class MainFrame(wx.Frame):
    def __init__(self, *a, **k):
        wx.Frame.__init__(self, *a, **k)
        self.wincanvas = NavCanvas.NavCanvas(self)
        self.canvas = self.wincanvas.Canvas
        label = self.canvas.AddScaledTextBox('hello', (0, 0), Size=30)
        label.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.onclic)

    def onclic(self, obj):
        print('hello')

if __name__ == "__main__":
    app = wx.App(False)
    frame = MainFrame(None)
    frame.Maximize()
    frame.Show()
    app.MainLoop()

If this one works, try making it a little more complex, for instance by drawing a few more things:

        # ...
        for i in range(10):
            label = self.canvas.AddScaledTextBox('hello', (0, i*50), Size=30)
            label.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.onclic)
        # ...

When you hit your bug, file it here (and/or on the bug tracker), including the code and the exact specifics of your environment. Then maybe someone will be able to reproduce and investigate it.

FWIW, (which isn’t much by this point,) I was able to reproduce
your issue on Ubuntu 18.04 using the OS version of python 3.6.9
and wxPython 4.1.0.

  Program flow was very uneven, with odd pauses here and there. 

Taking the Bind line out did prevent the crash, but not the uneven
program flow, which seemed to happen at different places in your
code as the program ran.

  As I said before, the code worked perfectly on Windows and macOS,

so it appears to be a Linux-specific issue.

David

That is good to hear at least its not my machine set-up. I can only think it is a bug in floatcanvas lib. If i was more proficient I would try what ricpol suggested, just seems a little beyond me.

@DavidW uhm, I lost track… which code exactly will fail?

Paul_Henry posted code on Aug. 12 that drew a series of lines on
a FloatCanvas, noting that the code was taking a long time to bind
FloatCanvas objects and would fail after a number of iterations.
It only worked when you commented out the “Bind” line.

  I tested it on Windows and Mac and it worked fine.  IIRC, you

tested it as well with no problem. In a later post, Paul_Henry
mentioned he was on Ubuntu. So I tested it on Ubuntu, after
fighting a bit to get wxPython 4.1.0 installed, and I see the
problem he described originally there.

I’ve attached the code again for your convenience.

David

FloatCanvasIssue.py (4.87 KB)

I have also tried to get a resolution on StackOverflow and was conversing with Rolf of Saxony he provided this information about his system which ran the code without issue.
@user9133067 python 3.6.9 | wxpython 4.1.0 gtk2 (phoenix) wxWidgets 3.1.4 | Mint 19 (Ubuntu 18) | on a tiny 4 core Nuc @ 1.99Ghz – [Rolf of Saxony] The thread was titled wxPython FloatCanvas event Binding

@DavidW I see, thank you.
On the other hand, just as a sanity check, can you confirm that my previous little “200 lines” example will work on your ubuntu environment?

I hate being picky - on the one hand, if the same code runs on windows but hangs on wxGtk, there must be a bug there somewhere. On the other hand, the problem with the code of the OP is that is way too complex and does too many things to help me see the actual problem. Maybe someone with more experience and a keener eye will easily spot the critical part… But it would help if the OP simplified his code down to twenty lines or so, which attempt to do some very reasonable and plain drawing, and fail.

Ricpol,

  After adding the necessary imports, your sample runs on Windows

but not Ubuntu for me.

  I rarely work on Linux, so it's possible I have the same

environment setup issues as OP. I had to fight a bit to get
wxPython installed on Ubuntu, and am just using the system python
3.6 that Ubuntu installed.

David

Yes I tried the code for the ‘200 lines’ it will not run unless the ‘label.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.onclic)’ is commented out. With out the statement it runs as would be expected.

Well, apparently this settles it. Feel free to file an issue, specifying the code, the environment and whatever. I’ll try to look at it as soon as I have a little time, but hopefully Robin or someone else will step in first.

DrawObject.Bind does a heck of a lot more than simply binding the event type to a handler. (Like creating and/or updating bitmaps to be used to aid with hit testing, creating a pen and brush, etc.) So it’s not too surprising that in some cases it could get… um, I guess congested would be a good term. Especially when many of those operations will require a round trip to the X-server like on a Linux build.

OTOH, it would probably be possible to streamline that, or perhaps defer some of it until it is actually needed, and end up with some performance gains, if somebody felt like putting some effort into it.

BTW,

Pasting is fine (and preferred by some) as long as the code is enclosed in triple backticks. Indents should not get lost that way. I edit messages on discuss.wxpython.org and add in the backticks when they come in without them, but that doesn’t help email users when the message has already been sent.

BTW2, @DavidW have you seen what your messages with the leading paragraph indents look like on the website? :wink: