Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/dialogs/tools/GNEPythonToolDialog.cpp
169684 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file GNEPythonToolDialog.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Jun 2022
17
///
18
// Dialog for tools
19
/****************************************************************************/
20
21
#include <netedit/GNEApplicationWindow.h>
22
#include <netedit/GNEViewNet.h>
23
#include <netedit/GNEViewParent.h>
24
#include <netedit/tools/GNEPythonTool.h>
25
#include <utils/foxtools/MFXLabelTooltip.h>
26
#include <utils/foxtools/MFXStaticToolTip.h>
27
#include <utils/gui/div/GUIDesigns.h>
28
29
#include "GNEPythonToolDialog.h"
30
31
// ===========================================================================
32
// Defines
33
// ===========================================================================
34
35
#define MARGIN 4
36
#define MAXNUMCOLUMNS 4
37
#define NUMROWSBYCOLUMN 20
38
39
// ===========================================================================
40
// FOX callback mapping
41
// ===========================================================================
42
43
FXDEFMAP(GNEPythonToolDialog) GNEPythonToolDialogMap[] = {
44
FXMAPFUNC(SEL_COMMAND, MID_SHOWTOOLTIPS_MENU, GNEPythonToolDialog::onCmdShowToolTipsMenu),
45
FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_SAVE, GNEPythonToolDialog::onCmdSave),
46
FXMAPFUNC(SEL_UPDATE, MID_CHOOSEN_SAVE, GNEPythonToolDialog::onUpdRequiredAttributes),
47
FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_LOAD, GNEPythonToolDialog::onCmdLoad),
48
FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE, GNEPythonToolDialog::onCmdSetVisualization),
49
FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_RUN, GNEPythonToolDialog::onCmdRun),
50
FXMAPFUNC(SEL_UPDATE, MID_GNE_BUTTON_RUN, GNEPythonToolDialog::onUpdRequiredAttributes),
51
FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_RESET, GNEPythonToolDialog::onCmdReset)
52
};
53
54
// Object implementation
55
FXIMPLEMENT(GNEPythonToolDialog, GNEDialog, GNEPythonToolDialogMap, ARRAYNUMBER(GNEPythonToolDialogMap))
56
57
// ============================================-===============================
58
// member method definitions
59
// ===========================================================================
60
61
GNEPythonToolDialog::GNEPythonToolDialog(GNEApplicationWindow* applicationWindow, GNEPythonTool* tool) :
62
GNEDialog(applicationWindow, TL("Tool"), GUIIcon::TOOL_PYTHON, DialogType::PYTHON,
63
GNEDialog::Buttons::RUN_CANCEL_RESET, OpenType::MODAL, ResizeMode::RESIZABLE) {
64
// create options
65
auto horizontalOptionsFrame = new FXHorizontalFrame(myContentFrame, GUIDesignHorizontalFrameNoPadding);
66
// build options
67
myShowToolTipsMenu = new MFXCheckableButton(false, horizontalOptionsFrame,
68
applicationWindow->getStaticTooltipMenu(), (std::string("\t") + TL("Toggle Menu Tooltips") + std::string("\t") + TL("Toggles whether tooltips in the menu shall be shown.")).c_str(),
69
GUIIconSubSys::getIcon(GUIIcon::SHOWTOOLTIPS_MENU), this, MID_SHOWTOOLTIPS_MENU, GUIDesignMFXCheckableButtonSquare);
70
auto saveFile = new MFXButtonTooltip(horizontalOptionsFrame, applicationWindow->getStaticTooltipMenu(), TL("Save toolcfg"),
71
GUIIconSubSys::getIcon(GUIIcon::SAVE), this, MID_CHOOSEN_SAVE, GUIDesignButtonConfiguration);
72
saveFile->setTipText(TL("Save file with tool configuration"));
73
auto loadFile = new MFXButtonTooltip(horizontalOptionsFrame, applicationWindow->getStaticTooltipMenu(), TL("Load toolcfg"),
74
GUIIconSubSys::getIcon(GUIIcon::OPEN), this, MID_CHOOSEN_LOAD, GUIDesignButtonConfiguration);
75
loadFile->setTipText(TL("Load file with tool configuration"));
76
mySortedCheckButton = new FXCheckButton(horizontalOptionsFrame, TL("Sorted by name"), this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButton);
77
myGroupedCheckButton = new FXCheckButton(horizontalOptionsFrame, TL("Grouped by categories"), this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButton);
78
// add separators
79
new FXSeparator(myContentFrame);
80
// Create scroll frame for content rows
81
auto contentScrollWindow = new FXScrollWindow(myContentFrame, GUIDesignScrollWindow);
82
auto horizontalRowFrames = new FXHorizontalFrame(contentScrollWindow, LAYOUT_FILL_X | LAYOUT_FILL_Y | PACK_UNIFORM_WIDTH);
83
myArgumentFrameLeft = new FXVerticalFrame(horizontalRowFrames, GUIDesignAuxiliarFrame);
84
myArgumentFrameRight = new FXVerticalFrame(horizontalRowFrames, GUIDesignAuxiliarFrame);
85
// set tool
86
myPythonTool = tool;
87
// set title
88
setTitle(myPythonTool->getToolName().c_str());
89
// reset checkboxes
90
mySortedCheckButton->setCheck(FALSE);
91
myGroupedCheckButton->setCheck(TRUE);
92
// set myShowToolTipsMenu
93
myShowToolTipsMenu->setChecked(getApp()->reg().readIntEntry("gui", "menuToolTips", 0) != 1);
94
// set current values in options (like current folders and similar)
95
myPythonTool->setCurrentValues();
96
// build arguments
97
buildArguments(false, true);
98
// get maximum height
99
const int maximumHeight = myArgumentFrameLeft->numChildren() * GUIDesignHeight + 120;
100
// resize
101
resize(1024, maximumHeight <= 768 ? maximumHeight : 768);
102
// open dialog
103
openDialog();
104
}
105
106
107
GNEPythonToolDialog::~GNEPythonToolDialog() {}
108
109
110
void
111
GNEPythonToolDialog::runInternalTest(const InternalTestStep::DialogArgument* /*dialogArgument*/) {
112
// nothing to do
113
}
114
115
116
const GNEPythonTool*
117
GNEPythonToolDialog::getPythonTool() const {
118
return myPythonTool;
119
}
120
121
122
long
123
GNEPythonToolDialog::onCmdShowToolTipsMenu(FXObject*, FXSelector, void*) {
124
// toggle check
125
myShowToolTipsMenu->setChecked(!myShowToolTipsMenu->amChecked());
126
if (myApplicationWindow->getViewNet()) {
127
myApplicationWindow->getViewNet()->getViewParent()->getShowToolTipsMenu()->setChecked(myShowToolTipsMenu->amChecked());
128
myApplicationWindow->getViewNet()->getViewParent()->getShowToolTipsMenu()->update();
129
}
130
// enable/disable static tooltip
131
myApplicationWindow->getStaticTooltipMenu()->enableStaticToolTip(myShowToolTipsMenu->amChecked());
132
// save in registry
133
getApp()->reg().writeIntEntry("gui", "menuToolTips", myShowToolTipsMenu->amChecked() ? 0 : 1);
134
update();
135
136
return 1;
137
}
138
139
140
long
141
GNEPythonToolDialog::onCmdSave(FXObject*, FXSelector, void*) {
142
// open save dialog
143
const auto optionsFileDialog = GNEFileDialog(myApplicationWindow, TL("options file"),
144
SUMOXMLDefinitions::XMLFileExtensions.getStrings(),
145
GNEFileDialog::OpenMode::SAVE,
146
GNEFileDialog::ConfigType::NETEDIT);
147
// check file
148
if (optionsFileDialog.getResult() == GNEDialog::Result::ACCEPT) {
149
myPythonTool->saveConfiguration(optionsFileDialog.getFilename());
150
}
151
return 1;
152
}
153
154
155
long
156
GNEPythonToolDialog::onCmdLoad(FXObject*, FXSelector, void*) {
157
// open file dialog
158
const auto optionsFileDialog = GNEFileDialog(myApplicationWindow, TL("options file"),
159
SUMOXMLDefinitions::XMLFileExtensions.getStrings(),
160
GNEFileDialog::OpenMode::LOAD_SINGLE,
161
GNEFileDialog::ConfigType::NETEDIT);
162
// check file
163
if ((optionsFileDialog.getResult() == GNEDialog::Result::ACCEPT) && myPythonTool->loadConfiguration(optionsFileDialog.getFilename())) {
164
// rebuild arguments
165
buildArguments((mySortedCheckButton->getCheck() == TRUE), (myGroupedCheckButton->getCheck() == TRUE));
166
}
167
return 1;
168
}
169
170
171
long
172
GNEPythonToolDialog::onCmdSetVisualization(FXObject*, FXSelector, void*) {
173
// rebuild arguments
174
buildArguments((mySortedCheckButton->getCheck() == TRUE), (myGroupedCheckButton->getCheck() == TRUE));
175
return 1;
176
}
177
178
179
long
180
GNEPythonToolDialog::onCmdRun(FXObject*, FXSelector, void*) {
181
// hide dialog
182
hide();
183
// run tool
184
return myApplicationWindow->tryHandle(myPythonTool->getMenuCommand(), FXSEL(SEL_COMMAND, MID_GNE_RUNPYTHONTOOL), nullptr);
185
}
186
187
188
long
189
GNEPythonToolDialog::onCmdReset(FXObject*, FXSelector, void*) {
190
// iterate over all arguments and reset values
191
for (const auto& argument : myArguments) {
192
argument->reset();
193
}
194
return 1;
195
}
196
197
198
long
199
GNEPythonToolDialog::onUpdRequiredAttributes(FXObject* sender, FXSelector, void*) {
200
// iterate over all arguments and check if required attribute is set
201
for (const auto& argument : myArguments) {
202
if (argument->requiredAttributeSet() == false) {
203
return sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), nullptr);
204
}
205
}
206
return sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), nullptr);
207
}
208
209
210
GNEPythonToolDialog::CategoryOptions::CategoryOptions(const std::string& category) :
211
std::string(category) {
212
}
213
214
215
void
216
GNEPythonToolDialog::CategoryOptions::addOption(const std::string& name, Option* option) {
217
myOptions.push_back(std::make_pair(name, option));
218
}
219
220
221
const std::vector<std::pair<std::string, Option*> >&
222
GNEPythonToolDialog::CategoryOptions::getOptions() const {
223
return myOptions;
224
}
225
226
227
void
228
GNEPythonToolDialog::CategoryOptions::sortByName() {
229
// just sort vector with options
230
std::sort(myOptions.begin(), myOptions.end());
231
}
232
233
234
void
235
GNEPythonToolDialog::buildArguments(bool sortByName, bool groupedByCategories) {
236
// clear arguments and categories
237
for (const auto& argument : myArguments) {
238
delete argument;
239
}
240
for (const auto& category : myCategories) {
241
delete category;
242
}
243
myArguments.clear();
244
myCategories.clear();
245
// get argument sorted by name and grouped by categories
246
auto categoryOptions = groupedByCategories ? getOptionsByCategories(myPythonTool->getToolsOptions()) : getOptions(myPythonTool->getToolsOptions());
247
// calculate number of arguments
248
int numArguments = 0;
249
for (auto& categoryOption : categoryOptions) {
250
numArguments += (int)categoryOption.getOptions().size() + 1;
251
}
252
const int halfNumArguments = numArguments / 2;
253
// declare counter for number of inserted arguments
254
int numInsertedArguments = 0;
255
// iterate over category options
256
for (auto& categoryOption : categoryOptions) {
257
// get argument frame
258
auto argumentFrame = (numInsertedArguments < halfNumArguments) ? myArgumentFrameLeft : myArgumentFrameRight;
259
// add category
260
if (categoryOption.size() > 0) {
261
myCategories.push_back(new GNEPythonToolDialogElements::Category(argumentFrame, categoryOption));
262
numInsertedArguments++;
263
}
264
// check if sort by name
265
if (sortByName) {
266
categoryOption.sortByName();
267
}
268
// add options
269
for (const auto& option : categoryOption.getOptions()) {
270
// get argument frame (again)
271
argumentFrame = (numInsertedArguments < halfNumArguments) ? myArgumentFrameLeft : myArgumentFrameRight;
272
// continue depending of type
273
if (option.second->isInteger()) {
274
myArguments.push_back(new GNEPythonToolDialogElements::IntArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
275
} else if (option.second->isFloat()) {
276
myArguments.push_back(new GNEPythonToolDialogElements::FloatArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
277
} else if (option.second->isBool()) {
278
myArguments.push_back(new GNEPythonToolDialogElements::BoolArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
279
} else if (option.second->isFileName()) {
280
myArguments.push_back(new GNEPythonToolDialogElements::FileNameArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
281
} else if (option.second->isNetwork()) {
282
myArguments.push_back(new GNEPythonToolDialogElements::NetworkArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
283
} else if (option.second->isAdditional()) {
284
myArguments.push_back(new GNEPythonToolDialogElements::AdditionalArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
285
} else if (option.second->isRoute()) {
286
myArguments.push_back(new GNEPythonToolDialogElements::RouteArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
287
} else if (option.second->isData()) {
288
myArguments.push_back(new GNEPythonToolDialogElements::DataArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
289
} else if (option.second->isSumoConfig()) {
290
myArguments.push_back(new GNEPythonToolDialogElements::SumoConfigArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
291
} else if (option.second->isEdge()) {
292
myArguments.push_back(new GNEPythonToolDialogElements::EdgeArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
293
} else if (option.second->isEdgeVector()) {
294
myArguments.push_back(new GNEPythonToolDialogElements::EdgeVectorArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
295
} else {
296
myArguments.push_back(new GNEPythonToolDialogElements::StringArgument(this, myPythonTool, getApplicationWindow(), argumentFrame, option.first, option.second));
297
}
298
numInsertedArguments++;
299
}
300
}
301
// adjust parameter column (call always after create elements)
302
adjustParameterColumn();
303
}
304
305
306
void
307
GNEPythonToolDialog::adjustParameterColumn() {
308
int maximumWidth = 0;
309
// iterate over all arguments and find the maximum width
310
for (const auto& argument : myArguments) {
311
const auto label = argument->getParameterLabel();
312
const int columnWidth = label->getFont()->getTextWidth(label->getText().text(), label->getText().length() + MARGIN);
313
if (columnWidth > maximumWidth) {
314
maximumWidth = columnWidth;
315
}
316
}
317
// set maximum width for all parameter labels
318
for (const auto& argument : myArguments) {
319
argument->getParameterLabel()->setWidth(maximumWidth);
320
}
321
}
322
323
324
std::vector<GNEPythonToolDialog::CategoryOptions>
325
GNEPythonToolDialog::getOptions(OptionsCont& optionsCont) const {
326
// use a vector with only one empty category to reuse code of buildArguments
327
std::vector<GNEPythonToolDialog::CategoryOptions> result = {GNEPythonToolDialog::CategoryOptions("")};
328
// add all options to result
329
for (const auto& option : optionsCont) {
330
result.front().addOption(option.first, option.second);
331
}
332
return result;
333
}
334
335
336
std::vector<GNEPythonToolDialog::CategoryOptions>
337
GNEPythonToolDialog::getOptionsByCategories(OptionsCont& optionsCont) const {
338
// declare vector with common categories
339
const std::vector<std::string> commonCategories = {"input", "output", "processing", "time"};
340
// fill categories
341
std::vector<std::string> categories = commonCategories;
342
for (const auto& option : optionsCont) {
343
if (std::find(categories.begin(), categories.end(), option.second->getSubTopic()) == categories.end()) {
344
categories.push_back(option.second->getSubTopic());
345
}
346
}
347
// declare vector of category options and fill
348
std::vector<GNEPythonToolDialog::CategoryOptions> result;
349
for (const auto& category : categories) {
350
result.push_back(GNEPythonToolDialog::CategoryOptions(category));
351
}
352
// fill result with options
353
for (const auto& option : optionsCont) {
354
auto category = std::find(result.begin(), result.end(), option.second->getSubTopic());
355
// add option in category
356
category->addOption(option.first, option.second);
357
}
358
// drop empty categories
359
auto it = result.begin();
360
while (it != result.end()) {
361
if (it->getOptions().empty()) {
362
it = result.erase(it);
363
} else {
364
it++;
365
}
366
}
367
return result;
368
}
369
370
371
int
372
GNEPythonToolDialog::getNumRowColums() const {
373
const int column = (int)myArguments.size() / NUMROWSBYCOLUMN;
374
return (column < MAXNUMCOLUMNS) ? column : (MAXNUMCOLUMNS - 1);
375
}
376
377
378
FXVerticalFrame*
379
GNEPythonToolDialog::getArgumentFrameLeft() const {
380
return myArgumentFrameLeft;
381
}
382
383
384
FXVerticalFrame*
385
GNEPythonToolDialog::getArgumentFrameRight() const {
386
return myArgumentFrameRight;
387
}
388
389
/****************************************************************************/
390
391