forked from Stellarium/stellarium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ch_scripting.tex
425 lines (365 loc) · 21.6 KB
/
ch_scripting.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
\chapter{Scripting}
\label{ch:scripting}
\sectionauthor*{A. Wolf, G. Zotti, with additions by Wolfgang Laun}
\section{Introduction}
\label{sec:scripting:introduction}
The scripting facility is Stellarium's version of a
\emph{Presentation}, a feature that may be used to run an astronomical
or other show for instruction or entertainment from within the
Stellarium program. The use of scripts was recognized as a perfect way
of arranging a presentation of a sequence of astronomical events
from the earliest versions of Stellarium.
The original \emph{Stratoscript} was quite limited in what it could do,
and so a new Stellarium Scripting System has been developed.
Since version 0.10.1, Stellarium has included a scripting feature based on
the Qt5 Scripting
Engine\footnote{\url{https://doc.qt.io/qt-5/qtscript-index.html}}. This
made it possible to write small programs within Stellarium to produce
automatic presentations, set up custom configurations, and to automate
repetitive tasks.
By version 0.14.0 the new scripting engine had reached a level where
it had all required features for usage, and support of scripts for
the old \emph{Stratoscript} engine has been discontinued.
The programming language
\indexterm{ECMAScript}\footnote{\url{https://en.wikipedia.org/wiki/ECMAScript}}
(also known as \indexterm{JavaScript}) gives users access to all basic ECMAScript
language features such as flow control, variables, string manipulation
and so on. Its integration with Qt's QtScript module and the way Stellarium's main
components (``StelModule''s) have been designed to work means that all module
functions which are labeled as ``slots'' can be called from JavaScript.
Interaction with Stellarium-specific features is done via a collection
of objects which represent components of Stellarium itself. The
various modules of Stellarium, and also activated plugins, can be
called in scripts to calculate, move the scene, switch on and off
display of objects, etc. You can write text output into text files
with the \command{output()} command. You can call all public slots
which are documented in the scripting API documentation\footnote{
\url{https://www.stellarium.org/doc/\StelSeries/scripting.html}}.
With the adaptation to Qt6 in versions 1.0 and later we had to switch to
yet another scripting engine in 2022. Qt6 comes with
QJSEngine\footnote{\url{https://doc.qt.io/qt-6/qtjavascript.html}}, another
JavaScript engine which behaves mostly similar to the older QtScript.
However, small differences exist, so that care must be taken if scripts
should be developed for both series of Stellarium. Some older scripts
will not work on versions 1.0 and later or at least require some adaptations
described in section~\ref{sec:scripting:differences}.
% Section 2020-03-26 by Wolfgang Laun
\section{The Script Console}
\label{sec:scripting:console}
It is possible to load, edit, run and save scripts using the script
console window. To toggle the script console, press \key{F12}.
\subsection{The Tabs in the Console}
The three tabs ``Script'', ``Log'' and ``Output'' each provide a text
field. In the ``Script'' text field you can edit a script,
which can be loaded from and saved to a file, and executed. The ``Log''
text field receives all errors and other messages resulting from the
script's execution; the ``Output'' text field will show the results of
\texttt{core.output()} function calls.
The fourth tab, ``Settings'', provides some configuration options.
\subsection{The Menu Bar}
The menu bar contains buttons for loading and saving a script. A file
dialog is shown for selecting the file. The ``clear'' button lets you
clear the contents of the text field in the currently selected tab.
The drop-down menu ``Execute:'' offers a selection of menu entries, mainly
intended to let you restore the sky to a clean state. The entry
``selected text as script'' executes selected text in the ``Script'' text
field, allowing to test scripts during development.
Entries ``remove screen text'', ``remove screen images'' and
``remove screen markers'' remove labels, images and markers, respectively,
that may have been left over on the screen from previous script actions.
Each of the ``clear map'' entries
results in a call to \texttt{core.clear()}, using one of the arguments
``natural'', ``starchart'', ``deepspace'', ``galactic'' or ``supergalactic''.
These calls reset Stellarium's state, resulting in a basic state
of the display and providing a clean starting point for another run of
your script.
The button SSC runs the Stellarium Script Preprocessor with the script in the text
field as input and produces an output by recursively inserting all
included scripts into the text.
\emph{This output replaces the contents of the Script text field. Store the original script if you need it!}
\subsection{German Keyboards}
There is a glitch in the keyboard module of the underlying system that
is responsible for keyboard handling. It affects users of Windows and
Linux systems with a German, and probably also other international
keyboards: The key combinations \key{AltGr+8} and \key{AltGr+9} cannot
be used for typing \verb+[+ and \verb+]+. You need to work around by
copy-pasting these two characters. You can also try to use a command
line argument like \texttt{-platform windows:altgr}.
\section{Includes}
\label{sec:scripting:includes}
Stellarium provides a mechanism for splitting scripts into different
files. Typical functions or lists of variables or celestial objects can be stored in
separate \file{.inc} files and used within other scripts through the
\textbf{include()} command:
\begin{script}
include("common_objects.inc");
\end{script}
\section{Minimal Scripts}
\label{sec:scripting:MinimalScript}
This script prints ``Hello Universe'' in the Script Console log window and into \file{log.txt}:
\begin{script}
core.debug("Hello Universe");
\end{script}
\noindent This script prints ``Hello Universe'' in the Script Console output window and into the file \file{output.txt}
which you will also find in the user data directory. The absolute path to the file will also be given in \file{log.txt}.
\begin{script}
core.output("Hello Universe");
\end{script}
The file \file{output.txt} will be rewritten on each run of Stellarium. In case you need to save a copy of the current output file to another file, call
\begin{script}
core.saveOutputAs("myImportantData.txt");
core.resetOutput();
\end{script}
\noindent This script uses the LabelMgr module to display ``Hello Universe'' in red, fontsize 20, on the screen for 3 seconds.
\begin{script}
var label=LabelMgr.labelScreen("Hello Universe", 200, 200,
true, 20, "#ff0000");
core.wait(3);
LabelMgr.deleteLabel(label);
\end{script}
\section{Critical Scripting Differences introduced with version 1.0}
\label{sec:scripting:differences}
Qt5's QtScript module which has been used in versions 0.10 to 0.22 was more flexible and easier to fine-tune than Qt6's QJSEngine,
which is used in the Qt6-based 1.* series of the program, i.e., versions 1.0 (22.3) and later.
Still, most scripts that have been developed for Stellarium versions 0.10 to 0.22
still run without or with just minor modifications as described in this section.
\subsection{Pause/Resume}
\label{sec:scripting:differences:pause}
The Qt6-based builds (version 1.0 and later) do not offer a way to pause and resume a script as was available in the Qt5-based builds (0.10 to 0.22).
\subsection{The Vec3f problem}
\label{sec:scripting:differences:Vec3f}
One of the most important little helper classes in the main program are three-dimensional vectors.
\texttt{Vec3f} describe those with single-precision floating point data,
and \texttt{Vec3d} are those with double-precision floating point data.
\begin{sidewaystable}
{\ttfamily\scriptsize
\begin{tabular}{rlll}
&Input & Result with Qt5 & Result with Qt6 \\\hline
x&\textbf{var f1=new V3d(.1,.2,.3);} & & \\
&core.output(f1); & [0.1, 0.2, 0.3] & V3d(0x249a1cf37c0) [some address] \\
&core.output(+f1.toString()); & [0.1, 0.2, 0.3] & V3d(0x249a1cf37c0) [some address] \\
x&\textbf{core.output(f1.toHex());} & \#19334c & \#19334c \\
x&\textbf{core.output(f1.toVec3d());} & [0.1, 0.2, 0.3] & [0.1, 0.2, 0.3] \\\hline
x&\textbf{var f2=new V3d(f1.x(), 2*f1.y(), 3*f1.z());} & & \\
&core.output(f2); & [0.1, 0.4, 0.9] & V3d(0x249a1cf3a60) [some address] \\
x&\textbf{core.output(f2.toVec3d());} & [0.1, 0.4, 0.8999999999999999] & [0.1, 0.4, 0.9] \\\hline
x&\textbf{var crimson=new Color("Crimson");} & & \\
x&\textbf{core.output(crimson.toHex());} & \#dc143c & \#dc143c \\
&core.output(crimson.toVec3f()); & [r:0.8627451062202454, g:0.0784313753247261, b:0.23529411852359772] & [0.862745, 0.0784314, 0.235294] \\
&core.output(crimson.r); & 0.8627451062202454 & error \\
&core.output(crimson.r()); & error & 0.8627451062202454 \\
x&\textbf{core.output(crimson.getR());} & 0.8627451062202454 & 0.8627451062202454 \\
x&\textbf{core.output(crimson.getG());} & 0.0784313753247261 & 0.0784313753247261 \\
x&\textbf{core.output(crimson.getB());} & 0.23529411852359772 & 0.23529411852359772 \\
&crimson.g=0.444; & (works) & (error) \\
&crimson.setG(0.444); & (error) & (works) \\
x&\textbf{crimson=new Color(crimson.getR(), 0.444, crimson.getB());} & (reassign crimson; recommended for Qt5 and Qt6) & \\
x&\textbf{core.output(crimson.toRGBString());} & [r:0.862745, g:0.444, b:0.235294] & [r:0.862745, g:0.444, b:0.235294] \\\hline
x&\multicolumn{2}{l}{\textbf{var eq=new Color(GridLinesMgr.getColorEquatorJ2000Grid());}} & \\
x&\textbf{core.output(eq.toHex());} & \#00aaff & \#00aaff \\
&core.output(eq.toVec3f()); & [r:0, g:0.6666669845581055, b:1] & [0, 0.666667, 1] \\
&core.output(eq.toVec3d()); & (n.a.) & [0, 0.666667, 1] \\
&core.output(eq.toString()); & [0, 0.666667, 1] & Color(0x24a11712910) [some address] \\
x&\textbf{core.output(eq.toRGBString());} & [r:0, g:0.666667, b:1] & [r:0, g:0.666667, b:1] \\\hline
x&\textbf{var c=new Color(core.vec3f(.3, .4, .5));} & & \\
&core.output(c.toString()); & [0.3, 0.4, 0.5] & Color(0x249a1e078b0) [some address] \\
&core.output(c.toVec3f()); & [r:0.30000001192092896, g:0.4000000059604645, b:0.5] & [0.3, 0.4, 0.5] \\
x&\textbf{var d=c;} // assign to other & & \\
&core.output(d.toVec3f()); & [r:0.30000001192092896, g:0.4000000059604645, b:0.5] & [0.3, 0.4, 0.5] \\\hline
x&\textbf{var c2=new Color(.3, .4, .5);} & & \\
&core.output(c2.toString()); & [0.3, 0.4, 0.5] & Color(0x24a11712a60) [some address] \\
&core.output(c2.toVec3f()); & [r:0.30000001192092896, g:0.4000000059604645, b:0.5] & [0.3, 0.4, 0.5] \\
x&\textbf{var d2=c2;} // assign to other & & \\
&core.output(d2.toVec3f()); & [r:0.30000001192092896, g:0.4000000059604645, b:0.5] & [0.3, 0.4, 0.5]
\end{tabular}}
\caption{Use of V3d, V3f and Color wrapper classes. Only use the calls marked with \texttt{x} in scripts targeted at all versions of Stellarium.}
\label{tab:scripting:Vec3f}
\end{sidewaystable}
As late as in version 0.19 we added \texttt{Vec3f} and later \texttt{Vec3d} data types to the Javascript environment.
These allowed addressing variables of C++ classes \texttt{Vec3f} or \texttt{Vec3d} with named sub-fields \texttt{r}, \texttt{g}, \texttt{b}
(if used to describe colors), or \texttt{x}, \texttt{y}, \texttt{z} (same data but contextually understood as 3D data).
These functions may have pleased the JavaScript connoisseur when running scripts with Qt5-based builds of
Stellarium (series 0.*, and now usually found on 32-bit and older systems), but we recommend to abstain from their use in new scripts.
Qt6's scripting module cannot be extended that easily. The \texttt{Vec3d} and \texttt{Vec3f} data types are not directly scriptable,
but we can deal with method arguments of these types when they are mentioned in the API documentation (see \ref{sec:scripting:introduction}).
To access internals of these classes, we must use wrapper classes which bear different names:
\begin{description}
\item[\texttt{V3d}] can be used where \texttt{Vec3d} must be accessed.
\item[\texttt{V3f}] can be used where \texttt{Vec3f} must be accessed.
\item[\texttt{Color}] can be used where a \texttt{Vec3f} must be accessed which represents a color value.
\end{description}
They behave slightly different when run using Qt5 or Qt6, as shown in Table~\ref{tab:scripting:Vec3f}. If you aim for maximum compatibility between versions,
only use the calls which provide equal results in both series. This current state is far from optimal or even pretty, but a pragmatic solution that works.
We invite advanced developers to find a better solution that works with both Qt5 and Qt6.
\section{Example: Retrograde motion of Mars}
\label{sec:scripting:RetrogradeMotionOfMars}
A good way begin writing of scripts: set yourself a specific
goal and try to achieve it with the help of few simple steps. Any
complex script can be split into simple parts or tasks, which may solve any
newbie problems in scripting.
Let me explain it with examples.
Imagine that you have set a goal to make a demonstration of a very
beautiful, but longish phenomenon --- the retrograde motion of the
planet Mars (Fig.~\ref{fig:Mars2005}).
\begin{figure}[tb]
\centering\includegraphics[width=0.8\linewidth]{Mars2005_tezel.jpg}
\caption{Retrograde motion of Mars in 2005. {\small(Credit \& Copyright: Tunc Tezel --- APOD: 2006 April 22 -- Z is for Mars.)}}
\label{fig:Mars2005}
\end{figure}
\subsection{Script header}
Any ``complex'' script should contain a few lines in the first part of
the file, which contains important data for humans --- the name of the
script and its description --- and some rules for Stellarium. You can
even assign default shortcuts in the script header, but make sure you
assign a key not used elsewhere! The shortcuts are read during startup
from all scripts in the \file{scripts} sub-directories of both program
and user data directories (see section
\ref{sec:FilesAndDirectories:DirectoryStructure}). The description
may cover several lines (until the end of the commented header) and
should therefore be the last entry of the header.
\begin{script}
//
// Name: Retrograde motion of Mars
// Author: John Doe
// License: Public Domain
// Version: 1.0
// Shortcut: Ctrl+M
// Description: A demo of retrograde motion of Mars.
//
\end{script}
\subsection{A body of script}
At the first stage of writing of the script for a demo of
retrograde motion of Mars we should set some limits for
our demo. For example we want to see motion of Mars every
day during 250 days since October $1^{st}$, 2009.
Choosing a value of field of view and of the coordinates
of the center of the screen should be done at the this
stage also.
Let's add few lines of code into the script after the header
and run it:
\begin{script}
core.setDate("2009-10-01T10:00:00");
core.moveToRaDec("08h44m41s", "+18d09m13s",1);
StelMovementMgr.zoomTo(40, 1);
for (i=0; i<250; i++)
{
core.setDate("+ 1 days");
core.wait(0.2);
}
\end{script}
\noindent\textbf{Note:}
The \texttt{wait(delay\_seconds)} instruction inside the loop is important:
when you are just setting the time in rapid succession, there is no guarantee
that the view is updated, or a textual result has been updated when writing ephemerides,
before executing the next time update!
The \texttt{wait()} ensures the program can update the main simulation loop and
put the planets where they should be at that set time.
The minimum delay required may depend on your computer's speed.
OK, Stellarium is doing something, but what exactly is it
doing? The ground and atmosphere is enabled and any
motion of Mars is invisible. Let's add an another few
lines into the script (hiding the landscape and atmosphere)
after setting date and time:
\begin{script}
LandscapeMgr.setFlagLandscape(false);
LandscapeMgr.setFlagAtmosphere(false);
\end{script}
The whole sky is moving now --- let's lock it! Add this line
after previous lines:
\begin{script}
StelMovementMgr.setFlagLockEquPos(true);
\end{script}
It looks better now, but what about cardinal points,
elements of GUI and some ``glitch of movement''?
Let's change the script:
\begin{script}
core.setDate("2009-10-01T10:00:00");
LandscapeMgr.setFlagCardinalsPoints(false);
LandscapeMgr.setFlagLandscape(false);
LandscapeMgr.setFlagAtmosphere(false);
core.setGuiVisible(false);
core.moveToRaDec("08h44m41s", "+18d09m13s",1);
StelMovementMgr.setFlagLockEquPos(true);
StelMovementMgr.zoomTo(40, 1);
core.wait(2);
for (i=0; i<250; i++)
{
core.setDate("+ 1 days");
core.wait(0.2);
}
core.setGuiVisible(true);
\end{script}
It's better, but let's draw the ``path'' of Mars! Add
those line before the loop:
\begin{script}
core.selectObjectByName("Mars", false);
SolarSystem.setFlagIsolatedTrails(true);
SolarSystem.setFlagTrails(true);
\end{script}
Hmm\ldots let's add a few strings with info for users (insert
those lines after the header):
\begin{script}
var color = "#ff9900";
var info = LabelMgr.labelScreen("A motion of Mars", 20, 20,
false, 24, color);
var apx = LabelMgr.labelScreen("Setup best viewing angle, FOV
and date/time.", 20, 50, false, 18, color);
LabelMgr.setLabelShow(info, true);
LabelMgr.setLabelShow(apx, true);
core.wait(2);
LabelMgr.setLabelShow(apx, false);
\end{script}
Let's add some improvements to display info for users ---
change in the loop:
\begin{script}
var label = LabelMgr.labelObject(" Normal motion, West to
East", "Mars", true, 16, color, "SE");
for (i=0; i<250; i++)
{
core.setDate("+ 1 days");
if ((i % 10) == 0)
{
var strDate = "Day " + i;
LabelMgr.setLabelShow(apx, false);
var apx = LabelMgr.labelScreen(strDate, 20,
50, false, 16, color);
LabelMgr.setLabelShow(apx, true);
}
if (i == 75)
{
LabelMgr.deleteLabel(label);
label = LabelMgr.labelObject(" Retrograde or
opposite motion begins", "Mars",
true, 16, color, "SE");
core.wait(2);
LabelMgr.deleteLabel(label);
label = LabelMgr.labelObject(" Retrograde
motion", "Mars", true, 16, color,
"SE");
}
if (i == 160)
{
LabelMgr.deleteLabel(label);
label = LabelMgr.labelObject(" Normal motion
returns", "Mars", true, 16, color,
"SE");
core.wait(2);
LabelMgr.deleteLabel(label);
label = LabelMgr.labelObject(" Normal motion",
"Mars", true, 16, color, "SE");
}
core.wait(0.2);
}
\end{script}
\section{More Examples}
\label{sec:scripting:examples}
The best source of examples is the \file{scripts} sub-directory of the
main Stellarium source tree. This directory contains a sub-directory
called \file{tests} which are not installed with Stellarium, but are
nonetheless useful sources of example code for various scripting
features.
% TODO: More examples?
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "guide"
%%% End: