1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo 2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others. 3# SUMOPy module 4# Copyright (C) 2012-2017 University of Bologna - DICAM 5# This program and the accompanying materials 6# are made available under the terms of the Eclipse Public License v2.0 7# which accompanies this distribution, and is available at 8# http://www.eclipse.org/legal/epl-v20.html 9# SPDX-License-Identifier: EPL-2.0 10 11# @file test_glcanvas.py 12# @author Joerg Schweizer 13# @date 14# @version $Id$ 15 16''' 17@author: Stou Sandalski (stou@icapsid.net) 18@license: Public Domain 19''' 20 21# Uncomment if you have multiple wxWidgets versions 22#import wxversion 23# wxversion.select('2.8') 24 25import math 26import wx 27 28from wx import glcanvas 29from wx.lib.buttons import GenBitmapTextButton, GenBitmapButton 30try: 31 from OpenGL.GL import * 32 from OpenGL.GLU import * 33 from OpenGL.GLUT import * 34 from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \ 35 glBindVertexArray 36 37 from OpenGL.arrays import vbo 38 import numpy as np 39 40except ImportError: 41 raise ImportError, "Required dependency OpenGL not present" 42 43import sys 44import os 45import types 46APPDIR = os.path.join(os.path.dirname(__file__), "..") 47sys.path.append(os.path.join(APPDIR, "lib_base")) 48IMAGEDIR = os.path.join(os.path.dirname(__file__), "images") 49import classman as cm 50 51# wx gui stuff 52from wxmisc import * 53 54import objpanel 55import wxmisc 56 57stockIDs = [ 58 wx.ID_ABOUT, 59 wx.ID_ADD, 60 wx.ID_APPLY, 61 wx.ID_BOLD, 62 wx.ID_CANCEL, 63 wx.ID_CLEAR, 64 wx.ID_CLOSE, 65 wx.ID_COPY, 66 wx.ID_CUT, 67 wx.ID_DELETE, 68 wx.ID_EDIT, 69 wx.ID_FIND, 70 wx.ID_FILE, 71 wx.ID_REPLACE, 72 wx.ID_BACKWARD, 73 wx.ID_DOWN, 74 wx.ID_FORWARD, 75 wx.ID_UP, 76 wx.ID_HELP, 77 wx.ID_HOME, 78 wx.ID_INDENT, 79 wx.ID_INDEX, 80 wx.ID_ITALIC, 81 wx.ID_JUSTIFY_CENTER, 82 wx.ID_JUSTIFY_FILL, 83 wx.ID_JUSTIFY_LEFT, 84 wx.ID_JUSTIFY_RIGHT, 85 wx.ID_NEW, 86 wx.ID_NO, 87 wx.ID_OK, 88 wx.ID_OPEN, 89 wx.ID_PASTE, 90 wx.ID_PREFERENCES, 91 wx.ID_PRINT, 92 wx.ID_PREVIEW, 93 wx.ID_PROPERTIES, 94 wx.ID_EXIT, 95 wx.ID_REDO, 96 wx.ID_REFRESH, 97 wx.ID_REMOVE, 98 wx.ID_REVERT_TO_SAVED, 99 wx.ID_SAVE, 100 wx.ID_SAVEAS, 101 wx.ID_SELECTALL, 102 wx.ID_STOP, 103 wx.ID_UNDELETE, 104 wx.ID_UNDERLINE, 105 wx.ID_UNDO, 106 wx.ID_UNINDENT, 107 wx.ID_YES, 108 wx.ID_ZOOM_100, 109 wx.ID_ZOOM_FIT, 110 wx.ID_ZOOM_IN, 111 wx.ID_ZOOM_OUT, 112 113] 114 115 116class BaseTool(cm.BaseObjman): 117 """ 118 This is a base tool class for Agilecanvas. 119 It must handle all mouse or keyboard events, 120 must create and draw helplines and finally 121 modify the state of client which are grafically 122 represented on the canvas. 123 """ 124 125 def __init__(self, parent, mainframe=None): 126 """ 127 To be overridden by specific tool. 128 """ 129 self.init_common('select', parent, 'Selection tool', mainframe, info='Select objects in cancvas') 130 131 def set_button_info(self, bsize=(32, 32)): 132 # print 'set_button_info select tool' 133 self._bitmap = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIcon.bmp'), wx.BITMAP_TYPE_BMP) 134 self._bitmap_sel = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIconSel.bmp'), wx.BITMAP_TYPE_BMP) 135 136 def get_button(self, parent, bottonsize=(32, 32), bottonborder=10): 137 138 # simple stockbuttons 139 #b=wx.Button(parent, wx.ID_DELETE) 140 141 id = wx.NewId() 142 bitmap = self._bitmap 143 144 #b=GenBitmapTextToggleButton(parent, id, bitmap,tool.name,name = self.get_name()) 145 b = GenBitmapToggleButton(parent, id, bitmap, (bitmap.GetWidth()+bottonborder, 146 bitmap.GetHeight()+bottonborder), name=self.get_name()) 147 #b=GenBitmapToggleButton(self, wx.ID_DELETE) 148 #b = GenBitmapTextToggleButton(self, id, None, tool.get('name',''), size = (200, 45)) 149 150 if bitmap is not None: 151 #mask = wx.Mask(bitmap, wx.BLUE) 152 # bitmap.SetMask(mask) 153 b.SetBitmapLabel(bitmap) 154 # bmp=wx.NullBitmap 155 156 bitmap_sel = self._bitmap_sel 157 if bitmap_sel is not None: 158 #mask = wx.Mask(bmp, wx.BLUE) 159 # bmp.SetMask(mask) 160 b.SetBitmapSelected(bitmap_sel) 161 162 b.SetUseFocusIndicator(False) 163 164 b.SetUseFocusIndicator(False) 165 # b.SetSize((36,140)) 166 # b.SetBestSize() 167 tt = wx.ToolTip(self.get_info()) 168 b.SetToolTip(tt) # .SetTip(tool.tooltip) 169 return b 170 171 def init_common(self, ident, parent, name, mainframe=None, info=None): 172 # print 'Agiletool.__init__',ident,name 173 self.name = name 174 self.metacanvas = None 175 # FSMnamed.__init__(self,ident,parent,name) 176 self._init_objman(ident, parent=parent, name=name.title(), info=info) 177 attrsman = self.set_attrsman(cm.Attrsman(self)) 178 179 self.access = attrsman.add(cm.AttrConf('access', ['bus', 'bike', 'tram'], 180 groupnames=['options'], 181 perm='rw', 182 is_save=True, 183 name='Access list', 184 info='List with vehicle classes that have access', 185 )) 186 187 self.emissiontype = attrsman.add(cm.AttrConf('emissiontype', 'Euro 0', 188 groupnames=['options'], 189 perm='rw', 190 is_save=True, 191 name='Emission type', 192 info='Emission type of vehicle', 193 )) 194 # dictionary of drawobjects that will be created during 195 # the application of the tool. 196 # ident is the number in chronological order of creation, 197 # starting with one. Value is the ad hoc instance of a drawing 198 # object. 199 self.drawobjs = {} 200 self.helpobjs = {} 201 202 self.mainframe = mainframe 203 # print ' call set_button',self.ident 204 self.set_button_info() 205 self.targetsets = {} 206 # self.optionspanel=None 207 208 def append_drawobj(self, drawobj): 209 """ 210 Append new drawobject 211 """ 212 n = len(self.drawobjs)+1 213 self.drawobjs[n] = drawobj 214 215 def pop_drawobj(self): 216 """ 217 Returns most recent drawobject, removing it from the list. 218 If there are no more drwobjects in the list None is returned. 219 """ 220 221 n = len(self.drawobjs) 222 if n > 0: 223 drawobj = self.drawobjs[n] 224 del self.drawobjs[n] 225 return drawobj 226 else: 227 return None 228 229 def get_last_drawobj(self): 230 """ 231 Returns most recent drawobject, without changing the list. 232 If there are no more drwobjects in the list None is returned. 233 """ 234 n = len(self.drawobjs) 235 if n > 0: 236 drawobj = self.drawobjs[n] 237 return drawobj 238 else: 239 return None 240 241 def clear_drawobjs(self): 242 """ 243 Clear list of drawobjects, while maintaining them on metacanvas. 244 """ 245 self.drawobjs = {} 246 247 def del_drawobjs(self): 248 """ 249 Remove all drawobjects from metacanvas. 250 """ 251 while len(self.drawobjs) > 0: 252 drawobj = self.pop_drawobj() 253 self.metacanvas.del_obj(drawobj) 254 255 def make_targetsets(self, setnames=None, layer=None): 256 """ 257 Returns a dictionary with instances of targetsets as values 258 and setnames as key. 259 This allows the tool to select a list of sets from the a specific 260 layer to which it can directly communicate. 261 262 If no setnames are given then all sets of the specific layer 263 are returned. 264 265 Can be used for example to change handle settings 266 """ 267 if setnames is not None: 268 objsets = {} 269 for name in setnames: 270 objsets[name] = self.metacanvas.get_objset_from_layer(layer, name) 271 self.targetsets = objsets 272 else: 273 self.targetsets = self.metacanvas.get_objset_from_layer(layer) 274 275 def set_handles(self): 276 """ 277 Set handles to selected object sets which can be connected. 278 """ 279 # put handles on all section objects 280 for name_set in self.targetsets.keys(): 281 self.metacanvas.set_handles(name_set=name_set) 282 283 def get_bitmap_from_file(self, name_bitmap): 284 # print 'get_bitmap_from_file :'+"gui/bitmaps/" + name_bitmap + ".bmp" 285 return wx.Bitmap("gui/bitmaps/" + name_bitmap + ".bmp", 286 wx.BITMAP_TYPE_BMP) 287 288 # def make_optionpanel(self,panel): 289 290 def get_optionpanel(self, parent): 291 """ 292 Return tool option widgets on given parent 293 """ 294 # print 'get_optionpanel',self 295 #button = wx.Button(parent, wx.NewId(), self.name+' Options') 296 #button.Bind(wx.EVT_BUTTON, self.set_options) 297 # return button 298 299 self.optionspanel = ObjPanel(parent, self, groups=['options'], 300 standartbuttons=[], 301 immediate_apply=True, 302 panelstyle='instrumental') 303 return self.optionspanel 304 305 def set_options(self, event): 306 """ 307 Called from options panel. 308 """ 309 print 'set_options', self.ident 310 print ' event=', event 311 pass 312 313 def set_statusbar(self, key, info): 314 pass 315 316 def activate_metacanvas(self, metacanvas): 317 """ 318 This call by metacanvas signals that the tool has been 319 activated and can now interact with metacanvas. 320 """ 321 # print 'activate_metacanvas',self.ident 322 self.metacanvas = metacanvas 323 self.metacanvas.del_handles() 324 self.activate() 325 326 def get_metacanvas(self): 327 return self.metacanvas 328 329 # def get_pentable(self): 330 # if self.metacanvas: 331 # return self.metacanvas.get_pentable() 332 333 def deactivate_metacanvas(self): 334 """ 335 This call by metacanvas signals that the tool has been 336 deactivated and can now interact with metacanvas. 337 """ 338 339 self.deactivate() 340 self.optionspanel = None 341 self.metacanvas = None 342 343 344class DelTool(BaseTool): 345 def __init__(self, parent, mainframe=None): 346 """ 347 To be overridden by specific tool. 348 """ 349 self.init_common('delete', parent, 'Delete', mainframe, info='Delete objects in cancvas') 350 351 def set_button_info(self, bsize=(32, 32)): 352 # print 'set_button_info select tool' 353 self._bitmap = None 354 self._bitmap_sel = None 355 356 def get_button(self, parent, bottonsize=(32, 32), bottonborder=10): 357 358 # simple stockbuttons 359 b = wx.Button(parent, wx.ID_DELETE, name=self.get_name()) 360 361 b.SetSize(bottonsize) 362 # b.SetBestSize() 363 tt = wx.ToolTip(self.get_info()) 364 b.SetToolTip(tt) # .SetTip(tool.tooltip) 365 # print 'DelTool.get_button',dir(b) 366 return b 367 368 369class ToolPalett(wx.Panel): 370 """ 371 This is a panel where tools are represented by images and/or text. 372 The tools are selected in a radio-button-fashion. 373 374 Each tool has a string as key. Each time the status changes, 375 a callback function is called with new and old tool key as argument. 376 """ 377 378 def __init__(self, parent, tools=[], callback=None, n_buttoncolumns=4): 379 """ 380 callback is a function that is called when a tool has been selected. 381 The function is called as: 382 callback(tool) 383 384 """ 385 # the metacanvas object with which the pallet should apply th tools 386 self._callback = callback 387 388 # wx.Window.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER|wx.WANTS_CHARS) 389 # wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,size,wx.RAISED_BORDER|wx.WANTS_CHARS) 390 wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, wx.DefaultSize) 391 # wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,(300,600),wx.RAISED_BORDER|wx.WANTS_CHARS) 392 self.sizer = wx.GridSizer(0, n_buttoncolumns, 5, 5) 393 self.SetSizer(self.sizer) 394 self._id_to_tool = {} 395 self._id = -1 396 397 for tool in tools: 398 self.add_tool(tool) 399 400 self.sizer.Fit(self) 401 # self.SetMaxSize((300,300)) 402 403 def add_tool(self, tool): 404 """ 405 Add a tool to the pallet. 406 """ 407 bottonsize = (32, 32) 408 bottonborder = 10 409 toolbarborder = 1 410 411 if tool is None: 412 self.sizer.Add() 413 return None 414 else: 415 b = tool.get_button(self, bottonsize=bottonsize, bottonborder=bottonborder) 416 self.Bind(wx.EVT_BUTTON, self.on_select, b) 417 418 self._id_to_tool[b.GetId()] = (tool, b) 419 420 #self.sizer.Add(b, 0, wx.GROW) 421 self.sizer.Add(b, 0, wx.EXPAND, border=toolbarborder) 422 # self.sizer.Add(b) 423 424 return id 425 426 def add_tool_old(self, tool): 427 """ 428 Add a tool to the pallet. 429 """ 430 bottonsize = (32, 32) 431 bottonborder = 10 432 toolbarborder = 1 433 434 if tool is None: 435 self.sizer.Add() 436 return None 437 else: 438 id = wx.NewId() 439 bitmap = tool.get_buttonbitmap() 440 441 # print '\n add_tool',key,bitmap 442 # print 'toolpallet.add_tool: key,name:',tool.key,tool.name 443 #b=GenBitmapTextToggleButton(self, id, bitmap,tool.name,name = tool.name) 444 b = GenBitmapToggleButton(self, id, bitmap, (bitmap.GetWidth() + 445 bottonborder, bitmap.GetHeight()+bottonborder)) 446 #b=GenBitmapToggleButton(self, wx.ID_DELETE) 447 #b = GenBitmapTextToggleButton(self, id, None, tool.get('name',''), size = (200, 45)) 448 self.Bind(wx.EVT_BUTTON, self.on_select, b) 449 450 if bitmap: 451 #mask = wx.Mask(bitmap, wx.BLUE) 452 # bitmap.SetMask(mask) 453 b.SetBitmapLabel(bitmap) 454 # bmp=wx.NullBitmap 455 456 bitmap_sel = tool.get_buttonbitmap_sel() 457 if bitmap_sel: 458 #mask = wx.Mask(bmp, wx.BLUE) 459 # bmp.SetMask(mask) 460 b.SetBitmapSelected(bitmap_sel) 461 462 b.SetUseFocusIndicator(False) 463 # b.SetSize((36,140)) 464 # b.SetBestSize() 465 tt = wx.ToolTip(tool.get_info()) 466 b.SetToolTip(tt) # .SetTip(tool.tooltip) 467 468 self._id_to_tool[id] = (tool, b) 469 470 #self.sizer.Add(b, 0, wx.GROW) 471 472 self.sizer.Add(b, 0, wx.EXPAND, border=toolbarborder) 473 # self.sizer.Add(b) 474 475 return id 476 477 def get_tools(self): 478 """ 479 Returns lins with all toll instances 480 """ 481 tools = [] 482 for (tool, b) in self._id_to_tool.values(): 483 tools.append(tool) 484 return tools 485 486 def refresh(self): 487 """ 488 Reorganizes toolpallet after adding/removing tools. 489 Attention is not automatically called. 490 """ 491 self.sizer.Layout() 492 493 def on_select(self, event): 494 495 _id = event.GetEventObject().GetId() 496 print '\n on_select', _id, self._id # ,self._id_to_tool[_id] 497 if _id != self._id: 498 if self._id_to_tool.has_key(_id): 499 500 (tool, button) = self._id_to_tool[_id] 501 print ' new tool', tool.get_name() 502 self.unselect() 503 self._id = _id 504 self.GetParent().set_options(tool) 505 if self._callback is not None: 506 self._callback(tool) 507 508 def unselect(self): 509 """ 510 Unselect currently selected tool. 511 """ 512 if self._id_to_tool.has_key(self._id): 513 (tool, button) = self._id_to_tool[self._id] 514 if hasattr(button, 'SetToggle'): 515 button.SetToggle(False) 516 else: 517 # button.SetFocus() 518 # print 'button.SetFocus',button.SetFocus.__doc__ 519 pass 520 521 def select(self, id): 522 """ 523 Select explicitelt a tool. 524 """ 525 print '\n select', id, self._id, self._id_to_tool 526 527 if id != self._id: 528 if self._id_to_tool.has_key(id): 529 # unselect previous 530 self.unselect() 531 532 # select and activate new tool 533 (tool, button) = self._id_to_tool[id] 534 button.SetToggle(True) 535 self._id = id 536 if self._callback is not None: 537 self._callback(tool) 538 539 540class __ToggleMixin: 541 def SetToggle(self, flag): 542 self.up = not flag 543 self.Refresh() 544 SetValue = SetToggle 545 546 def GetToggle(self): 547 return not self.up 548 GetValue = GetToggle 549 550 def OnLeftDown(self, event): 551 if not self.IsEnabled(): 552 return 553 self.saveUp = self.up 554 self.up = False # not self.up 555 self.CaptureMouse() 556 self.SetFocus() 557 self.Refresh() 558 559 def OnLeftUp(self, event): 560 if not self.IsEnabled() or not self.HasCapture(): 561 return 562 if self.HasCapture(): 563 if self.up != self.saveUp: 564 self.Notify() 565 self.ReleaseMouse() 566 self.Refresh() 567 568 def OnKeyDown(self, event): 569 event.Skip() 570 571 572class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton): 573 """A generic toggle bitmap button with text label""" 574 pass 575 576 577class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton): 578 """A generic toggle bitmap button with text label""" 579 pass 580 581 582class ToolsPanel(wx.Panel): 583 """ 584 585 Interactively navigates through objects and displays attributes 586 on a panel. 587 """ 588 589 def __init__(self, parent): 590 wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, wx.DefaultSize) 591 592 sizer = wx.BoxSizer(wx.VERTICAL) 593 594 self._toolspalett = ToolPalett(self) 595 596 # self._toolspalett.add_tool(BaseTool(self)) 597 598 # create initial option panel 599 self._optionspanel = wx.Window(self) 600 self._optionspanel.SetBackgroundColour("pink") 601 wx.StaticText(self._optionspanel, -1, "Tool Options", (300, -1)) 602 603 sizer.Add(self._toolspalett, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4) 604 sizer.Add(self._optionspanel, 1, wx.GROW) 605 606 # finish panel setup 607 self.SetSizer(sizer) 608 sizer.Fit(self) 609 610 # self.SetSize(parent.GetSize()) 611 612 def add_tool(self, tool): 613 self._toolspalett.add_tool(tool) 614 615 def set_options(self, tool): 616 #self._optionspanel.change_obj(tool,groupnames = ['options']) 617 # self._optionspanel.change_obj(tool) 618 sizer = self.GetSizer() 619 sizer.Remove(1) 620 self._optionspanel.Destroy() 621 622 self._optionspanel = objpanel.ObjPanel(self, obj=tool, 623 attrconfigs=None, 624 #tables = None, 625 # table = None, id=None, ids=None, 626 groupnames=None, 627 func_change_obj=None, 628 show_groupnames=False, show_title=True, is_modal=False, 629 mainframe=None, 630 pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.MAXIMIZE_BOX | wx.RESIZE_BORDER, 631 immediate_apply=False, panelstyle='default', 632 standartbuttons=['apply', 'restore']) 633 # if id is not None: 634 # self.objpanel=ObjPanel(self,obj,id=id,func_change_obj=self.change_obj) 635 # else: 636 # self.objpanel=ObjPanel(self,obj,func_change_obj=self.change_obj) 637 sizer.Add(self._optionspanel, 1, wx.GROW) 638 639 self.Refresh() 640 # sizer.Fit(self) 641 sizer.Layout() 642 643 644def get_dist_point_to_segs(p, y1, x1, y2, x2, is_ending=True): 645 """ 646 Minimum Distance between a Point p = (x,y) and a Line segments , 647 where vectors x1, y1 are the first points and x2,y2 are the second points 648 of the line segments. 649 Inspired by the description by Paul Bourke, October 1988 650 http://paulbourke.net/geometry/pointlineplane/ 651 652 Rewritten in vectorial form by Joerg Schweizer 653 """ 654 655 y3, x3 = p 656 657 d = np.zeros(len(y1), dtype=np.float32) 658 659 dx21 = (x2-x1) 660 dy21 = (y2-y1) 661 662 lensq21 = dx21*dx21 + dy21*dy21 663 664 # indexvector for all zero length lines 665 iz = (lensq21 == 0) 666 667 dy = y3-y1[iz] 668 dx = x3-x1[iz] 669 670 d[iz] = dx*dx + dy*dy 671 672 lensq21[iz] = 1.0 # replace zeros with 1.0 to avoid div by zero error 673 674 u = (x3-x1)*dx21 + (y3-y1)*dy21 675 u = u / lensq21 676 677 x = x1 + u * dx21 678 y = y1 + u * dy21 679 680 if is_ending: 681 ie = u < 0 682 x[ie] = x1[ie] 683 y[ie] = y1[ie] 684 ie = u > 1 685 x[ie] = x2[ie] 686 y[ie] = y2[ie] 687 688 dx30 = x3-x 689 dy30 = y3-y 690 d[~iz] = (dx30*dx30 + dy30*dy30)[~iz] 691 return d 692 693 694def is_inside_triangles(p, x1, y1, x2, y2, x3, y3): 695 """ 696 Returns a binary vector with True if point p is 697 inside a triangle. 698 x1,y1,x2,y2,x3,y3 are vectors with the 3 coordiantes of the triangles. 699 """ 700 alpha = ((y2 - y3)*(p[0] - x3) + (x3 - x2)*(p[1] - y3)) \ 701 / ((y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3)) 702 703 beta = ((y3 - y1)*(p[0] - x3) + (x1 - x3)*(p[1] - y3)) \ 704 / ((y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3)) 705 706 gamma = 1.0 - alpha - beta 707 return (alpha > 0) & (beta > 0) & (gamma > 0) 708 709 710class WxGLTest_orig(glcanvas.GLCanvas): 711 def __init__(self, parent): 712 713 glcanvas.GLCanvas.__init__(self, parent, -1, attribList=[glcanvas.WX_GL_DOUBLEBUFFER]) 714 wx.EVT_PAINT(self, self.OnDraw) 715 wx.EVT_SIZE(self, self.OnSize) 716 wx.EVT_MOTION(self, self.OnMouseMotion) 717 wx.EVT_WINDOW_DESTROY(self, self.OnDestroy) 718 719 self.init = True 720 721 def OnDraw(self, event): 722 self.SetCurrent() 723 724 if not self.init: 725 self.InitGL() 726 self.init = False 727 728 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 729 glLoadIdentity() 730 731 # Draw the spiral in 'immediate mode' 732 # WARNING: You should not be doing the spiral calculation inside the loop 733 # even if you are using glBegin/glEnd, sin/cos are fairly expensive functions 734 # I've left it here as is to make the code simpler. 735 radius = 1.0 736 x = radius*math.sin(0) 737 y = radius*math.cos(0) 738 glColor(0.0, 1.0, 0.0) 739 glBegin(GL_LINE_STRIP) 740 for deg in xrange(1000): 741 glVertex(x, y, 0.0) 742 rad = math.radians(deg) 743 radius -= 0.001 744 x = radius*math.sin(rad) 745 y = radius*math.cos(rad) 746 glEnd() 747 748 glEnableClientState(GL_VERTEX_ARRAY) 749 750 spiral_array = [] 751 752 # Second Spiral using "array immediate mode" (i.e. Vertex Arrays) 753 radius = 0.8 754 x = radius*math.sin(0) 755 y = radius*math.cos(0) 756 glColor(1.0, 0.0, 0.0) 757 for deg in xrange(820): 758 spiral_array.append([x, y]) 759 rad = math.radians(deg) 760 radius -= 0.001 761 x = radius*math.sin(rad) 762 y = radius*math.cos(rad) 763 764 glVertexPointerf(spiral_array) 765 glDrawArrays(GL_LINE_STRIP, 0, len(spiral_array)) 766 glFlush() 767 self.SwapBuffers() 768 return 769 770 def InitGL(self): 771 ''' 772 Initialize GL 773 ''' 774 775# # set viewing projection 776# glClearColor(0.0, 0.0, 0.0, 1.0) 777# glClearDepth(1.0) 778# 779# glMatrixMode(GL_PROJECTION) 780# glLoadIdentity() 781# gluPerspective(40.0, 1.0, 1.0, 30.0) 782# 783# glMatrixMode(GL_MODELVIEW) 784# glLoadIdentity() 785# gluLookAt(0.0, 0.0, 10.0, 786# 0.0, 0.0, 0.0, 787# 0.0, 1.0, 0.0) 788 789 def OnSize(self, event): 790 791 try: 792 width, height = event.GetSize() 793 except: 794 width = event.GetSize().width 795 height = event.GetSize().height 796 797 self.Refresh() 798 self.Update() 799 800 def OnMouseMotion(self, event): 801 x = event.GetX() 802 y = event.GetY() 803 804 def OnDestroy(self, event): 805 print "Destroying Window" 806 807 808class Lines: 809 """Lines class.""" 810 811 def __init__(self, linewidth=3, vertices=None, colors=None): 812 self.name = 'Lines' 813 self.n_vert_per_elem = 2 814 self.linewidth = linewidth 815 self.c_highl = 0.3 816 self.detectwidth = 0.1 # m 817 self.set_attrs(vertices, colors) 818 819 def set_attrs(self, vertices, colors): 820 821 self.vertices = np.array(vertices, dtype=np.float32) 822 self._update_vertexvbo() 823 824 self.colors = np.array(colors, dtype=np.float32) 825 self.colors_highl = np.zeros((len(colors), 4), dtype=np.float32) 826 self._update_colorvbo() 827 828 def _update_vertexvbo(self): 829 self._vertexvbo = vbo.VBO(self.vertices.reshape((-1, 3))) 830 self._indexvbo = vbo.VBO(np.arange(self.n_vert_per_elem*len(self.vertices), 831 dtype=np.int32), target=GL_ELEMENT_ARRAY_BUFFER) 832 833 def _update_colorvbo(self): 834 #self._colorvbo = vbo.VBO( np.resize( np.repeat(np.clip((self.colors+self.c_highl*self.colors_highl) ,0.0,1.0), self.n_vert_per_elem),(len(self.colors),4)) ) 835 self._colorvbo = vbo.VBO(np.clip((self.colors+self.colors_highl) 836 [np.array(np.arange(0, len(self.colors), 1.0/self.n_vert_per_elem), int)], 0.0, 1.0)) 837 838 def pick(self, p): 839 """ 840 Returns a binary vector which is True values for lines that have been selected 841 by point p. 842 843 In particular, an element of this vector is True if the minimum distance 844 between the respective line to point p is less than self.detectwidth 845 """ 846 x1 = self.vertices[:, 0, 0] 847 y1 = self.vertices[:, 0, 1] 848 849 x2 = self.vertices[:, 1, 0] 850 y2 = self.vertices[:, 1, 1] 851 852 return get_dist_point_to_segs(p, x1, y1, x2, y2, is_ending=True) < self.detectwidth**2 853 854 def highlight(self, inds_highl): 855 self.colors_highl = np.repeat(self.c_highl*np.array(inds_highl, dtype=np.float32), 4).reshape(-1, 4) 856 self._update_colorvbo() 857 858 def draw(self): 859 glLineWidth(self.linewidth) 860 861 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 862 glEnable(GL_BLEND) 863 864 glEnableClientState(GL_VERTEX_ARRAY) 865 glEnableClientState(GL_COLOR_ARRAY) 866 867 self._colorvbo.bind() 868 glColorPointer(4, GL_FLOAT, 0, None) 869 870 self._vertexvbo.bind() 871 self._indexvbo.bind() 872 glVertexPointer(3, GL_FLOAT, 0, None) 873 874 glDrawElements(GL_LINES, self.n_vert_per_elem*len(self.vertices), GL_UNSIGNED_INT, None) 875 876 glDisableClientState(GL_VERTEX_ARRAY) 877 glDisableClientState(GL_COLOR_ARRAY) 878 self._vertexvbo.unbind() 879 self._indexvbo.unbind() 880 self._colorvbo.unbind() 881 882 883class Rectangles(Lines): 884 885 def __init__(self, linewidth=3, vertices=None, colors=None): 886 self.name = 'Rectangles' 887 self.n_vert_per_elem = 4 888 self.c_highl = 0.3 889 self.detectwidth = 0.1 # m 890 self.linewidth = linewidth 891 892 self.set_attrs(vertices, colors) 893 894 def pick(self, p): 895 896 x1 = self.vertices[:, 0, 0] 897 y1 = self.vertices[:, 0, 1] 898 899 x2 = self.vertices[:, 1, 0] 900 y2 = self.vertices[:, 1, 1] 901 902 x3 = self.vertices[:, 2, 0] 903 y3 = self.vertices[:, 2, 1] 904 905 x4 = self.vertices[:, 3, 0] 906 y4 = self.vertices[:, 3, 1] 907 908 return is_inside_triangles(p, x1, y1, x2, y2, x3, y3) | is_inside_triangles(p, x1, y1, x3, y3, x4, y4) 909 910 def draw(self): 911 glLineWidth(self.linewidth) 912 913 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 914 glEnable(GL_BLEND) 915 916 glEnableClientState(GL_VERTEX_ARRAY) 917 glEnableClientState(GL_COLOR_ARRAY) 918 919 self._colorvbo.bind() 920 glColorPointer(4, GL_FLOAT, 0, None) 921 922 self._vertexvbo.bind() 923 self._indexvbo.bind() 924 glVertexPointer(3, GL_FLOAT, 0, None) 925 926 glDrawElements(GL_QUADS, self.n_vert_per_elem*len(self.vertices), GL_UNSIGNED_INT, None) 927 928 glDisableClientState(GL_VERTEX_ARRAY) 929 glDisableClientState(GL_COLOR_ARRAY) 930 self._vertexvbo.unbind() 931 self._indexvbo.unbind() 932 self._colorvbo.unbind() 933 934 935class Triangles(Lines): 936 """Triangles class.""" 937 938 def __init__(self, linewidth=3, vertices=None, colors=None): 939 self.name = 'Triangles' # ,self.__name__ 940 self.n_vert_per_elem = 3 941 self.c_highl = 0.3 942 self.detectwidth = 0.1 # m 943 self.linewidth = linewidth 944 945 self.set_attrs(vertices, colors) 946 947 def pick(self, p): 948 return is_inside_triangles(p, self.vertices[:, 0, 0], self.vertices[:, 0, 1], self.vertices[:, 1, 0], self.vertices[:, 1, 1], self.vertices[:, 2, 0], self.vertices[:, 2, 1]) 949 950 def draw(self): 951 glLineWidth(self.linewidth) 952 953 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 954 glEnable(GL_BLEND) 955 956 glEnableClientState(GL_VERTEX_ARRAY) 957 glEnableClientState(GL_COLOR_ARRAY) 958 959 self._colorvbo.bind() 960 glColorPointer(4, GL_FLOAT, 0, None) 961 962 self._vertexvbo.bind() 963 self._indexvbo.bind() 964 glVertexPointer(3, GL_FLOAT, 0, None) 965 966 glDrawElements(GL_TRIANGLES, self.n_vert_per_elem*len(self.vertices), GL_UNSIGNED_INT, None) 967 968 glDisableClientState(GL_VERTEX_ARRAY) 969 glDisableClientState(GL_COLOR_ARRAY) 970 self._vertexvbo.unbind() 971 self._indexvbo.unbind() 972 self._colorvbo.unbind() 973 974 975class GLFrame(wx.Frame): 976 """A simple class for using OpenGL with wxPython.""" 977 978 def __init__(self, parent, id=-1, title='', pos=wx.DefaultPosition, 979 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, 980 name='frame', mainframe=None): 981 982 print '\n\nGLFrame!!' 983 if mainframe is None: 984 self._mainframe = parent 985 else: 986 self._mainframe = mainframe 987 988 self._elements = [] 989 self.elements_selected = [] 990 991 self.eyex = 0.0 992 self.eyey = 0.0 993 self.eyez = -9.0 994 995 self.centerx = 0.0 996 self.centery = 0.0 997 self.centerz = 0.0 998 999 self.upx = -1.0 1000 self.upy = 0.0 1001 self.upz = 0.0 1002 1003 self.g_Width = 600 1004 self.g_Height = 600 1005 1006 self.g_nearPlane = 1. 1007 self.g_farPlane = 1000. 1008 1009 self.action = "" 1010 self.xStart = self.yStart = 0. 1011 self.xStart 1012 self.zoom = 65. 1013 1014 self.xRotate = 0. 1015 self.yRotate = 0. 1016 self.zRotate = 0. 1017 1018 self.xTrans = 0. 1019 self.yTrans = 0. 1020 1021 # 1022 # Forcing a specific style on the window. 1023 # Should this include styles passed? 1024 style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 1025 1026 super(GLFrame, self).__init__(parent, id, title, pos, size, style, name) 1027 #wx.Frame.__init__(self, parent, id, title, pos, size, style, name) 1028 1029 self.GLinitialized = False 1030 attribList = (glcanvas.WX_GL_RGBA, # RGBA 1031 glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered 1032 glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit 1033 1034 # 1035 # Create the canvas 1036 self.canvas = glcanvas.GLCanvas(self, attribList=attribList) 1037 1038 # 1039 # Set the event handlers. 1040 self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent) 1041 self.canvas.Bind(wx.EVT_SIZE, self.processSizeEvent) 1042 self.canvas.Bind(wx.EVT_PAINT, self.processPaintEvent) 1043 1044 self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) 1045 self.canvas.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) 1046 self.canvas.Bind(wx.EVT_MOTION, self.OnMotion) 1047 self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) 1048 1049 self.Show() 1050 # this is needed to initialize GL projections for unproject 1051 wx.CallAfter(self.processSizeEvent) 1052 1053 def add_element(self, element): 1054 self._elements.append(element) 1055 self.OnDraw() 1056 1057 def resetView(): 1058 self.zoom = 65. 1059 self.xRotate = 0. 1060 self.yRotate = 0. 1061 self.zRotate = 0. 1062 self.xTrans = 0. 1063 self.yTrans = 0. 1064 self.OnDraw() 1065 1066 def OnWheel(self, event): 1067 #EventType = FloatCanvas.EVT_FC_MOUSEWHEEL 1068 # 1069 Rot = event.GetWheelRotation() 1070 # print 'OnWheel!!',Rot,event.ControlDown(),event.ShiftDown() 1071 if event.ControlDown(): # event.ControlDown(): # zoom 1072 if Rot < 0: 1073 self.zoom *= 0.9 1074 else: 1075 self.zoom *= 1.1 1076 self.OnDraw() 1077 event.Skip() 1078 1079 def OnLeftDown(self, event): 1080 ## 1081 if (event.ControlDown() & event.ShiftDown()) & (self.action == ''): 1082 self.action = 'drag' 1083 self.BeginGrap(event) 1084 event.Skip() 1085 1086 def OnLeftUp(self, event): 1087 if self.action == 'drag': 1088 self.EndGrap(event) 1089 self.action == '' 1090 event.Skip() 1091 1092 def get_intersection(self, v_near, v_far): 1093 # 150918 1094 # idea from http://www.bfilipek.com/2012/06/select-mouse-opengl.html 1095 # https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection 1096 d = -v_near + v_far 1097 1098 t = -v_near[2]/d[2] 1099 v_inter = v_near+t*d 1100 1101 return v_inter 1102 1103 def OnMotion(self, event): 1104 1105 p = self.unproject(event)[0:2] 1106 for element in self._elements: 1107 inds_pick = element.pick(p) 1108 element.highlight(inds_pick) 1109 1110 self.OnDraw() 1111 1112 if (event.ControlDown() & event.ShiftDown() & (self.action == 'drag')): 1113 self.MoveGrap(event) 1114 self.OnDraw() 1115 event.Skip() 1116 1117 elif (self.action == 'drag'): 1118 self.EndGrap(event) 1119 self.action == '' 1120 event.Skip() 1121 1122 def unproject(self, event): 1123 """Get the world coordinates for viewCoordinate for the event 1124 """ 1125 mousex, mousey = event.GetPosition() 1126 x = mousex 1127 y = self.g_Height-mousey 1128 1129 modelviewmatrix = glGetDoublev(GL_MODELVIEW_MATRIX) 1130 projectionmatrix = glGetDoublev(GL_PROJECTION_MATRIX) 1131 viewport = glGetInteger(GL_VIEWPORT) 1132 z = 0.0 1133 worldCoordinate_near = np.array(gluUnProject( 1134 x, y, z, 1135 modelviewmatrix, 1136 projectionmatrix, 1137 viewport,), dtype=np.float32) 1138 z = 1.0 1139 worldCoordinate_far = np.array(gluUnProject( 1140 x, y, z, 1141 modelviewmatrix, 1142 projectionmatrix, 1143 viewport,), dtype=np.float32) 1144 1145 v_inter = self.get_intersection(worldCoordinate_near, worldCoordinate_far) 1146 return v_inter 1147 1148 def BeginGrap(self, event): 1149 1150 self.xStart, self.yStart = event.GetPosition() 1151 # print 'BeginGrap',self.xStart,self.yStart 1152 1153 def MoveGrap(self, event): 1154 x, y = event.GetPosition() 1155 1156 self.xTrans += x-self.xStart 1157 self.yTrans += y-self.yStart 1158 # print 'MoveGrap',self.xTrans,self.yTrans 1159 self.xStart, self.yStart = x, y 1160 1161 def EndGrap(self, event): 1162 # print 'EndGrap' 1163 self.canvas.SetCursor(wx.NullCursor) 1164 self.action = '' 1165 1166 # 1167 # Canvas Proxy Methods 1168 1169 def GetGLExtents(self): 1170 """Get the extents of the OpenGL canvas.""" 1171 return self.canvas.GetClientSize() 1172 1173 def SwapBuffers(self): 1174 """Swap the OpenGL buffers.""" 1175 self.canvas.SwapBuffers() 1176 1177 # 1178 # wxPython Window Handlers 1179 1180 def processEraseBackgroundEvent(self, event): 1181 """Process the erase background event.""" 1182 pass # Do nothing, to avoid flashing on MSWin 1183 1184 def processSizeEvent(self, event=None): 1185 """Process the resize event.""" 1186 if self.canvas.GetContext(): 1187 # Make sure the frame is shown before calling SetCurrent. 1188 self.Show() 1189 self.canvas.SetCurrent() 1190 1191 size = self.GetGLExtents() 1192 self.OnReshape(size.width, size.height) 1193 self.canvas.Refresh(False) 1194 if event: 1195 event.Skip() 1196 1197 def processPaintEvent(self, event): 1198 """Process the drawing event.""" 1199 self.canvas.SetCurrent() 1200 1201 # This is a 'perfect' time to initialize OpenGL ... only if we need to 1202 if not self.GLinitialized: 1203 self.OnInitGL() 1204 self.GLinitialized = True 1205 1206 self.OnDraw() 1207 event.Skip() 1208 1209 # 1210 # GLFrame OpenGL Event Handlers 1211 1212 def OnInitGL(self): 1213 """Initialize OpenGL for use in the window.""" 1214 glClearColor(0, 0, 0, 1) 1215 1216 def OnReshape(self, width, height): 1217 """Reshape the OpenGL viewport based on the dimensions of the window.""" 1218 #global g_Width, g_Height 1219 self.g_Width = width 1220 self.g_Height = height 1221 glViewport(0, 0, self.g_Width, self.g_Height) 1222 1223 def OnDraw(self, *args, **kwargs): 1224 """Draw the window.""" 1225 # Clear frame buffer and depth buffer 1226 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 1227 # Set up viewing transformation, looking down -Z axis 1228 glLoadIdentity() 1229 gluLookAt(self.eyex, self.eyey, self.eyez, self.centerx, self.centery, 1230 self.centerz, self.upx, self.upy, self.upz) # -.1,0,0 1231 1232 # Set perspective (also zoom) 1233 glMatrixMode(GL_PROJECTION) 1234 glLoadIdentity() 1235 # the window corner OpenGL coordinates are (-+1, -+1) 1236 glOrtho(-1, 1, 1, -1, -1, 1) 1237 1238 aspect = float(self.g_Width)/float(self.g_Height) 1239 1240 gluPerspective(self.zoom, aspect, self.g_nearPlane, self.g_farPlane) 1241 glMatrixMode(GL_MODELVIEW) 1242 self.polarView() 1243 1244 # draw actual scene 1245 for element in self._elements: 1246 element.draw() 1247 1248 self.SwapBuffers() 1249 1250 def polarView(self): 1251 glTranslatef(self.yTrans/100., 0.0, 0.0) 1252 glTranslatef(0.0, -self.xTrans/100., 0.0) 1253 glRotatef(-self.zRotate, 0.0, 0.0, 1.0) 1254 glRotatef(-self.xRotate, 1.0, 0.0, 0.0) 1255 glRotatef(-self.yRotate, .0, 1.0, 0.0) 1256 1257 1258class WxGLTest2(glcanvas.GLCanvas): 1259 def __init__(self, parent, mainframe=None): 1260 if mainframe is None: 1261 self._mainframe = parent 1262 else: 1263 self._mainframe = mainframe 1264 1265 self._elements = [] 1266 self.elements_selected = [] 1267 1268 self.eyex = 0.0 1269 self.eyey = 0.0 1270 self.eyez = -9.0 1271 1272 self.centerx = 0.0 1273 self.centery = 0.0 1274 self.centerz = 0.0 1275 1276 self.upx = -1.0 1277 self.upy = 0.0 1278 self.upz = 0.0 1279 1280 self.g_Width = 600 1281 self.g_Height = 600 1282 1283 self.g_nearPlane = 1. 1284 self.g_farPlane = 1000. 1285 1286 self.action = "" 1287 self.xStart = self.yStart = 0. 1288 self.xStart 1289 self.zoom = 65. 1290 1291 self.xRotate = 0. 1292 self.yRotate = 0. 1293 self.zRotate = 0. 1294 1295 self.xTrans = 0. 1296 self.yTrans = 0. 1297 1298 # 1299 # Forcing a specific style on the window. 1300 # Should this include styles passed? 1301 style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 1302 1303 attribList = (glcanvas.WX_GL_RGBA, # RGBA 1304 glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered 1305 glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit 1306 1307 glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList) 1308 #super(WxGLTest2, self).__init__(parent,-1, attribList=attribList) 1309 1310 self.GLinitialized = False 1311 1312 ### 1313 # Forcing a specific style on the window. 1314 # Should this include styles passed? 1315 #style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 1316 1317 #super(GLFrame, self).__init__(parent, id, title, pos, size, style, name) 1318 #wx.Frame.__init__(self, parent, id, title, pos, size, style, name) 1319 1320 # 1321 # Set the event handlers. 1322 self.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent) 1323 self.Bind(wx.EVT_SIZE, self.OnSize) 1324 self.Bind(wx.EVT_PAINT, self.processPaintEvent) 1325 1326 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) 1327 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) 1328 self.Bind(wx.EVT_MOTION, self.OnMotion) 1329 self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) 1330 1331 # this is needed to initialize GL projections for unproject 1332 wx.CallAfter(self.OnSize) 1333 1334 def add_element(self, element): 1335 self._elements.append(element) 1336 self.OnDraw() 1337 1338 def resetView(): 1339 self.zoom = 65. 1340 self.xRotate = 0. 1341 self.yRotate = 0. 1342 self.zRotate = 0. 1343 self.xTrans = 0. 1344 self.yTrans = 0. 1345 self.OnDraw() 1346 1347 def OnWheel(self, event): 1348 #EventType = FloatCanvas.EVT_FC_MOUSEWHEEL 1349 # 1350 Rot = event.GetWheelRotation() 1351 # print 'OnWheel!!',Rot,event.ControlDown(),event.ShiftDown() 1352 if event.ControlDown(): # event.ControlDown(): # zoom 1353 if Rot < 0: 1354 self.zoom *= 0.9 1355 else: 1356 self.zoom *= 1.1 1357 self.OnDraw() 1358 event.Skip() 1359 1360 def OnLeftDown(self, event): 1361 ## 1362 if (event.ControlDown() & event.ShiftDown()) & (self.action == ''): 1363 self.action = 'drag' 1364 self.BeginGrap(event) 1365 event.Skip() 1366 1367 def OnLeftUp(self, event): 1368 if self.action == 'drag': 1369 self.EndGrap(event) 1370 self.action == '' 1371 event.Skip() 1372 1373 def get_intersection(self, v_near, v_far): 1374 # 150918 1375 # idea from http://www.bfilipek.com/2012/06/select-mouse-opengl.html 1376 # https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection 1377 d = -v_near + v_far 1378 1379 t = -v_near[2]/d[2] 1380 v_inter = v_near+t*d 1381 1382 return v_inter 1383 1384 def OnMotion(self, event): 1385 1386 p = self.unproject(event)[0:2] 1387 for element in self._elements: 1388 inds_pick = element.pick(p) 1389 element.highlight(inds_pick) 1390 1391 self.OnDraw() 1392 1393 if (event.ControlDown() & event.ShiftDown() & (self.action == 'drag')): 1394 self.MoveGrap(event) 1395 self.OnDraw() 1396 event.Skip() 1397 1398 elif (self.action == 'drag'): 1399 self.EndGrap(event) 1400 self.action == '' 1401 event.Skip() 1402 1403 def unproject(self, event): 1404 """Get the world coordinates for viewCoordinate for the event 1405 """ 1406 mousex, mousey = event.GetPosition() 1407 x = mousex 1408 y = self.g_Height-mousey 1409 1410 modelviewmatrix = glGetDoublev(GL_MODELVIEW_MATRIX) 1411 projectionmatrix = glGetDoublev(GL_PROJECTION_MATRIX) 1412 viewport = glGetInteger(GL_VIEWPORT) 1413 z = 0.0 1414 worldCoordinate_near = np.array(gluUnProject( 1415 x, y, z, 1416 modelviewmatrix, 1417 projectionmatrix, 1418 viewport,), dtype=np.float32) 1419 z = 1.0 1420 worldCoordinate_far = np.array(gluUnProject( 1421 x, y, z, 1422 modelviewmatrix, 1423 projectionmatrix, 1424 viewport,), dtype=np.float32) 1425 1426 v_inter = self.get_intersection(worldCoordinate_near, worldCoordinate_far) 1427 return v_inter 1428 1429 def BeginGrap(self, event): 1430 1431 self.xStart, self.yStart = event.GetPosition() 1432 # print 'BeginGrap',self.xStart,self.yStart 1433 1434 def MoveGrap(self, event): 1435 x, y = event.GetPosition() 1436 1437 self.xTrans += x-self.xStart 1438 self.yTrans += y-self.yStart 1439 # print 'MoveGrap',self.xTrans,self.yTrans 1440 self.xStart, self.yStart = x, y 1441 1442 def EndGrap(self, event): 1443 # print 'EndGrap' 1444 self.SetCursor(wx.NullCursor) 1445 self.action = '' 1446 1447 # 1448 # Canvas Proxy Methods 1449 1450 def GetGLExtents(self): 1451 """Get the extents of the OpenGL canvas.""" 1452 return self.GetClientSize() 1453 1454 # def SwapBuffers(self): 1455 # """Swap the OpenGL buffers.""" 1456 # self.SwapBuffers() 1457 1458 # 1459 # wxPython Window Handlers 1460 1461 def processEraseBackgroundEvent(self, event): 1462 """Process the erase background event.""" 1463 pass # Do nothing, to avoid flashing on MSWin 1464 1465 def OnSize(self, event=None, win=None): 1466 """Process the resize event.""" 1467 if self.GetContext(): 1468 # Make sure the frame is shown before calling SetCurrent. 1469 self.Show() 1470 self.SetCurrent() 1471 1472 size = self.GetGLExtents() 1473 self.OnReshape(size.width, size.height) 1474 self.Refresh(False) 1475 if event: 1476 event.Skip() 1477 1478 def processPaintEvent(self, event): 1479 """Process the drawing event.""" 1480 self.SetCurrent() 1481 1482 # This is a 'perfect' time to initialize OpenGL ... only if we need to 1483 if not self.GLinitialized: 1484 self.OnInitGL() 1485 self.GLinitialized = True 1486 1487 self.OnDraw() 1488 event.Skip() 1489 1490 # 1491 # GLFrame OpenGL Event Handlers 1492 1493 def OnInitGL(self): 1494 """Initialize OpenGL for use in the window.""" 1495 glClearColor(0, 0, 0, 1) 1496 1497 def OnReshape(self, width, height): 1498 """Reshape the OpenGL viewport based on the dimensions of the window.""" 1499 #global g_Width, g_Height 1500 self.g_Width = width 1501 self.g_Height = height 1502 glViewport(0, 0, self.g_Width, self.g_Height) 1503 1504 def OnDraw(self, *args, **kwargs): 1505 """Draw the window.""" 1506 # Clear frame buffer and depth buffer 1507 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 1508 # Set up viewing transformation, looking down -Z axis 1509 glLoadIdentity() 1510 gluLookAt(self.eyex, self.eyey, self.eyez, self.centerx, self.centery, 1511 self.centerz, self.upx, self.upy, self.upz) # -.1,0,0 1512 1513 # Set perspective (also zoom) 1514 glMatrixMode(GL_PROJECTION) 1515 glLoadIdentity() 1516 # the window corner OpenGL coordinates are (-+1, -+1) 1517 glOrtho(-1, 1, 1, -1, -1, 1) 1518 1519 aspect = float(self.g_Width)/float(self.g_Height) 1520 1521 gluPerspective(self.zoom, aspect, self.g_nearPlane, self.g_farPlane) 1522 glMatrixMode(GL_MODELVIEW) 1523 self.polarView() 1524 1525 # draw actual scene 1526 for element in self._elements: 1527 element.draw() 1528 # causes bad things :AttributeError: 'Implementation' object has no attribute 'glGenBuffers' 1529 1530 self.SwapBuffers() 1531 1532 def polarView(self): 1533 glTranslatef(self.yTrans/100., 0.0, 0.0) 1534 glTranslatef(0.0, -self.xTrans/100., 0.0) 1535 glRotatef(-self.zRotate, 0.0, 0.0, 1.0) 1536 glRotatef(-self.xRotate, 1.0, 0.0, 0.0) 1537 glRotatef(-self.yRotate, .0, 1.0, 0.0) 1538 1539 1540class GlEditorSash(wx.SplitterWindow): 1541 1542 def __init__(self, parent, 1543 mainframe=None, 1544 size=wx.DefaultSize, 1545 is_menu=False, # create menu items 1546 Debug=0, 1547 ): 1548 1549 wx.SplitterWindow.__init__(self, parent, wx.ID_ANY, 1550 style=wx.SP_LIVE_UPDATE, 1551 size=size) 1552 self.log = log 1553 1554 self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged) 1555 self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging) 1556 1557 # id=wx.ID_ANY 1558 # pixel_snap=10 # radius in pixels in which a point is selected 1559 # n_test=5 1560 # wx.Window.__init__(self,parent,id,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER|wx.WANTS_CHARS) 1561 # wx.Panel.__init__(self,parent,id,wx.DefaultPosition,size,wx.SUNKEN_BORDER|wx.WANTS_CHARS) 1562 # self.parent=parent 1563 self._mainframe = mainframe # mainframe 1564 #splitter = Splitter(self) 1565 1566 self._toolspanel = ToolPalett(self) 1567 self._toolspanel.add_tool(BaseTool(self)) 1568 #sty = wx.BORDER_SUNKEN 1569 #self._toolspanel = wx.Window(self, style=sty) 1570 # self._toolspanel.SetBackgroundColour("pink") 1571 #wx.StaticText(self._toolspanel, -1, "Object", (50,50)) 1572 1573 self._canvas = WxGLTest2(self) 1574 #p2 = wx.Window(self, style=sty) 1575 # p2.SetBackgroundColour("blue") 1576 #wx.StaticText(p2, -1, "GLeditor", (50,50)) 1577 1578 #self.canvas = wx.Window(splitter, style=sty) 1579 # self.canvas.SetBackgroundColour("green") 1580 #wx.StaticText(self.canvas, -1, "Panel two", (50,50)) 1581 1582 #self.canvas = WxGLTest2(splitter) 1583 #self.canvas = WxGLTest_orig(splitter) 1584 1585 #nbpanel = wx.Panel(splitter) 1586 #self._viewtabs = wx.Notebook(nbpanel,wx.ID_ANY, style=wx.CLIP_CHILDREN) 1587 #sizer = wx.BoxSizer(wx.VERTICAL) 1588 #sizer.Add(self._viewtabs, 1, wx.ALL|wx.EXPAND, 5) 1589 # nbpanel.SetSizer(sizer) 1590 # self.Layout() 1591 1592 # finally, put the notebook in a sizer for the panel to manage 1593 # the layout 1594 #sizer = wx.BoxSizer() 1595 #sizer.Add(self._viewtabs, 1, wx.EXPAND) 1596 # self.SetSizer(sizer) 1597 1598 self.SetMinimumPaneSize(20) 1599 #splitter.SplitVertically(p1, self.canvas, -100) 1600 self.SplitVertically(self._toolspanel, self._canvas, -100) 1601 1602 #wx.EVT_SIZE (self, self.on_size) 1603 self.SetSashPosition(500, True) 1604 # sizer=wx.BoxSizer(wx.VERTICAL) 1605 # sizer.Add(p1,0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)# from NaviPanelTest 1606 # sizer.Add(self.canvas,1,wx.GROW)# from NaviPanelTest 1607 1608 # finish panel setup 1609 # self.SetSizer(sizer) 1610 # sizer.Fit(self) 1611 # self.on_size() 1612 # self.Show() 1613 1614 def get_canvas(self): 1615 return self._canvas 1616 1617 def OnSashChanged(self, evt): 1618 #print("sash changed to %s\n" % str(evt.GetSashPosition())) 1619 pass 1620 1621 def OnSashChanging(self, evt): 1622 print("sash changing to %s\n" % str(evt.GetSashPosition())) 1623 # uncomment this to not allow the change 1624 # evt.SetSashPosition(-1) 1625 # evt.SetSashPosition(210) 1626 # self.canvas.OnSize() 1627 pass 1628 1629 def on_size(self, event=None): 1630 # self.tc.SetSize(self.GetSize()) 1631 # self.tc.SetSize(self.GetSize()) 1632 # self._viewtabs.SetSize(self.GetSize()) 1633 # pass 1634 #wx.LayoutAlgorithm().LayoutWindow(self, self.p1) 1635 #wx.LayoutAlgorithm().LayoutWindow(self, self.p1) 1636 1637 # important: 1638 #wx.LayoutAlgorithm().LayoutWindow(self, self._viewtabs) 1639 1640 if event: 1641 event.Skip() 1642 1643 1644class GlEditor(wx.Panel): 1645 1646 def __init__(self, parent, 1647 mainframe=None, 1648 size=wx.DefaultSize, 1649 is_menu=False, # create menu items 1650 Debug=0, 1651 ): 1652 1653 wx.Panel.__init__(self, parent, wx.ID_ANY, size=size) 1654 sizer = wx.BoxSizer(wx.HORIZONTAL) 1655 1656 self._mainframe = mainframe 1657 1658 self._toolspanel = ToolsPanel(self) 1659 for i in range(5): 1660 self._toolspanel.add_tool(BaseTool(self)) 1661 self._toolspanel.add_tool(DelTool(self)) 1662 #sty = wx.BORDER_SUNKEN 1663 #self._toolspanel = wx.Window(self, style=sty) 1664 # self._toolspanel.SetBackgroundColour("pink") 1665 #wx.StaticText(self._toolspanel, -1, "Object", (50,50)) 1666 1667 self._canvas = WxGLTest2(self) 1668 #p2 = wx.Window(self, style=sty) 1669 # p2.SetBackgroundColour("blue") 1670 #wx.StaticText(p2, -1, "GLeditor", (50,50)) 1671 1672 sizer.Add(self._toolspanel, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4) # from NaviPanelTest 1673 sizer.Add(self._canvas, 1, wx.GROW) # from NaviPanelTest 1674 1675 # finish panel setup 1676 self.SetSizer(sizer) 1677 sizer.Fit(self) 1678 1679 def get_canvas(self): 1680 return self._canvas 1681 1682 def on_size(self, event=None): 1683 # self.tc.SetSize(self.GetSize()) 1684 # self.tc.SetSize(self.GetSize()) 1685 # self._viewtabs.SetSize(self.GetSize()) 1686 # pass 1687 #wx.LayoutAlgorithm().LayoutWindow(self, self.p1) 1688 #wx.LayoutAlgorithm().LayoutWindow(self, self.p1) 1689 1690 # important: 1691 #wx.LayoutAlgorithm().LayoutWindow(self, self._viewtabs) 1692 1693 if event: 1694 event.Skip() 1695 1696 1697class MainSplitter(wx.SplitterWindow): 1698 def __init__(self, parent, ID=-1): 1699 wx.SplitterWindow.__init__(self, parent, ID, 1700 style=wx.SP_LIVE_UPDATE 1701 ) 1702 1703 self.SetMinimumPaneSize(20) 1704 1705 #sty = wx.BORDER_SUNKEN 1706 1707 emptyobj = cm.BaseObjman('empty') 1708 self._objbrowser = objpanel.NaviPanel(self, 1709 emptyobj, 1710 #show_title = False 1711 #size = w.DefaultSize, 1712 #style = wx.DEFAULT_DIALOG_STYLE|wx.MAXIMIZE_BOX|wx.RESIZE_BORDER, 1713 # choose_id=False,choose_attr=False, 1714 # func_choose_id=None, 1715 # func_change_obj=None, 1716 #panelstyle = 'default', 1717 immediate_apply=False, 1718 buttons=[], 1719 standartbuttons=[], 1720 #defaultbutton = defaultbutton, 1721 ) 1722 1723 #p1 = wx.Window(splitter, style=sty) 1724 # p1.SetBackgroundColour("pink") 1725 #wx.StaticText(p1, -1, "Object", (50,50)) 1726 1727 #self.canvas = wx.Window(splitter, style=sty) 1728 # self.canvas.SetBackgroundColour("green") 1729 #wx.StaticText(self.canvas, -1, "Panel two", (50,50)) 1730 #self.canvas = WxGLTest2(splitter) 1731 #self.canvas = WxGLTest_orig(splitter) 1732 1733 #self._viewtabs = wx.Notebook(self,wx.ID_ANY, style=wx.CLIP_CHILDREN) 1734 self._viewtabs = wx.Notebook(self, -1, size=(21, 21), style=wx.BK_DEFAULT 1735 # wx.BK_TOP 1736 # wx.BK_BOTTOM 1737 # wx.BK_LEFT 1738 # wx.BK_RIGHT 1739 # | wx.NB_MULTILINE 1740 ) 1741 1742 #nbpanel = wx.Panel(splitter) 1743 #self._viewtabs = wx.Notebook(nbpanel,wx.ID_ANY, style=wx.CLIP_CHILDREN) 1744 #sizer = wx.BoxSizer(wx.VERTICAL) 1745 #sizer.Add(self._viewtabs, 1, wx.ALL|wx.EXPAND, 5) 1746 # nbpanel.SetSizer(sizer) 1747 # self.Layout() 1748 1749 # finally, put the notebook in a sizer for the panel to manage 1750 # the layout 1751 #sizer = wx.BoxSizer() 1752 #sizer.Add(self._viewtabs, 1, wx.EXPAND) 1753 # self.SetSizer(sizer) 1754 1755 #splitter.SplitVertically(self._objbrowser,self.canvas , -100) 1756 self.SplitVertically(self._objbrowser, self._viewtabs, -100) 1757 1758 self.SetSashPosition(500, True) 1759 self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged) 1760 self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING, self.OnSashChanging) 1761 1762 def add_view(self, name, ViewClass, **args): 1763 """ 1764 Add a new view to the notebook. 1765 """ 1766 # print 'context.add_view',ViewClass 1767 # print ' args',args 1768 view = ViewClass(self._viewtabs, 1769 mainframe=self.GetParent(), 1770 **args 1771 ) 1772 1773 # Add network tab with editor 1774 p = self._viewtabs.AddPage(view, name.title()) 1775 #self._views[name] = view 1776 # self._viewtabs.SetSelection(p) 1777 # self._viewtabs.Show(True) 1778 return view 1779 1780 def OnSashChanged(self, evt): 1781 #print("sash changed to %s\n" % str(evt.GetSashPosition())) 1782 pass 1783 1784 def OnSashChanging(self, evt): 1785 #print("sash changing to %s\n" % str(evt.GetSashPosition())) 1786 # uncomment this to not allow the change 1787 # evt.SetSashPosition(-1) 1788 # self.canvas.OnSize() 1789 pass 1790 1791 1792class TestMainframe(AgileToolbarFrameMixin, wx.Frame): 1793 """ 1794 Simple wx frame with some special features. 1795 """ 1796 1797 def __init__(self, parent=None, id=-1, title='mainframe', pos=wx.DefaultPosition, 1798 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, 1799 name='frame'): 1800 1801 # Forcing a specific style on the window. 1802 # Should this include styles passed? 1803 style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 1804 wx.Frame.__init__(self, parent, id, title, pos, size=size, style=style, name=name) 1805 #super(GLFrame, self).__init__(parent, id, title, pos, size, style, name) 1806 self._splitter = MainSplitter(self) 1807 self._views = {} 1808 #wx.EVT_SIZE (self, self.on_size) 1809 # sizer=wx.BoxSizer(wx.VERTICAL) 1810 # sizer.Add(p1,0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)# from NaviPanelTest 1811 # sizer.Add(self.canvas,1,wx.GROW)# from NaviPanelTest 1812 1813 # finish panel setup 1814 # self.SetSizer(sizer) 1815 # sizer.Fit(self) 1816 # self.Show() 1817 1818 # this is needed to initialize GL projections for unproject 1819 # wx.CallAfter(self.on_size) 1820 1821 #width,height = self.GetSize() 1822 #self._splitter.SetSashPosition(300, True) 1823 # maximize the frame 1824 self.Maximize() 1825 # self.CenterOnScreen() 1826 ################################################################# 1827 # create statusbar 1828 #self.statusbar = AgileStatusbar(self) 1829 self.statusbar = AgileStatusbar(self) 1830 self.SetStatusBar(self.statusbar) 1831 # self.count=0.0 1832 1833 ################################################################# 1834 # create toolbar 1835 1836 tsize = (16, 16) 1837 self.init_toolbar(size=tsize) 1838 1839 #new_bmp = wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR, tsize) 1840 #open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize) 1841 #save_bmp= wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize) 1842 #cut_bmp = wx.ArtProvider.GetBitmap(wx.ART_CUT, wx.ART_TOOLBAR, tsize) 1843 #copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR, tsize) 1844 #paste_bmp= wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR, tsize) 1845 1846 #self.add_tool('new',self.on_open,new_bmp,'create new doc') 1847 #self.add_tool('open',self.on_open,open_bmp,'Open doc') 1848 #self.add_tool('save',self.on_save,save_bmp,'Save doc') 1849 # self.toolbar.AddSeparator() 1850 # self.add_tool('cut',self.on_open,cut_bmp,'Cut') 1851 # self.add_tool('copy',self.on_open,copy_bmp,'Copy') 1852 # self.add_tool('paste',self.on_open,paste_bmp,'Paste') 1853 1854 # self.SetToolBar(self.toolbar) 1855 1856 ################################################################# 1857 # create the menu bar 1858 1859 self.menubar = AgileMenubar(self) 1860 self.menubar.append_menu('file') 1861 self.menubar.append_menu('file/doc') 1862 1863 self.menubar.append_item('file/doc/open', self.on_open, 1864 shortkey='Ctrl+o', info='open it out') 1865 1866 self.menubar.append_item('file/doc/save', self.on_save, 1867 shortkey='Ctrl+s', info='save it out') 1868 1869 # self.menubar.append_menu('edit') 1870 # self.menubar.append_item('edit/cut',self.cut,\ 1871 # shortkey='Ctrl+c',info='cut it out') 1872 1873 # self.menubar.append_item('edit/toggle',self.toggle_tools,\ 1874 # shortkey='Ctrl+t',info='toggle tools') 1875 # self.menubar.append_menu('tools') 1876 self.SetMenuBar(self.menubar) 1877 # self.Show(True) #NO!! 1878 1879 def add_view(self, name, ViewClass, **args): 1880 """ 1881 Add a new view to the notebook. 1882 """ 1883 # print 'context.add_view',ViewClass 1884 # print ' args',args 1885 1886 view = self._splitter.add_view(name, ViewClass, **args) 1887 self._views[name] = view 1888 # self._viewtabs.SetSelection(p) 1889 # self._splitter._viewtabs.Show(True) 1890 return view 1891 1892 def on_size(self, event=None): 1893 print 'Mainframe.on_size' 1894 # self.tc.SetSize(self.GetSize()) 1895 # self.tc.SetSize(self.GetSize()) 1896 # self._viewtabs.SetSize(self.GetSize()) 1897 # pass 1898 #wx.LayoutAlgorithm().LayoutWindow(self, self.p1) 1899 #wx.LayoutAlgorithm().LayoutWindow(self, self.p1) 1900 1901 # important: 1902 #wx.LayoutAlgorithm().LayoutWindow(self, self._viewtabs) 1903 wx.LayoutAlgorithm().LayoutWindow(self, self._splitter) 1904 if event: 1905 event.Skip() 1906 1907 def on_save(self, event): 1908 print 'save it!!' 1909 1910 def on_open(self, event): 1911 """Open a document""" 1912 #wildcards = CreateWildCards() + "All files (*.*)|*.*" 1913 print 'open it!!' 1914 1915 def destroy(self): 1916 """Destroy this object""" 1917 # self.theDocManager.theDestructor() 1918 #imgPreferences.saveXml(self.GetStartDirectory() + "/" + imgINI_FILE_NAME) 1919 ##del self.thePrint 1920 self.Destroy() 1921 1922 def on_close(self, event): 1923 # self.Close(True) 1924 # pass 1925 self.destroy() 1926 1927 def on_exit(self, event): 1928 """Called when the application is to be finished""" 1929 self.destroy() 1930 1931 def on_idle(self, event): 1932 pass 1933 #self.count = self.count + 1 1934 # if self.count >= 100: 1935 # self.count = 0 1936 1937 # self.statusbar.set_progress(self.count) 1938 1939 def on_about(self, event): 1940 """Display the information about this application""" 1941 #dlg = imgDlgAbout(self, -1, "") 1942 # dlg.ShowModal() 1943 # dlg.Destroy() 1944 pass 1945 1946 1947linewidth = 3 1948vertices = [ 1949 [[0.0, 0.0, 0.0], [0.2, 0.0, 0.0]], # 0 green 1950 [[0.0, 0.0, 0.0], [0.0, 0.9, 0.0]], # 1 red 1951] 1952 1953 1954colors = [ 1955 [0.0, 0.9, 0.0, 0.9], # 0 1956 [0.9, 0.0, 0.0, 0.9], # 1 1957] 1958lines = Lines(linewidth=linewidth, vertices=vertices, colors=colors) 1959 1960linewidth2 = 3 1961vertices2 = [ 1962 [[0.5, 0.5, 0.0], [0.7, 0.5, 0.0], [0.7, 1.0, 0.0]], # 0 green 1963 [[0.8, 0.5, 0.0], [0.9, 0.8, 0.0], [0.8, 0.2, 0.0]], # 1 orange 1964] 1965colors2 = [ 1966 [0.0, 0.9, 0.3, 0.9], # 0 1967 [0.9, 0.3, 0.0, 0.9], # 1 1968] 1969triangles = Triangles(linewidth=linewidth2, vertices=vertices2, colors=colors2) 1970 1971linewidth3 = 3 1972vertices3 = [ 1973 [[0.5, 0.0, 0.0], [0.7, 0.0, 0.0], [0.7, 0.3, 0.0], [0.5, 0.3, 0.0], ], # 0 1974 [[0.1, 0.0, 0.0], [0.3, 0.0, 0.0], [0.3, 0.2, 0.0], [0.1, 0.2, 0.0], ], # 1 1975] 1976colors3 = [ 1977 [0.8, 0.0, 0.8, 0.9], # 0 1978 [0.0, 0.6, 0.6, 0.9], # 1 1979] 1980rectangles = Rectangles(linewidth=linewidth3, vertices=vertices3, colors=colors3) 1981 1982if __name__ == '__main__': 1983 1984 app = wx.PySimpleApp() 1985 1986 if 1: 1987 1988 frame = TestMainframe() 1989 gleditor = frame.add_view('GL Editor', GlEditor) 1990 #gleditor = frame._splitter.add_view('GL Editor',GlEditor) 1991 frame.Show() 1992 frame.on_size() 1993 canvas = gleditor.get_canvas() 1994 canvas.add_element(lines) 1995 canvas.add_element(triangles) 1996 canvas.add_element(rectangles) 1997 1998 app.SetTopWindow(frame) 1999 app.MainLoop() 2000 2001 app.Destroy() 2002