Path: blob/main/tools/contributed/sumopy/agilepy/lib_wx/toolbox.py
169689 views
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo1# Copyright (C) 2016-2025 German Aerospace Center (DLR) and others.2# SUMOPy module3# Copyright (C) 2012-2021 University of Bologna - DICAM4# This program and the accompanying materials are made available under the5# terms of the Eclipse Public License 2.0 which is available at6# https://www.eclipse.org/legal/epl-2.0/7# This Source Code may also be made available under the following Secondary8# Licenses when the conditions for such availability set forth in the Eclipse9# Public License 2.0 are satisfied: GNU General Public License, version 210# or later which is available at11# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html12# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later1314# @file toolbox.py15# @author Joerg Schweizer16# @date 20121718import agilepy.lib_base.arrayman as am19import agilepy.lib_base.classman as cm20from objpanel import ObjPanel, NaviPanel21from wx.lib.buttons import GenBitmapTextButton, GenBitmapButton22import wx23import sys24import os25import string26import time27if __name__ == '__main__':28try:29FILEDIR = os.path.dirname(os.path.abspath(__file__))30except:31FILEDIR = os.path.dirname(os.path.abspath(sys.argv[0]))32sys.path.append(os.path.join(FILEDIR, "..", ".."))3334IMAGEDIR = os.path.join(os.path.dirname(__file__), "images")353637class BaseTool(am.ArrayObjman):38"""39This is a base tool class for Agilecanvas.40It must handle all mouse or keyboard events,41must create and draw helplines and finally42modify the state of client which are graphically43represented on the canvas.44"""4546def __init__(self, parent):47"""48To be overridden by specific tool.49"""50self.init_common('select', parent, 'Selection tool',51info='Select objects in canvas',52is_textbutton=True,53)5455def set_button_info(self, bsize=(32, 32)):56# print 'set_button_info select tool'57self._bitmap = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIcon.bmp'), wx.BITMAP_TYPE_BMP)58self._bitmap_sel = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIconSel.bmp'), wx.BITMAP_TYPE_BMP)5960def set_cursor(self):61# http://www.wxpython.org/docs/api/wx.Cursor-class.html62if self._canvas is not None:63# self._canvas.SetCursor(wx.StockCursor(wx.CURSOR_QUESTION_ARROW))64pass6566def get_button(self, parent, bottonsize=(32, 32), bottonborder=10):67"""68Returns button widget.69Called when toolbar is created.70"""71# simple stockbuttons72#b=wx.Button(parent, wx.ID_DELETE)7374id = wx.NewId()75bitmap = self._bitmap7677if self._is_textbutton:78b = GenBitmapTextToggleButton(parent, id, bitmap, self.ident.title(), name=self.get_name())79else:80b = GenBitmapToggleButton(parent, id, bitmap,81(bitmap.GetWidth()+bottonborder, bitmap.GetHeight()+bottonborder),82name=self.get_name())83#b=GenBitmapToggleButton(self, wx.ID_DELETE)84#b = GenBitmapTextToggleButton(self, id, None, tool.get('name',''), size = (200, 45))8586if bitmap is not None:87#mask = wx.Mask(bitmap, wx.BLUE)88# bitmap.SetMask(mask)89b.SetBitmapLabel(bitmap)90# bmp=wx.NullBitmap9192bitmap_sel = self._bitmap_sel93if bitmap_sel is not None:94#mask = wx.Mask(bmp, wx.BLUE)95# bmp.SetMask(mask)96b.SetBitmapSelected(bitmap_sel)9798b.SetUseFocusIndicator(False)99100b.SetUseFocusIndicator(False)101# b.SetSize((36,140))102# b.SetBestSize()103tt = wx.ToolTip(self.get_info())104b.SetToolTip(tt) # .SetTip(tool.tooltip)105return b106107def init_common(self, ident, parent, name, info=None, is_textbutton=False):108# print 'Agiletool.__init__',ident,name109#self.name = name110self._is_textbutton = is_textbutton111self._canvas = None112self._init_objman(ident, parent=parent, name=name.title(), info=info)113#attrsman = self.set_attrsman(cm.Attrsman(self))114self._is_active = False115116# print ' call set_button',self.ident117self.set_button_info()118self._optionspanel = None119120def get_optionspanel(self, parent, size=wx.DefaultSize):121"""122Return tool option widgets on given parent123"""124size = (200, -1)125self._optionspanel = ObjPanel(parent, obj=self,126attrconfigs=None,127#tables = None,128# table = None, id=None, ids=None,129groupnames=['options'],130func_change_obj=None,131show_groupnames=False, show_title=True, is_modal=False,132mainframe=self.parent.get_mainframe(),133pos=wx.DefaultPosition, size=size, style=wx.MAXIMIZE_BOX | wx.RESIZE_BORDER,134immediate_apply=False, panelstyle='default', # 'instrumental'135standartbuttons=['apply', 'restore'])136137return self._optionspanel138139def activate(self, canvas=None):140"""141This call by metacanvas??TooldsPallet signals that the tool has been142activated and can now interact with metacanvas.143"""144# print 'activate',self.ident145self._is_active = True146self._canvas = canvas147# self._canvas.del_handles()148canvas.activate_tool(self)149self.set_cursor()150151def get_drawing(self):152return self.parent.get_drawing()153154def get_drawobj_by_ident(self, ident):155return self.get_drawing().get_drawobj_by_ident(ident)156157def deactivate(self):158"""159This call by metacanvas??? ToolePallet signals that the tool has been160deactivated and can now interact with metacanvas.161"""162self._canvas.deactivate_tool()163self._canvas = None164self._is_active = False165166def is_active(self):167return self._is_active168169def force_deactivation(self):170"""171Explicit call to deactivate this tool in the tools panel.172"""173self.parent.unselect_tool()174175def on_left_down(self, event):176return False177178def on_left_up(self, event):179return False180181def on_left_dclick(self, event):182return False183184def on_right_down(self, event):185return False186187def on_right_up(self, event):188return self.aboard(event)189190def aboard(self):191return False192193def on_wheel(self, event):194return False195196def on_motion(self, event):197return False # return True if something moved198199200class DelTool(BaseTool):201def __init__(self, parent):202"""203To be overridden by specific tool.204"""205self.init_common('delete', parent, 'Delete', info='Delete objects in canvas')206207def set_button_info(self, bsize=(32, 32)):208# print 'set_button_info select tool'209self._bitmap = None210self._bitmap_sel = None211212def get_button(self, parent, bottonsize=(32, 32), bottonborder=10):213214# simple stockbuttons215b = wx.Button(parent, wx.ID_DELETE, name=self.get_name())216217b.SetSize(bottonsize)218# b.SetBestSize()219tt = wx.ToolTip(self.get_info())220b.SetToolTip(tt) # .SetTip(tool.tooltip)221# print 'DelTool.get_button',dir(b)222return b223224225class ToolPalett(wx.Panel):226"""227This is a panel where tools are represented by images and/or text.228The tools are selected in a radio-button-fashion.229230Each tool has a string as key. Each time the status changes,231a callback function is called with new and old tool key as argument.232"""233234def __init__(self, parent, tools=[], callback=None, n_buttoncolumns=3):235"""236callback is a function that is called when a tool has been selected.237The function is called as:238callback(tool)239240"""241# the metacanvas object with which the pallet should apply th tools242243# callback when a new tool gets selected (NOT in USE)244self._callback = callback245246# wx.Window.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER|wx.WANTS_CHARS)247# wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,size,wx.RAISED_BORDER|wx.WANTS_CHARS)248wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, wx.DefaultSize)249# wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,(300,600),wx.RAISED_BORDER|wx.WANTS_CHARS)250self.sizer = wx.GridSizer(0, n_buttoncolumns, 5, 5)251self.SetSizer(self.sizer)252self._id_to_tool = {}253self._id = -1254255for tool in tools:256self.add_tool(tool)257258# self.sizer.Fit(self)259# self.SetMaxSize((300,-1))260261def has_tool(self, newtool):262for tool, b in self._id_to_tool.values():263if tool.get_ident() == newtool.get_ident():264return True265return False266267def get_tool_by_ident(self, ident):268# print 'get_tool_by_ident',ident269for tool, b in self._id_to_tool.values():270# print ' tool',tool.get_ident()271if tool.get_ident() == ident:272return tool273274return None275276def add_tool(self, tool):277"""278Add a tool to the pallet.279"""280if not self.has_tool(tool):281# print 'add_tool',tool282bottonsize = (32, 32)283bottonborder = 10284toolbarborder = 1285286b = tool.get_button(self, bottonsize=bottonsize, bottonborder=bottonborder)287self.Bind(wx.EVT_BUTTON, self.on_select, b)288289_id = b.GetId()290self._id_to_tool[_id] = (tool, b)291292#self.sizer.Add(b, 0, wx.GROW)293self.sizer.Add(b, 0, wx.EXPAND, border=toolbarborder)294# self.sizer.Add(b)295# print ' _id =',_id296return _id297else:298return -1299300def get_tools(self):301"""302Returns lins with all toll instances303"""304tools = []305for (tool, b) in self._id_to_tool.values():306tools.append(tool)307return tools308309def refresh(self):310"""311Reorganizes toolpallet after adding/removing tools.312Attention is not automatically called.313"""314self.sizer.Layout()315316def on_select(self, event):317"""318Called from a pressed button319"""320_id = event.GetEventObject().GetId()321# print '\n on_select',_id,self._id#,self._id_to_tool[_id]322323if _id != self._id:324if self._id_to_tool.has_key(_id):325326(tool, button) = self._id_to_tool[_id]327# print ' new tool',tool.get_name()328self.unselect()329self._id = _id330331# this will cause the main OGL editor to activate the332# tool with the current canvas333self.GetParent().set_tool(tool)334# if self._callback is not None:335# self._callback(tool)336event.Skip()337return tool338339return None340341def select(self, _id):342"""343Select explicitelt a tool with _id.344"""345# print '\nselect',_id,self._id,self._id_to_tool346347if _id != self._id:348if self._id_to_tool.has_key(_id):349350(tool, button) = self._id_to_tool[_id]351352# print ' explicitly press button'353if hasattr(button, 'SetToggle'):354button.SetToggle(True)355else:356button.SetFocus()357# print 'button.SetFocus',button.SetFocus.__doc__358# pass359360# print ' new tool',tool.get_name()361# self.unselect()362self._id = _id363364self.GetParent().set_tool(tool)365# if self._callback is not None:366# self._callback(tool)367return tool368369return None370371def unselect(self):372"""373Unselect currently selected tool.374"""375if self._id_to_tool.has_key(self._id):376(tool, button) = self._id_to_tool[self._id]377378if tool.is_active() == True:379# Disactivate current tool380tool.deactivate()381382if hasattr(button, 'SetToggle'):383button.SetToggle(False)384else:385# button.SetFocus()386# print 'button.SetFocus',button.SetFocus.__doc__387pass388389390class __ToggleMixin:391def SetToggle(self, flag):392self.up = not flag393self.Refresh()394SetValue = SetToggle395396def GetToggle(self):397return not self.up398GetValue = GetToggle399400def OnLeftDown(self, event):401if not self.IsEnabled():402return403self.saveUp = self.up404self.up = False # not self.up405self.CaptureMouse()406self.SetFocus()407self.Refresh()408409def OnLeftUp(self, event):410if not self.IsEnabled() or not self.HasCapture():411return412if self.HasCapture():413if self.up != self.saveUp:414self.Notify()415self.ReleaseMouse()416self.Refresh()417418def OnKeyDown(self, event):419event.Skip()420421422class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):423"""A generic toggle bitmap button with text label"""424pass425426427class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):428"""A generic toggle bitmap button with text label"""429pass430431432class ToolsPanel(wx.Panel):433"""434Shows a toolpallet with different tools and an options panel.435"""436437def __init__(self, parent, size=wx.DefaultSize, size_title=150, **kwargs):438439#size = wx.DefaultSize440#size = (300,-1)441wx.Panel.__init__(self, parent, wx.NewId(), wx.DefaultPosition, size)442# wx.DefaultSize443# sizer=wx.BoxSizer(wx.VERTICAL)444sizer = wx.StaticBoxSizer(wx.StaticBox(parent, wx.NewId(), "test"), wx.VERTICAL)445446self._toolspalett = ToolPalett(self, **kwargs)447448# self._toolspalett.add_tool(BaseTool(self))449450# create initial option panel451self._optionspanel = wx.Window(self)452self._optionspanel.SetBackgroundColour("pink")453wx.StaticText(self._optionspanel, -1, "Tool Options", (size_title, -1))454455# OK, but toolspane changes size with optionpanel456#sizer.Add(self._toolspalett,0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)457# sizer.Add(self._optionspanel,1,wx.GROW)# wx.EXPAND458459sizer.Add(self._toolspalett, 0, wx.EXPAND)460sizer.Add(self._optionspanel, 1, wx.EXPAND)461462# finish panel setup463self.SetSizer(sizer)464sizer.Fit(self)465466# self.SetSize(parent.GetSize())467# self.SetMaxSize((300,-1))468469def get_canvas(self):470# ask the OGL editor for the currently active canvas in focus471return self.GetParent().get_canvas()472473def get_drawing(self):474return self.get_canvas().get_drawing()475476def get_mainframe(self):477return self.GetParent().get_mainframe()478479def add_tool(self, tool):480return self._toolspalett.add_tool(tool)481482def add_toolclass(self, ToolClass, **kwargs):483# init and add484return self._toolspalett.add_tool(ToolClass(self, **kwargs))485486def add_initial_tool(self, tool):487self._id_initialtool = self.add_tool(tool)488489def reset_initial_tool(self):490self.set_tool_with_id(self._id_initialtool)491492def reset_initial_tool(self):493self.set_tool_with_id(self._id_initialtool)494495def set_tool_with_id(self, _id):496"""497Explicitely set a tool from tool pallet using its id.498Used to set initial tool.499"""500# print 'set_tool_with_id',_id501return self._toolspalett.select(_id)502503def set_tool(self, tool):504"""505Called by toolpallet after new tool has been selected.506"""507# Activate current tool508# then tool wil set itself to canvas509tool.activate(self.get_canvas())510511# set options of current tool512self.refresh_optionspanel(tool)513514def get_tool_by_ident(self, ident):515return self._toolspalett.get_tool_by_ident(ident)516517def refresh_optionspanel(self, tool):518sizer = self.GetSizer()519sizer.Remove(1)520self._optionspanel.Destroy()521522self._optionspanel = tool.get_optionspanel(self) # , size = self.GetSize())523# self._optionspanel.SetSize((100,0))524# if id is not None:525# self.objpanel=ObjPanel(self,obj,id=id,func_change_obj=self.change_obj)526# else:527# self.objpanel=ObjPanel(self,obj,func_change_obj=self.change_obj)528529# ok, but chanes sice of whole palle530# sizer.Add(self._optionspanel,1,wx.GROW)531532sizer.Add(self._optionspanel, 1, wx.EXPAND)533534# self.Refresh()535# sizer.Fit(self)536sizer.Layout()537# self.GetParent().Layout()538539def unselect_tool(self):540"""541Unselect currently selected tool.542"""543self._toolspalett.unselect()544545546