0

I want to add an arrow (some object) upon the press of a button. The code is a modified version of the example given in the vispy repository.

import wx
from vispy import scene
from vispy.scene.visuals import Arrow

import numpy as np

class Canvas(scene.SceneCanvas):
    def __init__(self, *a, **k):
        sizes = k.pop("sizes", (300, 300))  # Default value is (300, 300)
        scene.SceneCanvas.__init__(self, *a, **k, size=sizes)
        view = self.central_widget.add_view()
        view.bgcolor = 'snow'
        view.camera = scene.TurntableCamera(up='+y', azimuth=100, elevation=15, fov=60)
        axis = scene.visuals.XYZAxis(parent=view.scene)
        
        arrow1 = np.array([(0, 0, 0, 1, 1, 0)])  # Arrow direction, position
        arr = Arrow(pos=np.array([(0, 0, 0), (1, 1, 0)]), color='teal', method='gl', width=5., arrows=arrow1,
                    arrow_type="angle_30", arrow_size=5.0, arrow_color='teal', antialias=True, parent=view.scene)
        
        self.show()

class mainFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = "Add Object", pos = wx.DefaultPosition, size = wx.Size( 905,569 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

        bSizer1 = wx.BoxSizer( wx.VERTICAL )

        self.panel_vispy = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer1.Add( self.panel_vispy, 9, wx.EXPAND |wx.ALL, 5 )

        self.panel_Buttons = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )


        bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 )

        self.button_AddArrow = wx.Button( self.panel_Buttons, wx.ID_ANY, u"Add Arrow", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.button_AddArrow, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )


        bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 )


        self.panel_Buttons.SetSizer( bSizer2 )
        self.panel_Buttons.Layout()
        bSizer2.Fit( self.panel_Buttons )
        bSizer1.Add( self.panel_Buttons, 1, wx.EXPAND |wx.ALL, 5 )


        self.SetSizer( bSizer1 )
        self.Layout()

        self.Centre( wx.BOTH )
        
        # Connect Events
        self.button_AddArrow.Bind(wx.EVT_BUTTON, self.button_AddArrow_OnClick)
        
        self.panel_vispy.canvas = Canvas(app="wx", parent=self, sizes=self.panel_vispy.GetSize())
    
    def button_AddArrow_OnClick(self, event):
        print("Adding arrow")
        arrow2 = np.array([(0, 0, 0, 1, 1, 0)])  # Arrow direction, position
        arr = Arrow(pos=np.array([(0, 0, 0), (1, 1, 0)]), color='red', method='gl', width=5., arrows=arrow2,
                    arrow_type="angle_30", arrow_size=5.0, arrow_color='red', antialias=True)#, parent=view.scene) -Unable to access view from the Canvas class


if __name__ == "__main__":
    app = wx.App(False)
    
    GUI = mainFrame(None)
    
    GUI.Show(True)
    
    app.MainLoop()

I have created the Canvas as given in the example and tried to instantiate it from the mainFrame. All the objects that are given in the __init__ of the Canvas class are created. If I want to add any new object after the GUI is created with the press of a button, I am unable to access the view in the Canvas class from the mainFrame. Or is there any other way to achieve this.

Update: Here is the code after following the suggestions given in the comments:

import wx
from vispy import scene, gloo
from vispy.scene.visuals import Arrow

import numpy as np

class Canvas(scene.SceneCanvas):
    def __init__(self, *a, **k):
        sizes = k.pop("sizes", (300, 300))  # Default value is (300, 300)
        scene.SceneCanvas.__init__(self, *a, **k, size=sizes)
        self.unfreeze()
        self.view = self.central_widget.add_view()
        self.view.bgcolor = 'snow'
        self.view.camera = scene.TurntableCamera(up='+y', azimuth=100, elevation=15, fov=60)
        axis = scene.visuals.XYZAxis(parent=self.view.scene)
        
        arrow1 = np.array([(0, 0, 0, 1, 1, 0)])  # Arrow direction, position
        arr = Arrow(pos=np.array([(0, 0, 0), (1, 1, 0)]), color='teal', method='gl', width=5., arrows=arrow1,
                    arrow_type="angle_30", arrow_size=5.0, arrow_color='teal', antialias=True, parent=self.view.scene)
        
        self.show()
    
class mainFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = "Add Object", pos = wx.DefaultPosition, size = wx.Size( 905,569 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

        bSizer1 = wx.BoxSizer( wx.VERTICAL )

        self.panel_vispy = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer1.Add( self.panel_vispy, 9, wx.EXPAND |wx.ALL, 5 )

        self.panel_Buttons = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )


        bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 )

        self.button_AddArrow = wx.Button( self.panel_Buttons, wx.ID_ANY, u"Add Arrow", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.button_AddArrow, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )


        bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 )


        self.panel_Buttons.SetSizer( bSizer2 )
        self.panel_Buttons.Layout()
        bSizer2.Fit( self.panel_Buttons )
        bSizer1.Add( self.panel_Buttons, 1, wx.EXPAND |wx.ALL, 5 )


        self.SetSizer( bSizer1 )
        self.Layout()

        self.Centre( wx.BOTH )
        
        # Connect Events
        self.button_AddArrow.Bind(wx.EVT_BUTTON, self.button_AddArrow_OnClick)
        
        self.panel_vispy.canvas = Canvas(app="wx", parent=self, sizes=self.panel_vispy.GetSize())
    
    def button_AddArrow_OnClick(self, event):
        print("Adding arrow")
        arrow2 = np.array([(0, 0, 0, 1, 1, 0)])  # Arrow direction, position
        arr = Arrow(pos=np.array([(0, 0, 0), (1, 1, 0)]), color='teal', method='gl', width=5., arrows=arrow2,
                    arrow_type="angle_30", arrow_size=5.0, arrow_color='teal', antialias=True, parent=self.panel_vispy.canvas.view.scene)
        self.panel_vispy.canvas.update() # Canvas not getting updated


if __name__ == "__main__":
    app = wx.App(False)
    
    GUI = mainFrame(None)
    
    GUI.Show(True)
    
    app.MainLoop()

The code now runs without any error which means the arrow is probably added but the scene is not updated.

DVS
  • 43
  • 10
  • It looks like you've commented out the main thing I would have thought would make this work; mainly the `parent=view.scene`. You need to tell the visual where it should be attached via that parent property. – djhoese Sep 17 '20 at 18:35
  • Thanks for your reply. I just commented out so that the sample python code would run without any error. The problem is that I am unable to access the ```view``` object from a different class outside ```Canvas``` – DVS Sep 18 '20 at 06:21
  • Surely it would help accessibility, if you made `view` an `instance` variable of `Canvas` i.e. `self.view =` – Rolf of Saxony Sep 18 '20 at 08:17
  • What's wrong with `self.panel_vispy.canvas.view` after setting `self.view =` as @RolfofSaxony suggests? If you can't access the canvas properties then you can't update the canvas with new visuals. You need to be able to access parts of the canvas if you want to interact with it. – djhoese Sep 18 '20 at 13:28
  • Sure. I tried and got an Attribute error asking to ```unfreeze()``` the canvas to add the ```view``` to ```Canvas``` attributes. Solved the error by calling ```self.unfreeze()```. Now the above code runs with ```parent=self.panel_vispy.canvas.view.scene``` included. But I do not see the arrow in the scene. I tried calling the ```update()``` method of the ```Canvas``` -but no use. May be I have to refresh the ```view``` or ```scene```? How do I do that – DVS Sep 19 '20 at 02:09
  • The example given in the vispy repository demonstrates by updating ```gloo``` which I could not get to work for my sample code. – DVS Sep 19 '20 at 02:17
  • @DVS Could you provide an updated version of your code as an edit to your original question. It is hard to tell what is going on based on what you've described here. – djhoese Sep 25 '20 at 12:41
  • Just to be sure, are you seeing your print message "Adding arrow" when you click the button? – djhoese Sep 25 '20 at 19:07
  • Yes. I get the message – DVS Sep 26 '20 at 17:24
  • I could be wrong, but I'm noticing that it seems all of your properties for both the original arrow and the new arrow are the same, right? How do you know the new arrow isn't showing up if it is the exact same as the old arrow? – djhoese Sep 27 '20 at 15:24
  • 1
    @djhoese You are right. That's an embarrassing mistake to make. Not sure how this error crept in. The problem got resolved with the addition of ```self.unfreeze()```. I am working with arrows for the first time -took a while for me to figure out the Arrows visual, hence the confusion. Thank you for your patience and help. I will post the updated working code soon. – DVS Oct 02 '20 at 02:05

1 Answers1

0

Following the suggestions of @djhoese and @RolfofSaxony, I have updated the code. The code now successfully adds an arrow upon clicking the button.

import wx
from vispy import scene, gloo
from vispy.scene.visuals import Arrow

import numpy as np

class Canvas(scene.SceneCanvas):
    def __init__(self, *a, **k):
        sizes = k.pop("sizes", (300, 300))  # Default value is (300, 300)
        scene.SceneCanvas.__init__(self, *a, **k, size=sizes)
        self.unfreeze()
        self.view = self.central_widget.add_view()
        self.view.bgcolor = 'snow'
        self.view.camera = scene.TurntableCamera(up='+y', azimuth=100, elevation=15, fov=60)
        axis = scene.visuals.XYZAxis(parent=self.view.scene)
        
        arrow1 = np.array([(0, 0, 0, 1, 1, 0)])  # Arrow direction, position
        arr = Arrow(pos=np.array([(0, 0, 0), (1, 1, 0)]), color='red', method='gl', width=5., arrows=arrow1,
                    arrow_type="angle_30", arrow_size=5.0, arrow_color='blue', antialias=True, parent=self.view.scene)
        
        self.show()
        
class mainFrame ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = "Add Object", pos = wx.DefaultPosition, size = wx.Size( 905,569 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

        bSizer1 = wx.BoxSizer( wx.VERTICAL )

        self.panel_vispy = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer1.Add( self.panel_vispy, 9, wx.EXPAND |wx.ALL, 5 )

        self.panel_Buttons = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer2 = wx.BoxSizer( wx.HORIZONTAL )


        bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 )

        self.button_AddArrow = wx.Button( self.panel_Buttons, wx.ID_ANY, u"Add Arrow", wx.DefaultPosition, wx.DefaultSize, 0 )
        bSizer2.Add( self.button_AddArrow, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )


        bSizer2.Add( ( 0, 0), 1, wx.EXPAND, 5 )


        self.panel_Buttons.SetSizer( bSizer2 )
        self.panel_Buttons.Layout()
        bSizer2.Fit( self.panel_Buttons )
        bSizer1.Add( self.panel_Buttons, 1, wx.EXPAND |wx.ALL, 5 )


        self.SetSizer( bSizer1 )
        self.Layout()

        self.Centre( wx.BOTH )
        
        # Connect Events
        self.button_AddArrow.Bind(wx.EVT_BUTTON, self.button_AddArrow_OnClick)
        
        self.panel_vispy.canvas = Canvas(app="wx", parent=self, sizes=self.panel_vispy.GetSize())
    
    def button_AddArrow_OnClick(self, event):
        print("Adding arrow")
        arrow2 = np.array([(0, 0, 0, -1, -0.5, 1)])  # Arrow direction, position
        arr = Arrow(pos=np.array([(0, 0, 0), (-1, -0.5, 1)]), color='green', method='gl', width=5., arrows=arrow2,
                    arrow_type="angle_30", arrow_size=5.0, arrow_color='blue', antialias=True, parent=self.panel_vispy.canvas.view.scene)


if __name__ == "__main__":
    app = wx.App(False)
    
    GUI = mainFrame(None)
    
    GUI.Show(True)
    
    app.MainLoop()
DVS
  • 43
  • 10