1 /* EXTRAITS DE LA LICENCE
2 Copyright CEA, contributeurs : Damien CALISTE, Tristan
3 BERTHELOT, laboratoire L_Sim, (2001-2010)
4
5 Adresse m�l :
6 CALISTE, damien P caliste AT cea P fr.
7 BERTHELOT, tristan P berthelot AT isen P fr.
8
9 Ce logiciel est un programme informatique servant � visualiser des
10 structures atomiques dans un rendu pseudo-3D.
11
12 Ce logiciel est r�gi par la licence CeCILL soumise au droit fran�ais et
13 respectant les principes de diffusion des logiciels libres. Vous pouvez
14 utiliser, modifier et/ou redistribuer ce programme sous les conditions
15 de la licence CeCILL telle que diffus�e par le CEA, le CNRS et l'INRIA
16 sur le site "http://www.cecill.info".
17
18 Le fait que vous puissiez acc�der � cet en-t�te signifie que vous avez
19 pris connaissance de la licence CeCILL, et que vous en avez accept� les
20 termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
21 */
22
23 /* LICENCE SUM UP
24 Copyright CEA, contributors: Damien CALISTE, Tristan
25 BERTHELOT, L_Sim laboratory, (2001-2010)
26
27 E-mail address:
28 CALISTE, damien P caliste AT cea P fr.
29 BERTHELOT, tristan P berthelot AT isen P fr.
30
31 This software is a computer program whose purpose is to visualize atomic
32 configurations in 3D.
33
34 This software is governed by the CeCILL license under French law and
35 abiding by the rules of distribution of free software. You can use,
36 modify and/ or redistribute the software under the terms of the CeCILL
37 license as circulated by CEA, CNRS and INRIA at the following URL
38 "http://www.cecill.info".
39
40 The fact that you are presently reading this means that you have had
41 knowledge of the CeCILL license and that you accept its terms. You can
42 find a copy of this licence shipped with this software at
43 Documentation/licence.en.txt.
44 */
45
46 #include <glib.h>
47 #include <gtk/gtk.h>
48 #include <gdk/gdkkeysyms.h>
49
50 #include <Python.h>
51
52 #include <support.h>
53 #include <visu_basic.h>
54 #include <visu_gtk.h>
55 #include <visu_commandLine.h>
56 #include <coreTools/toolOptions.h>
57 #include <extraGtkFunctions/gtk_toolPanelWidget.h>
58 #include <visu_commandLine.h>
59 #include <visu_configFile.h>
60
61 #define PYTHONGI_DESCRIPTION _("<span size=\"smaller\">" \
62 "This plug-in allows to execute\n" \
63 "Python scripts using GObject\n" \
64 "introspection.</span>")
65 #define PYTHONGI_AUTHORS "Caliste Damien &\nTristan Berthelot"
66
67 #define FLAG_PARAMETER_INIT_SCRIPTS "init_scripts"
68 #define DESC_PARAMETER_INIT_SCRIPTS "Scripts loaded on startup ; paths separated by ':'"
69
70 enum
71 {
72 SCRIPT_LABEL,
73 SCRIPT_PATH,
74 N_SCRIPTS
75 };
76
77 /* Local variables. */
78 static gchar *iconPath;
79 static GtkWidget *panelPython, *pyFileChooser, *pyExecute, *pyTextView;
80 static gboolean isPanelInitialised = FALSE, isPythonInitialised = FALSE;
81 static GtkListStore *initScripts;
82 static GtkTextBuffer *pyTextBuffer;
83 static GtkTextTag *monoTag, *errorTag, *boldTag;
84 static GList *history, *curHist;
85
86 /* Local methods. */
87 static void initialisePython();
88 static void initialisePanel(VisuUiPanel *panel);
89 static gboolean loadScript(gpointer file);
90 static void addInitScript(const gchar *file, gboolean immediate, GtkWindow *parent);
91 static void exportParameters(GString *data, VisuData *dataObj);
92 static gboolean readInitScripts(VisuConfigFileEntry *entry, gchar **lines,
93 int nbLines, int position, GError **error);
94 static void _bufSetText(const gchar *text, GtkTextTag *tag, gboolean error);
95
96 /* Callbacks. */
97 static void onPanelEnter(VisuUiPanel *panel, gpointer data);
98 static void onScriptChosen(GtkFileChooserButton *widget, gpointer user_data);
99 static void onScriptExecute(GtkButton *widget, gpointer user_data);
100 static void onInitScriptAdded(GtkToolButton *bt, gpointer data);
101 static void onInitScriptRemoved(GtkToolButton *bt, gpointer data);
102 static void onInitScriptReload(GtkToolButton *bt, gpointer data);
103 static gboolean onPyIO(GIOChannel *source, GIOCondition condition, gpointer data);
104 static void onOutputCleared(GtkToolButton *bt, gpointer data);
105 static void onOutputCancel(GtkToolButton *bt, gpointer data);
106 static void onOutputGetData(GtkToolButton *bt, gpointer data);
107 #if GTK_MAJOR_VERSION >= 3
108 static void onVScroll(GtkAdjustment *adj, gpointer data);
109 #endif
110 static void onInteractiveEntry(GtkEntry *entry, gpointer data);
111 static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *event, gpointer data);
112
pythongiInit(void)113 gboolean pythongiInit(void)
114 {
115 GHashTable *options;
116 ToolOption *opt;
117 const gchar *file;
118 VisuConfigFileEntry *entry;
119
120 DBG_fprintf(stderr, "Plugin PythonGI: initialisation.\n");
121
122 iconPath = g_build_filename(V_SIM_PIXMAPS_DIR, "pythongi.png", NULL);
123
124 initScripts = gtk_list_store_new(N_SCRIPTS, G_TYPE_STRING, G_TYPE_STRING);
125 pyTextBuffer = gtk_text_buffer_new((GtkTextTagTable*)0);
126 monoTag = gtk_text_buffer_create_tag(pyTextBuffer, "typewriter",
127 "family", "monospace", NULL);
128 errorTag = gtk_text_buffer_create_tag(pyTextBuffer, "error",
129 "foreground", "Tomato", NULL);
130 boldTag = gtk_text_buffer_create_tag(pyTextBuffer, "bold",
131 "weight", PANGO_WEIGHT_BOLD, NULL);
132 history = curHist = (GList*)0;
133
134 isPythonInitialised = FALSE;
135 options = commandLineGet_options();
136 if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScriptInit")))
137 {
138 DBG_fprintf(stderr, "Plugin PythonGI: will run init script.\n");
139 initialisePython();
140 file = g_value_get_string(tool_option_getValue(opt));
141 addInitScript(file, TRUE, (GtkWindow*)0);
142 }
143 if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScript")))
144 {
145 DBG_fprintf(stderr, "Plugin PythonGI: will run py script.\n");
146 initialisePython();
147 file = g_value_get_string(tool_option_getValue(opt));
148 g_idle_add_full(G_PRIORITY_LOW, loadScript, (gpointer)file, NULL);
149 }
150
151 visu_config_file_addKnownTag("python");
152 entry = visu_config_file_addEntry(VISU_CONFIG_FILE_PARAMETER,
153 FLAG_PARAMETER_INIT_SCRIPTS,
154 DESC_PARAMETER_INIT_SCRIPTS,
155 1, readInitScripts);
156 visu_config_file_entry_setVersion(entry, 3.7f);
157 visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
158 exportParameters);
159
160 return TRUE;
161 }
pythongiInitGtk(void)162 gboolean pythongiInitGtk(void)
163 {
164 /* Long description */
165 gchar *cl = _("Python scripting");
166 /* Short description */
167 gchar *tl = _("Python");
168 GHashTable *options;
169 ToolOption *opt;
170 const gchar *file;
171
172 isPanelInitialised = FALSE;
173
174 panelPython = visu_ui_panel_newWithIconFromPath("Panel_python", cl,
175 tl, "stock-pythongi_20.png");
176 visu_ui_panel_setDockable(VISU_UI_PANEL(panelPython), TRUE);
177 visu_ui_panel_attach(VISU_UI_PANEL(panelPython), visu_ui_panel_class_getCommandPanel());
178 g_signal_connect(G_OBJECT(panelPython), "page-entered",
179 G_CALLBACK(onPanelEnter), (gpointer)0);
180
181 options = commandLineGet_options();
182 if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScript")))
183 {
184 initialisePanel(VISU_UI_PANEL(panelPython));
185 file = g_value_get_string(tool_option_getValue(opt));
186 gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(pyFileChooser), file);
187 gtk_widget_set_sensitive(pyExecute, TRUE);
188 }
189
190 return TRUE;
191 }
pythongiGet_description(void)192 const char* pythongiGet_description(void)
193 {
194 return PYTHONGI_DESCRIPTION;
195 }
196
pythongiGet_authors(void)197 const char* pythongiGet_authors(void)
198 {
199 return PYTHONGI_AUTHORS;
200 }
201
pythongiGet_icon(void)202 const char* pythongiGet_icon(void)
203 {
204 return iconPath;
205 }
206
pythongiFree(void)207 void pythongiFree(void)
208 {
209 DBG_fprintf(stderr, "Panel PythonGI: finalise.\n");
210
211 g_free(iconPath);
212 g_object_unref(initScripts);
213
214 if (!isPythonInitialised)
215 return;
216
217 Py_Finalize();
218 }
219
onPanelEnter(VisuUiPanel * panel,gpointer data _U_)220 static void onPanelEnter(VisuUiPanel *panel, gpointer data _U_)
221 {
222 DBG_fprintf(stderr, "Panel PythonGI: caught the 'page-entered' signal %d.\n",
223 isPanelInitialised);
224 if (!isPythonInitialised)
225 initialisePython();
226 if (!isPanelInitialised)
227 initialisePanel(panel);
228 }
initialisePython()229 static void initialisePython()
230 {
231 if (isPythonInitialised)
232 return;
233
234 DBG_fprintf(stderr, "Panel PythonGI: initialise Python.\n");
235 Py_SetProgramName((char*)commandLineGet_programName());
236 Py_Initialize();
237 DBG_fprintf(stderr, "Panel PythonGI: import sys.\n"
238 "import os;\n"
239 "os.putenv(\"GI_TYPELIB_PATH\", \"" VISU_TYPELIBS_DIR "\")\n");
240 PyRun_SimpleString("import os;"
241 "os.putenv(\"GI_TYPELIB_PATH\", \"" VISU_TYPELIBS_DIR "\");"
242 "import sys;"
243 "sys.argv = ['']\n"
244 "from gi.repository import v_sim");
245 DBG_fprintf(stderr, " | done.\n");
246
247 isPythonInitialised = TRUE;
248 }
addFilters(GtkWidget * wd)249 static void addFilters(GtkWidget *wd)
250 {
251 GtkFileFilter *flt;
252
253 flt = gtk_file_filter_new();
254 gtk_file_filter_set_name(flt, _("Python scripts for V_Sim"));
255 gtk_file_filter_add_mime_type(flt, "text/x-script.python");
256 gtk_file_filter_add_mime_type(flt, "text/x-python");
257 gtk_file_filter_add_mime_type(flt, "application/x-python");
258 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wd), flt);
259 flt = gtk_file_filter_new();
260 gtk_file_filter_set_name(flt, _("All"));
261 gtk_file_filter_add_pattern(flt, "*");
262 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wd), flt);
263 }
initialisePanel(VisuUiPanel * panel)264 static void initialisePanel(VisuUiPanel *panel)
265 {
266 int fd[2];
267 gchar *string;
268 GIOChannel *pyIO;
269 GtkWidget *vbox, *hbox, *wd, *scrolled;
270 GtkTreeViewColumn *column;
271 GtkCellRenderer *renderer;
272 GtkToolItem *item;
273 GtkTreeSelection *selection;
274
275 g_return_if_fail(!isPanelInitialised);
276
277 /* Redirect stdout. */
278 if (pipe(fd))
279 g_warning("Cannot create pipes for Python process.");
280 else
281 {
282 string = g_strdup_printf("sys.stdout = os.fdopen(%d, 'w', 0)", fd[1]);
283 DBG_fprintf(stderr, "Panel PythonGI: run '%s'.\n", string);
284 PyRun_SimpleString(string);
285 g_free(string);
286 pyIO = g_io_channel_unix_new(fd[0]);
287 g_io_channel_set_flags(pyIO, G_IO_FLAG_NONBLOCK, NULL);
288 g_io_add_watch(pyIO, G_IO_IN | G_IO_PRI, onPyIO, GINT_TO_POINTER(FALSE));
289 }
290 /* Redirect stderr. */
291 if (pipe(fd))
292 g_warning("Cannot create pipes for Python process.");
293 else
294 {
295 string = g_strdup_printf("sys.stderr = os.fdopen(%d, 'w', 0)", fd[1]);
296 DBG_fprintf(stderr, "Panel PythonGI: run '%s'.\n", string);
297 PyRun_SimpleString(string);
298 g_free(string);
299 pyIO = g_io_channel_unix_new(fd[0]);
300 g_io_channel_set_flags(pyIO, G_IO_FLAG_NONBLOCK, NULL);
301 g_io_add_watch(pyIO, G_IO_IN | G_IO_PRI, onPyIO, GINT_TO_POINTER(TRUE));
302 }
303
304 vbox = gtk_vbox_new(FALSE, 0);
305 gtk_container_set_border_width(GTK_CONTAINER(panel), 5);
306 gtk_container_add(GTK_CONTAINER(panel), vbox);
307
308 wd = gtk_label_new(_("<b>Scripts loaded on startup</b>"));
309 gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
310 gtk_label_set_xalign(GTK_LABEL(wd), 0.);
311 gtk_widget_set_name(wd, "label_head");
312 gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);
313
314 hbox = gtk_hbox_new(FALSE, 0);
315 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
316
317 scrolled = gtk_scrolled_window_new(NULL, NULL);
318 gtk_widget_set_size_request(scrolled, -1, 100);
319 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
320 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
321 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
322 GTK_SHADOW_ETCHED_IN);
323 gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
324
325 wd = gtk_tree_view_new_with_model(GTK_TREE_MODEL(initScripts));
326 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(wd), FALSE);
327 gtk_widget_set_tooltip_text(wd,
328 _("This list is saved with the parameter file."));
329 gtk_container_add(GTK_CONTAINER(scrolled), wd);
330
331 renderer = gtk_cell_renderer_text_new();
332 column = gtk_tree_view_column_new_with_attributes("", renderer,
333 "text", SCRIPT_LABEL,
334 NULL);
335 gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
336 renderer = gtk_cell_renderer_text_new();
337 g_object_set(G_OBJECT(renderer), "scale", 0.75, NULL);
338 column = gtk_tree_view_column_new_with_attributes("", renderer,
339 "text", SCRIPT_PATH,
340 NULL);
341 gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
342 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(wd));
343
344 wd = gtk_toolbar_new();
345 gtk_orientable_set_orientation(GTK_ORIENTABLE(wd), GTK_ORIENTATION_VERTICAL);
346 gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
347 gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);
348 gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
349
350 item = gtk_tool_button_new(NULL, NULL);
351 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-add");
352 g_signal_connect(G_OBJECT(item), "clicked",
353 G_CALLBACK(onInitScriptAdded), (gpointer)0);
354 gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
355 item = gtk_tool_button_new(NULL, NULL);
356 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-remove");
357 g_signal_connect(G_OBJECT(item), "clicked",
358 G_CALLBACK(onInitScriptRemoved), (gpointer)selection);
359 gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
360 item = gtk_tool_button_new(NULL, NULL);
361 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "view-refresh");
362 g_signal_connect(G_OBJECT(item), "clicked",
363 G_CALLBACK(onInitScriptReload), (gpointer)selection);
364 gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
365
366 wd = gtk_label_new(_("<b>Interactive scripting</b>"));
367 gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
368 gtk_label_set_xalign(GTK_LABEL(wd), 0.);
369 gtk_widget_set_name(wd, "label_head");
370 gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);
371
372 hbox = gtk_hbox_new(FALSE, 3);
373 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
374 wd = gtk_label_new(_("Load:"));
375 gtk_widget_set_margin_start(wd, 5);
376 gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
377 pyFileChooser = gtk_file_chooser_button_new(_("Choose a Python script"),
378 GTK_FILE_CHOOSER_ACTION_OPEN);
379 addFilters(pyFileChooser);
380 gtk_box_pack_start(GTK_BOX(hbox), pyFileChooser, TRUE, TRUE, 0);
381 pyExecute = gtk_button_new_from_icon_name("system-run", GTK_ICON_SIZE_SMALL_TOOLBAR);
382 gtk_widget_set_margin_end(pyExecute, 5);
383 gtk_widget_set_sensitive(pyExecute, FALSE);
384 gtk_box_pack_start(GTK_BOX(hbox), pyExecute, FALSE, FALSE, 0);
385
386 g_signal_connect(G_OBJECT(pyFileChooser), "file-set",
387 G_CALLBACK(onScriptChosen), (gpointer)pyExecute);
388 g_signal_connect(G_OBJECT(pyExecute), "clicked",
389 G_CALLBACK(onScriptExecute), (gpointer)pyFileChooser);
390
391 hbox = gtk_hbox_new(FALSE, 0);
392 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 3);
393
394 scrolled = gtk_scrolled_window_new(NULL, NULL);
395 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
396 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
397 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
398 GTK_SHADOW_ETCHED_IN);
399 gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
400
401 pyTextView = gtk_text_view_new_with_buffer(pyTextBuffer);
402 g_object_unref(pyTextBuffer);
403 gtk_text_view_set_editable(GTK_TEXT_VIEW(pyTextView), FALSE);
404 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(pyTextView), FALSE);
405 gtk_container_add(GTK_CONTAINER(scrolled), pyTextView);
406 #if GTK_MAJOR_VERSION >= 3
407 g_signal_connect(G_OBJECT(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(pyTextView))),
408 "changed", G_CALLBACK(onVScroll), (gpointer)0);
409 #endif
410
411 wd = gtk_toolbar_new();
412 gtk_orientable_set_orientation(GTK_ORIENTABLE(wd), GTK_ORIENTATION_VERTICAL);
413 gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
414 gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);
415 gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
416
417 item = gtk_tool_button_new(NULL, NULL);
418 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-clear");
419 g_signal_connect(G_OBJECT(item), "clicked",
420 G_CALLBACK(onOutputCleared), (gpointer)0);
421 gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
422 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Clear Python output."));
423 item = gtk_tool_button_new(NULL, NULL);
424 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "process-stop");
425 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
426 g_signal_connect(G_OBJECT(item), "clicked",
427 G_CALLBACK(onOutputCancel), (gpointer)0);
428 gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
429 item = gtk_tool_button_new(NULL, NULL);
430 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-find");
431 gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
432 gtk_widget_set_tooltip_text(GTK_WIDGET(item),
433 _("Get the current VisuData object as 'data'"
434 " variable and the current VisuGlView as 'view'."));
435
436 wd = gtk_entry_new();
437 #if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION > 1)
438 gtk_entry_set_placeholder_text(GTK_ENTRY(wd), _("Python interactive command line"));
439 #endif
440 g_signal_connect(G_OBJECT(wd), "activate",
441 G_CALLBACK(onInteractiveEntry), (gpointer)0);
442 g_signal_connect(G_OBJECT(wd), "key-press-event",
443 G_CALLBACK(onKeyPressed), (gpointer)0);
444 g_signal_connect(G_OBJECT(item), "clicked",
445 G_CALLBACK(onOutputGetData), (gpointer)wd);
446 gtk_box_pack_end(GTK_BOX(vbox), wd, FALSE, FALSE, 0);
447
448 gtk_widget_show_all(vbox);
449
450 isPanelInitialised = TRUE;
451 }
452
onScriptChosen(GtkFileChooserButton * widget,gpointer user_data)453 static void onScriptChosen(GtkFileChooserButton *widget, gpointer user_data)
454 {
455 gchar *file;
456
457 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
458 gtk_widget_set_sensitive(GTK_WIDGET(user_data), (file != (gchar*)0));
459 if (file)
460 g_free(file);
461 }
onScriptExecute(GtkButton * widget _U_,gpointer user_data)462 static void onScriptExecute(GtkButton *widget _U_, gpointer user_data)
463 {
464 gchar *file;
465
466 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(user_data));
467 g_return_if_fail(file);
468
469 g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, loadScript, (gpointer)file, g_free);
470 }
471
472
loadScript(gpointer file)473 static gboolean loadScript(gpointer file)
474 {
475 gchar *script, *label, *name;
476 GError *error;
477
478 DBG_fprintf(stderr, "Panel PythonGI: load script '%s'.\n", (gchar*)file);
479
480 g_return_val_if_fail(isPythonInitialised, FALSE);
481
482 error = (GError*)0;
483 if (!g_file_get_contents((gchar*)file, &script, (gsize*)0, &error))
484 {
485 if (error)
486 {
487 visu_ui_raiseWarning(_("Load a Python script"), error->message,
488 visu_ui_panel_getContainerWindow
489 (VISU_UI_PANEL(panelPython)));
490 g_error_free(error);
491 }
492 return FALSE;
493 }
494
495 name = g_path_get_basename((const gchar*)file);
496 label = g_strdup_printf(_("Load script \"%s\"\n"), name);
497 g_free(name);
498 _bufSetText(label, boldTag, FALSE);
499 g_free(label);
500
501 PyRun_SimpleString(script);
502 g_free(script);
503
504 return FALSE;
505 }
506
addInitScript(const gchar * file,gboolean immediate,GtkWindow * parent)507 static void addInitScript(const gchar *file, gboolean immediate, GtkWindow *parent)
508 {
509 GtkTreeIter iter;
510 gchar *path, *name;
511
512 if (!g_file_test(file, G_FILE_TEST_IS_REGULAR))
513 {
514 if (parent)
515 visu_ui_raiseWarning(_("Choose a Python script"),
516 _("Not a regular file."), parent);
517 return;
518 }
519
520 path = tool_path_normalize(file);
521 name = g_path_get_basename(file);
522 gtk_list_store_append(initScripts, &iter);
523 gtk_list_store_set(initScripts, &iter, SCRIPT_PATH, path, SCRIPT_LABEL, name, -1);
524 g_free(name);
525
526 if (immediate)
527 {
528 loadScript((gpointer)path);
529 g_free(path);
530 }
531 else
532 g_idle_add_full(G_PRIORITY_LOW, loadScript, (gpointer)path, g_free);
533 }
onInitScriptAdded(GtkToolButton * bt _U_,gpointer data _U_)534 static void onInitScriptAdded(GtkToolButton *bt _U_, gpointer data _U_)
535 {
536 GtkWidget *dialog;
537 gchar *filename;
538 GtkWindow *parent;
539
540 parent = visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelPython));
541 dialog = gtk_file_chooser_dialog_new(_("Choose a Python script"), parent,
542 GTK_FILE_CHOOSER_ACTION_OPEN,
543 TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
544 TOOL_ICON_OPEN, GTK_RESPONSE_ACCEPT,
545 NULL);
546 addFilters(dialog);
547
548 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
549 {
550 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
551 addInitScript(filename, FALSE, parent);
552 g_free(filename);
553 }
554
555 gtk_widget_destroy(dialog);
556 }
onInitScriptRemoved(GtkToolButton * bt _U_,gpointer data)557 static void onInitScriptRemoved(GtkToolButton *bt _U_, gpointer data)
558 {
559 gboolean valid;
560 GtkTreeModel *model;
561 GtkTreeIter iter;
562
563 valid = gtk_tree_selection_get_selected(GTK_TREE_SELECTION(data), &model, &iter);
564 if (!valid)
565 return;
566
567 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
568 }
onInitScriptReload(GtkToolButton * bt _U_,gpointer data)569 static void onInitScriptReload(GtkToolButton *bt _U_, gpointer data)
570 {
571 gboolean valid;
572 GtkTreeModel *model;
573 GtkTreeIter iter;
574 gchar *path;
575
576 valid = gtk_tree_selection_get_selected(GTK_TREE_SELECTION(data), &model, &iter);
577 if (!valid)
578 return;
579
580 gtk_tree_model_get(model, &iter, SCRIPT_PATH, &path, -1);
581 loadScript((gpointer)path);
582 g_free(path);
583 }
onPyIO(GIOChannel * source,GIOCondition condition _U_,gpointer data)584 static gboolean onPyIO(GIOChannel *source,
585 GIOCondition condition _U_, gpointer data)
586 {
587 #define size 256
588 gchar string[size];
589 gsize lg;
590 GError *error;
591
592 error = (GError*)0;
593 /* DBG_fprintf(stderr, "Panel PythonGI: receive buffer data.\n"); */
594 if (g_io_channel_read_chars(source, string, size, &lg, &error) != G_IO_STATUS_NORMAL)
595 {
596 g_warning("%s", error->message);
597 g_error_free(error);
598 return TRUE;
599 }
600 string[lg] = '\0';
601 /* DBG_fprintf(stderr, "Panel PythonGI: got\n%s\n", string); */
602
603 _bufSetText(string, monoTag, GPOINTER_TO_INT(data));
604
605 /* Flush Gtk events. */
606 visu_ui_wait();
607
608 return TRUE;
609 }
_bufSetText(const gchar * text,GtkTextTag * tag,gboolean error)610 static void _bufSetText(const gchar *text, GtkTextTag *tag, gboolean error)
611 {
612 GtkTextIter iter;
613
614 gtk_text_buffer_get_end_iter(pyTextBuffer, &iter);
615 if (!error)
616 gtk_text_buffer_insert_with_tags(pyTextBuffer, &iter, text, -1, tag, NULL);
617 else
618 gtk_text_buffer_insert_with_tags(pyTextBuffer, &iter, text, -1, tag, errorTag, NULL);
619 }
onOutputCleared(GtkToolButton * bt _U_,gpointer data _U_)620 static void onOutputCleared(GtkToolButton *bt _U_, gpointer data _U_)
621 {
622 GtkTextIter start, end;
623
624 gtk_text_buffer_get_bounds(pyTextBuffer, &start, &end);
625 gtk_text_buffer_delete(pyTextBuffer, &start, &end);
626 }
onOutputCancel(GtkToolButton * bt _U_,gpointer data _U_)627 static void onOutputCancel(GtkToolButton *bt _U_, gpointer data _U_)
628 {
629 DBG_fprintf(stderr, "Panel PythonGI: try to interrupt.\n");
630 PyErr_SetString(PyExc_TypeError, "script interrupted.");
631 }
onOutputGetData(GtkToolButton * bt _U_,gpointer data)632 static void onOutputGetData(GtkToolButton *bt _U_, gpointer data)
633 {
634 DBG_fprintf(stderr, "Panel PythonGI: get current data.\n");
635 gtk_entry_set_text(GTK_ENTRY(data), "scene = v_sim.UiMainClass.getDefaultRendering().getGlScene()");
636 g_signal_emit_by_name(G_OBJECT(data), "activate");
637 gtk_entry_set_text(GTK_ENTRY(data), "data = scene.getData()");
638 g_signal_emit_by_name(G_OBJECT(data), "activate");
639 gtk_entry_set_text(GTK_ENTRY(data), "view = scene.getGlView()");
640 g_signal_emit_by_name(G_OBJECT(data), "activate");
641 gtk_entry_set_text(GTK_ENTRY(data), "selection = v_sim.ui_interactive_pick_getSelection()");
642 g_signal_emit_by_name(G_OBJECT(data), "activate");
643 }
644 #if GTK_MAJOR_VERSION >= 3
onVScroll(GtkAdjustment * adj,gpointer data _U_)645 static void onVScroll(GtkAdjustment *adj, gpointer data _U_)
646 {
647 guint oldUpper;
648
649 oldUpper = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(adj), "old-upper"));
650 if (gtk_adjustment_get_value(adj) + gtk_adjustment_get_page_size(adj) == oldUpper)
651 /* Scroll to the end. */
652 gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj) -
653 gtk_adjustment_get_page_size(adj));
654 g_object_set_data(G_OBJECT(adj), "old-upper",
655 GINT_TO_POINTER(gtk_adjustment_get_upper(adj)));
656 }
657 #endif
onInteractiveEntry(GtkEntry * entry,gpointer data _U_)658 static void onInteractiveEntry(GtkEntry *entry, gpointer data _U_)
659 {
660 _bufSetText("> ", NULL, FALSE);
661 _bufSetText(gtk_entry_get_text(entry), monoTag, FALSE);
662 _bufSetText("\n", NULL, FALSE);
663 PyRun_SimpleString(gtk_entry_get_text(entry));
664 history = g_list_prepend(history, g_strdup(gtk_entry_get_text(entry)));
665 curHist = (GList*)0;
666 gtk_entry_set_text(entry, "");
667 }
onKeyPressed(GtkWidget * widget,GdkEventKey * event,gpointer data _U_)668 static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *event, gpointer data _U_)
669 {
670 if (event->keyval != GDK_KEY_Up &&
671 event->keyval != GDK_KEY_Down)
672 return FALSE;
673
674 if (event->keyval == GDK_KEY_Up)
675 curHist = (curHist)?((curHist->next)?curHist->next:curHist):history;
676 else if (event->keyval == GDK_KEY_Down && curHist)
677 curHist = curHist->prev;
678
679 gtk_entry_set_text(GTK_ENTRY(widget), (curHist)?(const gchar*)curHist->data:"");
680 gtk_editable_set_position(GTK_EDITABLE(widget), -1);
681
682 return TRUE;
683 }
684
685 /**************/
686 /* Parameters */
687 /**************/
readInitScripts(VisuConfigFileEntry * entry _U_,gchar ** lines,int nbLines,int position _U_,GError ** error _U_)688 static gboolean readInitScripts(VisuConfigFileEntry *entry _U_, gchar **lines,
689 int nbLines, int position _U_, GError **error _U_)
690 {
691 gchar **paths;
692 guint i;
693
694 g_return_val_if_fail(nbLines == 1, FALSE);
695
696 DBG_fprintf(stderr, "Plugin PythonGI: initialisation scripts.\n");
697
698 initialisePython();
699
700 paths = g_strsplit_set(lines[0], ":\n", 100);
701 for (i = 0; paths[i]; i++)
702 {
703 g_strstrip(paths[i]);
704 DBG_fprintf(stderr, "Panel PythonGI: test file '%s'.\n", paths[i]);
705 addInitScript(paths[i], TRUE, (GtkWindow*)0);
706 }
707 g_strfreev(paths);
708
709 return TRUE;
710 }
exportParameters(GString * data,VisuData * dataObj _U_)711 static void exportParameters(GString *data, VisuData *dataObj _U_)
712 {
713 gboolean valid;
714 GtkTreeIter iter;
715 gchar *file;
716
717 g_string_append_printf(data, "# %s\n", DESC_PARAMETER_INIT_SCRIPTS);
718 g_string_append_printf(data, "%s[python]: ", FLAG_PARAMETER_INIT_SCRIPTS);
719 for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(initScripts), &iter);
720 valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(initScripts), &iter))
721 {
722 gtk_tree_model_get(GTK_TREE_MODEL(initScripts), &iter, SCRIPT_PATH, &file, -1);
723 g_string_append_printf(data, "%s:", file);
724 g_free(file);
725 }
726 g_string_append_printf(data, "\n\n");
727 }
728