Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/contributed/sumopy/agilepy/lib_wx/toolbox.py
169689 views
1
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
2
# Copyright (C) 2016-2025 German Aerospace Center (DLR) and others.
3
# SUMOPy module
4
# Copyright (C) 2012-2021 University of Bologna - DICAM
5
# This program and the accompanying materials are made available under the
6
# terms of the Eclipse Public License 2.0 which is available at
7
# https://www.eclipse.org/legal/epl-2.0/
8
# This Source Code may also be made available under the following Secondary
9
# Licenses when the conditions for such availability set forth in the Eclipse
10
# Public License 2.0 are satisfied: GNU General Public License, version 2
11
# or later which is available at
12
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
13
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
14
15
# @file toolbox.py
16
# @author Joerg Schweizer
17
# @date 2012
18
19
import agilepy.lib_base.arrayman as am
20
import agilepy.lib_base.classman as cm
21
from objpanel import ObjPanel, NaviPanel
22
from wx.lib.buttons import GenBitmapTextButton, GenBitmapButton
23
import wx
24
import sys
25
import os
26
import string
27
import time
28
if __name__ == '__main__':
29
try:
30
FILEDIR = os.path.dirname(os.path.abspath(__file__))
31
except:
32
FILEDIR = os.path.dirname(os.path.abspath(sys.argv[0]))
33
sys.path.append(os.path.join(FILEDIR, "..", ".."))
34
35
IMAGEDIR = os.path.join(os.path.dirname(__file__), "images")
36
37
38
class BaseTool(am.ArrayObjman):
39
"""
40
This is a base tool class for Agilecanvas.
41
It must handle all mouse or keyboard events,
42
must create and draw helplines and finally
43
modify the state of client which are graphically
44
represented on the canvas.
45
"""
46
47
def __init__(self, parent):
48
"""
49
To be overridden by specific tool.
50
"""
51
self.init_common('select', parent, 'Selection tool',
52
info='Select objects in canvas',
53
is_textbutton=True,
54
)
55
56
def set_button_info(self, bsize=(32, 32)):
57
# print 'set_button_info select tool'
58
self._bitmap = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIcon.bmp'), wx.BITMAP_TYPE_BMP)
59
self._bitmap_sel = wx.Bitmap(os.path.join(IMAGEDIR, 'selectIconSel.bmp'), wx.BITMAP_TYPE_BMP)
60
61
def set_cursor(self):
62
# http://www.wxpython.org/docs/api/wx.Cursor-class.html
63
if self._canvas is not None:
64
# self._canvas.SetCursor(wx.StockCursor(wx.CURSOR_QUESTION_ARROW))
65
pass
66
67
def get_button(self, parent, bottonsize=(32, 32), bottonborder=10):
68
"""
69
Returns button widget.
70
Called when toolbar is created.
71
"""
72
# simple stockbuttons
73
#b=wx.Button(parent, wx.ID_DELETE)
74
75
id = wx.NewId()
76
bitmap = self._bitmap
77
78
if self._is_textbutton:
79
b = GenBitmapTextToggleButton(parent, id, bitmap, self.ident.title(), name=self.get_name())
80
else:
81
b = GenBitmapToggleButton(parent, id, bitmap,
82
(bitmap.GetWidth()+bottonborder, bitmap.GetHeight()+bottonborder),
83
name=self.get_name())
84
#b=GenBitmapToggleButton(self, wx.ID_DELETE)
85
#b = GenBitmapTextToggleButton(self, id, None, tool.get('name',''), size = (200, 45))
86
87
if bitmap is not None:
88
#mask = wx.Mask(bitmap, wx.BLUE)
89
# bitmap.SetMask(mask)
90
b.SetBitmapLabel(bitmap)
91
# bmp=wx.NullBitmap
92
93
bitmap_sel = self._bitmap_sel
94
if bitmap_sel is not None:
95
#mask = wx.Mask(bmp, wx.BLUE)
96
# bmp.SetMask(mask)
97
b.SetBitmapSelected(bitmap_sel)
98
99
b.SetUseFocusIndicator(False)
100
101
b.SetUseFocusIndicator(False)
102
# b.SetSize((36,140))
103
# b.SetBestSize()
104
tt = wx.ToolTip(self.get_info())
105
b.SetToolTip(tt) # .SetTip(tool.tooltip)
106
return b
107
108
def init_common(self, ident, parent, name, info=None, is_textbutton=False):
109
# print 'Agiletool.__init__',ident,name
110
#self.name = name
111
self._is_textbutton = is_textbutton
112
self._canvas = None
113
self._init_objman(ident, parent=parent, name=name.title(), info=info)
114
#attrsman = self.set_attrsman(cm.Attrsman(self))
115
self._is_active = False
116
117
# print ' call set_button',self.ident
118
self.set_button_info()
119
self._optionspanel = None
120
121
def get_optionspanel(self, parent, size=wx.DefaultSize):
122
"""
123
Return tool option widgets on given parent
124
"""
125
size = (200, -1)
126
self._optionspanel = ObjPanel(parent, obj=self,
127
attrconfigs=None,
128
#tables = None,
129
# table = None, id=None, ids=None,
130
groupnames=['options'],
131
func_change_obj=None,
132
show_groupnames=False, show_title=True, is_modal=False,
133
mainframe=self.parent.get_mainframe(),
134
pos=wx.DefaultPosition, size=size, style=wx.MAXIMIZE_BOX | wx.RESIZE_BORDER,
135
immediate_apply=False, panelstyle='default', # 'instrumental'
136
standartbuttons=['apply', 'restore'])
137
138
return self._optionspanel
139
140
def activate(self, canvas=None):
141
"""
142
This call by metacanvas??TooldsPallet signals that the tool has been
143
activated and can now interact with metacanvas.
144
"""
145
# print 'activate',self.ident
146
self._is_active = True
147
self._canvas = canvas
148
# self._canvas.del_handles()
149
canvas.activate_tool(self)
150
self.set_cursor()
151
152
def get_drawing(self):
153
return self.parent.get_drawing()
154
155
def get_drawobj_by_ident(self, ident):
156
return self.get_drawing().get_drawobj_by_ident(ident)
157
158
def deactivate(self):
159
"""
160
This call by metacanvas??? ToolePallet signals that the tool has been
161
deactivated and can now interact with metacanvas.
162
"""
163
self._canvas.deactivate_tool()
164
self._canvas = None
165
self._is_active = False
166
167
def is_active(self):
168
return self._is_active
169
170
def force_deactivation(self):
171
"""
172
Explicit call to deactivate this tool in the tools panel.
173
"""
174
self.parent.unselect_tool()
175
176
def on_left_down(self, event):
177
return False
178
179
def on_left_up(self, event):
180
return False
181
182
def on_left_dclick(self, event):
183
return False
184
185
def on_right_down(self, event):
186
return False
187
188
def on_right_up(self, event):
189
return self.aboard(event)
190
191
def aboard(self):
192
return False
193
194
def on_wheel(self, event):
195
return False
196
197
def on_motion(self, event):
198
return False # return True if something moved
199
200
201
class DelTool(BaseTool):
202
def __init__(self, parent):
203
"""
204
To be overridden by specific tool.
205
"""
206
self.init_common('delete', parent, 'Delete', info='Delete objects in canvas')
207
208
def set_button_info(self, bsize=(32, 32)):
209
# print 'set_button_info select tool'
210
self._bitmap = None
211
self._bitmap_sel = None
212
213
def get_button(self, parent, bottonsize=(32, 32), bottonborder=10):
214
215
# simple stockbuttons
216
b = wx.Button(parent, wx.ID_DELETE, name=self.get_name())
217
218
b.SetSize(bottonsize)
219
# b.SetBestSize()
220
tt = wx.ToolTip(self.get_info())
221
b.SetToolTip(tt) # .SetTip(tool.tooltip)
222
# print 'DelTool.get_button',dir(b)
223
return b
224
225
226
class ToolPalett(wx.Panel):
227
"""
228
This is a panel where tools are represented by images and/or text.
229
The tools are selected in a radio-button-fashion.
230
231
Each tool has a string as key. Each time the status changes,
232
a callback function is called with new and old tool key as argument.
233
"""
234
235
def __init__(self, parent, tools=[], callback=None, n_buttoncolumns=3):
236
"""
237
callback is a function that is called when a tool has been selected.
238
The function is called as:
239
callback(tool)
240
241
"""
242
# the metacanvas object with which the pallet should apply th tools
243
244
# callback when a new tool gets selected (NOT in USE)
245
self._callback = callback
246
247
# wx.Window.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER|wx.WANTS_CHARS)
248
# wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,size,wx.RAISED_BORDER|wx.WANTS_CHARS)
249
wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, wx.DefaultSize)
250
# wx.Panel.__init__(self,parent,wx.ID_ANY,wx.DefaultPosition,(300,600),wx.RAISED_BORDER|wx.WANTS_CHARS)
251
self.sizer = wx.GridSizer(0, n_buttoncolumns, 5, 5)
252
self.SetSizer(self.sizer)
253
self._id_to_tool = {}
254
self._id = -1
255
256
for tool in tools:
257
self.add_tool(tool)
258
259
# self.sizer.Fit(self)
260
# self.SetMaxSize((300,-1))
261
262
def has_tool(self, newtool):
263
for tool, b in self._id_to_tool.values():
264
if tool.get_ident() == newtool.get_ident():
265
return True
266
return False
267
268
def get_tool_by_ident(self, ident):
269
# print 'get_tool_by_ident',ident
270
for tool, b in self._id_to_tool.values():
271
# print ' tool',tool.get_ident()
272
if tool.get_ident() == ident:
273
return tool
274
275
return None
276
277
def add_tool(self, tool):
278
"""
279
Add a tool to the pallet.
280
"""
281
if not self.has_tool(tool):
282
# print 'add_tool',tool
283
bottonsize = (32, 32)
284
bottonborder = 10
285
toolbarborder = 1
286
287
b = tool.get_button(self, bottonsize=bottonsize, bottonborder=bottonborder)
288
self.Bind(wx.EVT_BUTTON, self.on_select, b)
289
290
_id = b.GetId()
291
self._id_to_tool[_id] = (tool, b)
292
293
#self.sizer.Add(b, 0, wx.GROW)
294
self.sizer.Add(b, 0, wx.EXPAND, border=toolbarborder)
295
# self.sizer.Add(b)
296
# print ' _id =',_id
297
return _id
298
else:
299
return -1
300
301
def get_tools(self):
302
"""
303
Returns lins with all toll instances
304
"""
305
tools = []
306
for (tool, b) in self._id_to_tool.values():
307
tools.append(tool)
308
return tools
309
310
def refresh(self):
311
"""
312
Reorganizes toolpallet after adding/removing tools.
313
Attention is not automatically called.
314
"""
315
self.sizer.Layout()
316
317
def on_select(self, event):
318
"""
319
Called from a pressed button
320
"""
321
_id = event.GetEventObject().GetId()
322
# print '\n on_select',_id,self._id#,self._id_to_tool[_id]
323
324
if _id != self._id:
325
if self._id_to_tool.has_key(_id):
326
327
(tool, button) = self._id_to_tool[_id]
328
# print ' new tool',tool.get_name()
329
self.unselect()
330
self._id = _id
331
332
# this will cause the main OGL editor to activate the
333
# tool with the current canvas
334
self.GetParent().set_tool(tool)
335
# if self._callback is not None:
336
# self._callback(tool)
337
event.Skip()
338
return tool
339
340
return None
341
342
def select(self, _id):
343
"""
344
Select explicitelt a tool with _id.
345
"""
346
# print '\nselect',_id,self._id,self._id_to_tool
347
348
if _id != self._id:
349
if self._id_to_tool.has_key(_id):
350
351
(tool, button) = self._id_to_tool[_id]
352
353
# print ' explicitly press button'
354
if hasattr(button, 'SetToggle'):
355
button.SetToggle(True)
356
else:
357
button.SetFocus()
358
# print 'button.SetFocus',button.SetFocus.__doc__
359
# pass
360
361
# print ' new tool',tool.get_name()
362
# self.unselect()
363
self._id = _id
364
365
self.GetParent().set_tool(tool)
366
# if self._callback is not None:
367
# self._callback(tool)
368
return tool
369
370
return None
371
372
def unselect(self):
373
"""
374
Unselect currently selected tool.
375
"""
376
if self._id_to_tool.has_key(self._id):
377
(tool, button) = self._id_to_tool[self._id]
378
379
if tool.is_active() == True:
380
# Disactivate current tool
381
tool.deactivate()
382
383
if hasattr(button, 'SetToggle'):
384
button.SetToggle(False)
385
else:
386
# button.SetFocus()
387
# print 'button.SetFocus',button.SetFocus.__doc__
388
pass
389
390
391
class __ToggleMixin:
392
def SetToggle(self, flag):
393
self.up = not flag
394
self.Refresh()
395
SetValue = SetToggle
396
397
def GetToggle(self):
398
return not self.up
399
GetValue = GetToggle
400
401
def OnLeftDown(self, event):
402
if not self.IsEnabled():
403
return
404
self.saveUp = self.up
405
self.up = False # not self.up
406
self.CaptureMouse()
407
self.SetFocus()
408
self.Refresh()
409
410
def OnLeftUp(self, event):
411
if not self.IsEnabled() or not self.HasCapture():
412
return
413
if self.HasCapture():
414
if self.up != self.saveUp:
415
self.Notify()
416
self.ReleaseMouse()
417
self.Refresh()
418
419
def OnKeyDown(self, event):
420
event.Skip()
421
422
423
class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
424
"""A generic toggle bitmap button with text label"""
425
pass
426
427
428
class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
429
"""A generic toggle bitmap button with text label"""
430
pass
431
432
433
class ToolsPanel(wx.Panel):
434
"""
435
Shows a toolpallet with different tools and an options panel.
436
"""
437
438
def __init__(self, parent, size=wx.DefaultSize, size_title=150, **kwargs):
439
440
#size = wx.DefaultSize
441
#size = (300,-1)
442
wx.Panel.__init__(self, parent, wx.NewId(), wx.DefaultPosition, size)
443
# wx.DefaultSize
444
# sizer=wx.BoxSizer(wx.VERTICAL)
445
sizer = wx.StaticBoxSizer(wx.StaticBox(parent, wx.NewId(), "test"), wx.VERTICAL)
446
447
self._toolspalett = ToolPalett(self, **kwargs)
448
449
# self._toolspalett.add_tool(BaseTool(self))
450
451
# create initial option panel
452
self._optionspanel = wx.Window(self)
453
self._optionspanel.SetBackgroundColour("pink")
454
wx.StaticText(self._optionspanel, -1, "Tool Options", (size_title, -1))
455
456
# OK, but toolspane changes size with optionpanel
457
#sizer.Add(self._toolspalett,0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
458
# sizer.Add(self._optionspanel,1,wx.GROW)# wx.EXPAND
459
460
sizer.Add(self._toolspalett, 0, wx.EXPAND)
461
sizer.Add(self._optionspanel, 1, wx.EXPAND)
462
463
# finish panel setup
464
self.SetSizer(sizer)
465
sizer.Fit(self)
466
467
# self.SetSize(parent.GetSize())
468
# self.SetMaxSize((300,-1))
469
470
def get_canvas(self):
471
# ask the OGL editor for the currently active canvas in focus
472
return self.GetParent().get_canvas()
473
474
def get_drawing(self):
475
return self.get_canvas().get_drawing()
476
477
def get_mainframe(self):
478
return self.GetParent().get_mainframe()
479
480
def add_tool(self, tool):
481
return self._toolspalett.add_tool(tool)
482
483
def add_toolclass(self, ToolClass, **kwargs):
484
# init and add
485
return self._toolspalett.add_tool(ToolClass(self, **kwargs))
486
487
def add_initial_tool(self, tool):
488
self._id_initialtool = self.add_tool(tool)
489
490
def reset_initial_tool(self):
491
self.set_tool_with_id(self._id_initialtool)
492
493
def reset_initial_tool(self):
494
self.set_tool_with_id(self._id_initialtool)
495
496
def set_tool_with_id(self, _id):
497
"""
498
Explicitely set a tool from tool pallet using its id.
499
Used to set initial tool.
500
"""
501
# print 'set_tool_with_id',_id
502
return self._toolspalett.select(_id)
503
504
def set_tool(self, tool):
505
"""
506
Called by toolpallet after new tool has been selected.
507
"""
508
# Activate current tool
509
# then tool wil set itself to canvas
510
tool.activate(self.get_canvas())
511
512
# set options of current tool
513
self.refresh_optionspanel(tool)
514
515
def get_tool_by_ident(self, ident):
516
return self._toolspalett.get_tool_by_ident(ident)
517
518
def refresh_optionspanel(self, tool):
519
sizer = self.GetSizer()
520
sizer.Remove(1)
521
self._optionspanel.Destroy()
522
523
self._optionspanel = tool.get_optionspanel(self) # , size = self.GetSize())
524
# self._optionspanel.SetSize((100,0))
525
# if id is not None:
526
# self.objpanel=ObjPanel(self,obj,id=id,func_change_obj=self.change_obj)
527
# else:
528
# self.objpanel=ObjPanel(self,obj,func_change_obj=self.change_obj)
529
530
# ok, but chanes sice of whole palle
531
# sizer.Add(self._optionspanel,1,wx.GROW)
532
533
sizer.Add(self._optionspanel, 1, wx.EXPAND)
534
535
# self.Refresh()
536
# sizer.Fit(self)
537
sizer.Layout()
538
# self.GetParent().Layout()
539
540
def unselect_tool(self):
541
"""
542
Unselect currently selected tool.
543
"""
544
self._toolspalett.unselect()
545
546