简体   繁体   English

wxPython FloatCanvas 事件绑定

[英]wxPython FloatCanvas event Binding

I am using Python 3.6 with wxPython 4.1.0 gtk3 (phoenix) wxWidgets 3.1.4.我正在使用 Python 3.6 和 wxPython 4.1.0 gtk3 (phoenix) wxWidgets 3.1.4。 There is a delay in the Bind function on every 4th line that i try to plot and after about 12 lines the program crashs.在我尝试 plot 的每 4 行绑定 function 有一个延迟,大约 12 行后程序崩溃。 Normally my code uses a grid to input the points for the lines but the attached code generates the data once the Draw button is clicked with exactly the same effect.通常,我的代码使用网格来输入线条的点,但附加的代码会在单击 Draw 按钮后生成数据,效果完全相同。 The delay is about 2.5 seconds on the 4th line normally it takes about.1 secs to plot a line.第 4 行的延迟约为 2.5 秒,通常需要约 1 秒到 plot 一行。 I have tried two functions for the Bind call one uses the event the other calls the object.我已经为绑定调用尝试了两个函数,一个使用事件,另一个调用 object。 If anyone knows a work around can you please let me know.如果有人知道解决方法,请告诉我。

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 = {}

        # 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
        rght = NavCanvas.NavCanvas(self,
                                   ProjectionFun=None,
                                   Debug=0,
                                   BackgroundColor="LIGHT GREY",
                                   )
        self.Canvas = rght.Canvas

        self.InitCanvas()

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

    def InitCanvas(self):
        # add the x & y axis
        self.Canvas.AddLine([(0, 0), (0, 5)], LineWidth=2, LineColor='Yellow')
        self.Canvas.AddLine([(0, 0), (5, 0)], LineWidth=2, LineColor='Green')
        origin = self.Canvas.AddScaledTextBox('origin', (0, 0),
                                              Color='blue',
                                              Size=.5,
                                              PadSize=0,
                                              Width=None,
                                              LineColor=None,
                                              Family=wx.MODERN,
                                              Position='tr',
                                              Alignment='bottom',
                                              InForeground=True)
        # first Bind of node to EvtLeftDown
        origin.Bind(FloatCanvas.EVT_FC_LEFT_DOWN,
                    lambda evt, selctEnd='Origin':
                    self.EvtLeftDown(evt, 'Origin'))

        wx.CallAfter(self.Canvas.ZoomToBB)

    def OnDraw(self, evt):
        pts1 = (0, 0)
        
        x = [i for i in range(5, 30, 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'''

        # 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 136 = {toc-tic:0.2f}')

        wx.CallAfter(self.Canvas.ZoomToBB)

    def ObjLeftDown(self, object):
        lbl = object.Name

        if lbl == 'Origin':
            self.Node(lbl)
        elif 65 <= ord(lbl) <= 90:
            print('you have selected line ', lbl)
        elif 97 <= ord(lbl) <= 122:
            print('you have selected node ', 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()

With minor adjustments to your code, I am unable to replicate the delays that you refer to.通过对您的代码进行细微调整,我无法复制您所指的delays
No matter how many times I re-draw the lines, the timings, always fall within roughly the same time frame.无论我重新画线多少次,时间总是在大致相同的时间范围内。
The bindings are to different objects, and it's just binding an event to an object, so I doubt that that is the issue.绑定是针对不同的对象,它只是将一个事件绑定到 object,所以我怀疑这是问题所在。

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.InitCanvas()

        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 InitCanvas(self):
        # add the x & y axis
        self.Canvas = self.rght.Canvas
        self.Canvas.ClearAll()
        self.Canvas.AddLine([(0, 0), (0, 5)], LineWidth=2, LineColor='Yellow')
        self.Canvas.AddLine([(0, 0), (5, 0)], LineWidth=2, LineColor='Green')
        origin = self.Canvas.AddScaledTextBox('origin', (0, 0),
                                              Color='blue',
                                              Size=.5,
                                              PadSize=0,
                                              Width=None,
                                              LineColor=None,
                                              Family=wx.MODERN,
                                              Position='tr',
                                              Alignment='bottom',
                                              InForeground=True)
        # first Bind of node to EvtLeftDown
        origin.Bind(FloatCanvas.EVT_FC_LEFT_DOWN,
                    lambda evt, selctEnd='Origin':
                    self.EvtLeftDown(evt, 'Origin'))

        wx.CallAfter(self.Canvas.ZoomToBB)

    def OnDraw(self, evt):
        self.InitCanvas()
        pts1 = (0, 0)
        
        x = [i for i in range(5, 30, 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

        if lbl == 'Origin':
            self.Node(lbl)
            print(dir(self.Node))
        elif 65 <= ord(lbl) <= 90:
            print('you have selected line ', lbl)
        elif 97 <= ord(lbl) <= 122:
            print('you have selected node ', 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)
#        try:
#            evt.Skip()
#        except:
#            pass

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

在此处输入图像描述

TL;DR : As a workaround don't install wxpython via pip inside a venv, install the ubuntu package python3-wxgtk4.0 . TL;DR :作为一种解决方法,不要在 venv 中通过 pip 安装 wxpython,而是安装 ubuntu ZEFE90A8E604A7C840E88D03A6gtF6B7D8Z python3-wxk

I was facing the same issue you ran into - at least I think it is the same issue - and was, in a way, able to solve it.我面临着你遇到的同样的问题——至少我认为这是同样的问题——并且在某种程度上能够解决它。 Running the code provided in the answer by Rolf of Saxony, the program would freeze for minutes when trying to draw the 12th or so line.运行 Saxony 的 Rolf 在答案中提供的代码,程序在尝试绘制第 12 条左右的线时会冻结几分钟。 After some minutes however, I at least got an exception out of it:然而,几分钟后,我至少得到了一个例外:

    Traceback (most recent call last):
  File "/bug_test.py", line 92, in OnDraw
    self.DrawLine(points, LnLbl, New_EndPt)
  File "/bug_test.py", line 138, in DrawLine
    new_line.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.ObjLeftDown)
  File "/env/lib/python3.8/site-packages/wx/lib/floatcanvas/FCObjects.py", line 236, in Bind
    self.HitColor = next(self._Canvas.HitColorGenerator)
StopIteration

Which to me looks like some problem with the generation of the hit color.对我来说,这看起来像是命中颜色的生成问题。 I guess the iterator generating it is for some reason not able to come up with an adequate hit color and thus runs for almost ever.我猜生成它的迭代器由于某种原因无法提供足够的命中颜色,因此几乎永远运行。 Although interesting, this did not help in solving the problem.虽然很有趣,但这无助于解决问题。 What helped was leaving my virtual environment.离开我的虚拟环境有帮助。 Initially I was running everything inside an venv and installed wxpython via pip.最初,我在 venv 中运行所有内容,并通过 pip 安装了 wxpython。 When I left the venv and installed wxpython via the ubuntu package python3-wxgtk4.0, everything worked as expected.当我离开 venv 并通过 ubuntu package python3-wxgtk4.0 安装 wxpython 时,一切都按预期工作。 So this is the workaround I found.所以这是我找到的解决方法。 Nevertheless the problem inside the venv persists.尽管如此,venv内部的问题仍然存在。 I suspect it might have something to do with the installation via pip which builds wxpython from source.我怀疑它可能与通过 pip 安装有关,它从源代码构建 wxpython。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM