GeoMaestro
Advanced GUI features: customizing/programming
customizing the initialisations.k
the ACPATCHES system
writing and registering a new projector
writing and registering a new distortion function
writing and registering a new plug-in generator
events with scripts
using the GUI tool from inside a program (or from the console)
customizing the initialisations.k
Here you can set differents global variables that act like parameters for the whole GeoMaestro system. They are located in the body of the InitConstantes()
function (see here for a description of the initialisation steps)
|
Most of them (but not all of them !), would better be set in your GeoPostInit() function (located in userlib/GeoPostinit.k). The value defined there will override the ones set in InitConstantes() .
Doing so will make it easier to update the system, since you will not have to hack again the initialisations.k file for each new version of GeoMaestro.
Moreover, this can be done very easily with the help of the Customization tool
|
|
Here are the most important parameters:
CPCM
is the number of clicks in a GeoMaestro unit of distance (see here for the way it is used).
KeepInitialTempo
is explained here.
NbCan
is the total number of channels availables, including the 16 MIDI ones. If you want to change it during a KeyKit session, use the UpdateNbCan()
function.
CentrePolaire
is the center point for the polar grid
SILENCE
is the minimum velocity value required for a projected nodur in order to appear in the rendered phrase.
DefDuration
, DefT0
and DefRegion
are the default values for the projectors optional arguments
DURDISTSCALE
and TIMDISTSCALE
are used by the "show distortions" mouse menu and the "DistMap" plug-in: they define the range for displaying duration and time offset values
AutoSnarph
: when set to 1, all phrases generated by the "hear ev/seg" and "hear ev/cir" mouse modes of the GUI are automatically Snarphed.
AutoSnarf
: when set to 1, the phrase generated by the "hear ev/seg" or "hear ev/cir" mouse mode is put into Snarf. This is not the same thing as previously ! Here there's no memory, while before the Snarf was left untouched. So these two settings are complementary.
ProjInLignes
: when set to 1, the lignes rendered by a projector contain a field "proj" which is used to perform fast ligne recalculation after a change in existing events nodurs and/or distortion functions settings. Use the function ProjAgain()
to do so. This option should be enabled only when necessary since it leads to very large lignes.
AskForRedoType
is only meaningful when ProjInLignes
has been set to 1, so that some (or all) lignes have a "proj" field. Then if it is 1, the main GUI [redo] button will query the type of recalculation to perform: a full redo, by default, or a fast recalculation performed by ProjAgain(
). If AskForRedoType
is 0, then no query will be made and [redo] will perform a fast recalculation whenever it detects a "proj" field in the ligne. Note that you can always delete the "proj" field in ligne RL[i] at the console with the command: delete RL[i]["proj"]
... this will force a full [redo] when recalculating RL[i]
AnimInLignes
: when set to 1, the lignes rendered by a projector contain a field "anim" which is used to display informations in real time in the GUI, along with an animation showing the projections, when using the [hear] button. By default, this is set to 0 since it produces very big lignes and therefore makes a lot of things become much slower than usual (and first of all, the projections themselves). If you want to go back to the default, don't forget to [redo] the projections in order to refresh the lignes and reduce their size (or alternaly directly delete their "anim" field: delete WhateverLigne["anim"]
).
AutoRedraw
: only useful if AnimInLignes
is 1. It sets how many projections are to be drawn between two redraw() of the graphic area (this because the projections tend to erase other graphical objects)
NoUNDO
when set to 1, the UNDO feature is not enabled: the GUI will respond faster and use less memory. This is only useful if did not succeed in managing time and memory with the following, more specific, parameters:
LigneUNDO
(when 0, lignes and RL array are removed from the UNDO capabilities)
EvUNDO
(same with the Ev scene)
DispUNDO
(same with the displayed objects)
UNDOLEVEL
is the size of the UNDO stack.
RegDensity
is the filling factor for regions. Regions are displayed as a cloud of random points or segments (depending on RegFill
value, from 1 to 3, which sets the type of filling); the closest RegDensity
is to 1, the thicker is the cloud. The default 0.03 seems a good value to me (more makes the display slower and darker). You can change this value at the console. If you set it to more than 0.1, consider setting FastRedraw
to 1: this will make it easy to stop the display (see below)
When FastRedraw
is set to 1, redrawing of the graphic area is performed by a specific thread which can be killed at any moment. As a consequence, the GUI responds quite fast to the mouse motions, even if a lot of graphical objects are displayed (because we don't have to wait for them to get finished any more). But on the other hand, color messages tend to leak around and you may not like it (buttons or console text may get some fun colors); also, the display can sometimes get finished right above the pop-up window, if you're too fast... which is quite unpleasant; and you will probably also have minor (?) error messages at the console from time to time. So by default FastRedraw
is set to 0: the quiet, slow (or VERY slow !) but stable mode. You can change FastRedraw
value on-the-fly at the console, when you need it, and I think it's the best way to use it.
MDOP
is the path to the default directory where MIDI files will be written
MAT
is the location of the /GeoMaestro directory
DATA
is the path to the /data directory where many different kinds of data files will be written
COMPOS
is the location of the /userlib directory; it is where you can store you own .k files for compositions. You should not change MAT
and COMPOS
; if you do so you will have to update the code in keylocal(), so that KeyKit knows where it has to look for .k files
BASE
is where GeoMaestro looks for data to start with (like .ev and .geo file)
DEFDIR
is used by a few functions as a default directory where to write files (by default it is DATA
)
Then you must choose the langage in which messages are issued: uncomment EnglisBlahBlah()
for english, or FrenchBlahBlah()
for, euh.. japanese (not ?)
NOTEPAD1
and NOTEPAD2
are used by the commandsystem(NOTEPAD1+"filename"+NOTEPAD2)
...to open the file "filename" in an external text editor. By default this is configured for Windows' notepad.
TEMPFILE
is the default file name used by the Notepad()
function for writing a temporary text file. It should have an absolute path name (so you have to change it from its initial setting, since by default it is DATA+"Notepad.txt"
, where DATA
is a relative path), otherwise the text editor may decide the file is locked and you won't be able to edit it. Don´t ask me why.
HearWithTimidity
, RenderAsWave
, EditedRenderedWave
and the related parameters are described here
TocTocToc
is the amount of time (measured in clicks) before a phrase is send to MIDI OUT when using the [hear] button in the GUI. It applies also when you use the Bang tool on the GUI (whose bang() method is the same as [hear]'s action)
AutoSnarph
: when set to 1, the phrases generated by the "hear ev/seg" and "hear ev/cir" mouse modes are automatically Snarphed.
CS_DIGITS
is the default number of digits for a float parameter in a CSound score.
CS_SnarfFile
is the name of the file use for temporary scores (it is used by [snarf], [Score] and ExScore() )
ShowScoreWhenSnarfed
: when set to 1, a [snarf]ed score is automatically displayed in the text editor (see NOTEPAD1
and NOTEPAD2
above)
Then you have a whole bunch of parameters used by the Compositor Tool to mix audio files. They are described here. The other parameters specific to the Compositor are described here.
the ACPATCHES system
In KeyKit, tools like the "Program Change" one rely on the patchmap() function in lib/maps.k to provide a list of the available instruments. You can change their names if you hack its code, but the Midi messages sent by a "Program Change" tool will anyway not include a bank change message, so you can only choose your instrument from program 0 to 127 in bank 0, that is the GM (General Midi) set.
About the drums, it's a bit the same thing: drummap() (again defined in lib/maps.k ) gives a list of the drums names and keynotes, but this will only refer to program 0 on a drum channel (which channel is the drum channel depends on your synthesizer's setup; usually it's channel 10, sometimes along with channel 16)
So if you're not going to write your own functions to generate bank changes, you're stuck with the instruments in bank 0 and the drums in program 0
GeoMaestro includes a system to give access to 128 banks and 128 drumsets (making it possible, for example, to have any subset of patches from the ones defined in a TiMidity configuration file available in a "Program Change" tool menus)
Here's the idea: the global variable ACPATCHES is used to index a collection of patches, defined in external files. You can set ACPATCHES's value in the geoprerc()
function (located in the lib/geomaestro.k file from KeyKit distribution). If it is 0, or if it is undefined, KeyKit's behaviour will be the usual one (default).
If ACPATCHES is an integer n, KeyKit's patchmap() function will call GeoMaestro's ACpatchmap() function (located in GeoMaestro/lib/acpatches.k). It will look for a file called ACpatchmapn.txt in the GeoMaestro/userlib directory. It is in this file that should be defined the set of patches to be used in the following KeyKit session. So, for example, if ACPATCHES is 2, we need a file called "ACpatchmap2.txt"; if it doesn't exist, KeyKit will end up immediately.
WARNING: a bug has to be corrected in the distribution of KeyKit 6.6b; see here for details.
The basic format for the "ACpatchmapn.txt" is the following:
- A line starting with # is a comment and is ignored
- Empty lines are okay
- A patch is defined by its name (which can include space characters), its bank number (0 to 127), its program number (0 to 127).
These three fields must be separated by tabulations
Here is an example file.
This basic format does not allow you to define drum sets yet (it will come some day... especially if someone asks for it !).
There's a second format, that can be used and combined with the previous one: it is a set of keywords which manage configurations files from GNU's soft synth TiMidity and make it very easy and powerful to create a patchmap out of TiMidity's settings. This format is described here.
You're not supposed to change ACPATCHES's value during a KeyKit session, mainly because some tools (notably the "Program Change" tool) will be unable to refresh themselves and take the change into account. Note that the GeoMaestro GUI handles this change (you can even make on-the-fly modifications to the "ACpatchmapn.txt" file, and have them taken into account immediately by clicking the [REDRAW] button of the GUI) .
If you want to do so anyway, use the ACRefresh() function:
ACRefresh(n)
... will set ACPATCHES to n and make the necessary initialisations. You will probably have to close and re-open some tools.
NOTE: In KeyKit 6.6c, a more elaborate "Program Change" tool is introduced: this one seems to have its own way to manage alternate patchmaps, and it does not support the ACPATCHES system. Use the "prog change (old)" tool instead.
Drum sets:
At the moment, only the TiMidity format allows you to manage different drum sets.
The drum sets appear at the end of the patchmap, as instruments to use in a drum channel. To have details about specific drums in the sets, you must use the "Drum Picker" tool. To install the tool, add the line
Drum Picker:wdrumpick
... to your GeoMaestro/userlib/localtools1.lst.
The upper button in this tool is used to select a drum set, the second one to Snarf a drum note. You can use it with a Kboom tool, for example: Snarf the drum you want, then choose "Use Snarf" in the Kboom track menu in order to replace the previous drum/phrase with the one you just choosed.
Internals:
The patchmap is stored in the array ACPat
The drumsets are stored in the array ACDrumset
All code related to the management of alternative patchmaps lives in GeoMaestro/lib/acpatches.k
writing and registering a new projector
A projector is a function taking any kind and number of arguments (only the four last ones are imposed), using them to perfom a calculation on the events scene, and returning a ligne as output.
A projector is registered if its name appears in the PROJECTORS list in the GUI.
If you want to make your projector available from the GUI you must update the array returned by the proj_infos()
function in order to perform the registration. There you must give the parameters names (as they will appear in the console when you use the projector), and also give their types using the special syntax set at the beginning of proj_infos()
NOTE: proj_infos()
is coded in the lib/lib_proj.k file. If you want to avoid changing anything there (which is recommended since it will make it easier to upgrade the system by simply replacing all files in the GeoMaestro/lib directory), you can instead use the corresponding user function in userlib/GUIuserreg.k; see the comments in this file for details.
You can do this in a text editor without quitting KeyKit nor deleting the current GUI. When the code is ready and lib/lib_proj.k or userlib/GUIuserreg.k is updated, simply type
#include lib_proj.k
or
#include GUIuserreg.k
at the console, and also #include
the file where you wrote the actual code, then redraw the tool and it's done: the new projector is immediately available.
About the parameters: the four last ones (optionals duration
, t0
, region
and df
) must be represented by a ... in the function definition (see below), and they do not need to be referenced: it's automatic.
Now, about the code of the function:
Writing your projector from scratch is a big task, since it has to produce a ligne, which is a complex array where some fields are optional and most of them aren't. If you're really going to do so, please look at the Ecoute()
and EcouteC()
implementations, in the projection.k file. These are the lowest-level usable projectors. They call the functions ProjSeg(
) and ProjCer()
who calculate the geometry of projection for a segment or a circle, and who themselves call the function CoreModes()
were live the calculations leading from an event's fields to (a) projected note(s). Good hacking would involve writing a function of the same level as ProjSeg
and ProjCer
and relying on CoreModes
for the basic calculation.
Now the simplest way to implement a new projector is to make it depend on Ecoute()
and/or EcouteC(
), or else on any other already existing projector: the main task is then to simply combine the different outputs, which are lignes, and this is easily done with the functions NewLigne()
, PlusLignes()
and MergeLignes()
.
Here's the template for a projector definition:
#------------------------------------------------------------------------------------
function name( p1 , p2 , ... , pn ,...)
{
duree = OptArgs(...)["duree"]
t0 = OptArgs(...)["t0"]
region = OptArgs(...)["region"]
df = OptArgs(...)["df"]
ligne name = NewLigne( projector id , t0)
(...) definition of the returned ligne, based either on already existing projectors,
or on Ecoute and/or EcouteC
return ( ligne name )
}
#------------------------------------------------------------------------------------
... and here's a commented example (from lib_proj.k):
#------------------------------------------------------------------------------------
function Scint(a,b,long,ouvert,nS,...) # calls nS times AversB between points a and b
{
duree = OptArgs(...)["duree"] # these lines take care of the optional arguments
t0 = OptArgs(...)["t0"]
region = OptArgs(...)["region"]
df = OptArgs(...)["df"]
bars= NewLigne("Scint", t0) # the projector id should simply be the name of the function
for (n=1; n<=nS; n++)
bars = MergeLignes(bars, AversB(a,b,long,ouvert,duree,bars[1],region,df))
# this adds the ligne AversB(a,b,long,ouvert,duree,0,region,df) to bars.
# Note the t0 = 0 in the call.
return (bars)
}
#------------------------------------------------------------------------------------
The AddLignes()
function is the main and easiest way to create a ligne by adding the results from different projectors: it manages "inter"
fields and time sequence, so that you don't have to calculate anything: just use 0 as value for the t0 argument in the call for the next ligne to add, and the next rendered phrase will start right at the end of the previous one.
MergeLignes()
is similar to AddLignes()
, it simply merges lignes instead of adding them. This is the same as having projectors working together (you can use their t0 parameter as an offset value). Of course you can combine AddLignes()
and MergeLignes()
, in the same way as you would combine the KeyKit "+" and "|" phrase operators.
Creating from scratch a new ligne should always be done with Newligne()
; its first argument is the value of the "id"
field of the new ligne, the second one its starting time. Both are optional.
For more examples of code for projectors, see the files projection.k and lib_proj.k. Their usage is described here.
writing and registering a new distortion function
A distortion function takes as arguments the following ones:
- ph this is the nodur of the event to project
- pan this is the side (+1 is right, -1 is left) of the event relatively to the support
- dist distance from the event to the support
- ch MIDI channel (identical to the event's channel only for the 16 first ones !)
- theta angle (from 0 to 2*Pi) of the projection
- t is the local time (in clicks): use it at your own risks, it is not documented here
- ... (optional) are any number of parameters specific to the function
You must register the functions you want to use in the GUI by listing their names in the choice_of_distortions()
function, which is coded in file lib_dist.k. If one or more global parameters are used, you CAN give their names in the parameters_infos()
function, so that they will appear at the console when you use the GUI and will also automatically be registered in the GVARS
array. You MUST set their default value in the InitParameters()
function. The default values for local parameters (the ones that will be affected when typing "-" at the GUI) MUST be defined in the RegDFDefaultArgs
array, in the SetDefaultLocalParameters()
function
In the function dist_comments()
, you should write a short text explaining the usage of the distortion function and its parameters. This will be written at the console when using the GUI to select distortion.
NOTE: you should actually leave alone the lib/lib_dist.k file (which will make it easier to later upgrade the system, since you will only have to replace all files in the /lib directory), and instead use the corresponding user functions in userlib/GUIuserreg.k; see the comments in this file for details.
See the codes in lib_dist.k as examples. Note that the type of distortion function (Volume, Dur, Pit, Pan, Time
) only refers to what will happen to the value they return (which is always an integer or a float): the output of a Volume
function is added to the event's nodur volume attribute, etc... Since the first argument is the nodur itself, you can use it in any way you want in the calculation. For example, a volume output could depend on the nodur's pitch; duration of the note can be calculated from its volume, etc.. This is why these functions are called "distortion" functions...
On the other hand, Mer
is an array for a completely different type of functions: they return phrases, not numbers. The phrase will be merged to the distorted nodur, which means that all other distortions happen first, then the phrase is merged.
See tutorial 5 for a general overview of distortion functions, and for an introduction to the ChooseDF
plug-in which provides a simple and pleasant way to design custom functions.
When writing a function of type Volume, Pit, Dur
or Time
the main thing to keep in mind if that the returned value will be added to the current attribute value, it will not replace it.
For example, if we were to define an exponential decrease of volume, with events at distance 0 having a volume of 127, we could write
function NotGood(ph, pan, dist) # we don't need ch, so we can skip it here
{
return ( 127*exp(-dist) )
}
but since this is added to the nodur's volume attribute, the projected event volume will only decrease after a distance, and then go down to the nodur volume value, not to zero. This is not what we want.
The correct function is
function ThisIsGood(ph, pan, dist)
{
return ( 127*exp(-dist) - ph.vol)
}
... which compensates for the nodur volume (ph.vol)
The registered Vexp1
function is similar, simply it starts from the nodur volume better than 127:
function Vexp1(ph, pan, distance)
{
if ( distance > 5 ) return(-127) # this to shorten calculation time
return ( ph.vol*(exp(-distance)-1) )
}
For functions of type Pan
, it is different again: the return value is used to set the pan controller, which is then merged at the beginning of the nodur. So if the nodur had already a pan message in it, the Pan function will have no effect.
writing and registering a new plug-in generator
A plug-in generator is simply a new mouse mode for the GUI. A template is provided so that it is very simple to program one; notably, all graphics and mouse messages are handled by the plug-in system. Only the core of the programming is left to you: what you want the mouse mode to do.
The plugin must have a name: say Foo... no, it's overused. We'll call it Toto. To register Toto, simply add an entry "Toto" to the array that is returned by the function choice_of_generators()
, in lib/lib_gen.k :
Toto will immediately be available from the GUI.
NOTE: Again, you want to leave alone the lib/lib_gen.k file (which will make it easier to later upgrade the system, since you will only have to replace all files in the /lib directory), so you can instead use the corresponding user function in userlib/GUIuserreg.k; see the comments in this file for details.
Now, programming Toto:
Three functions are required: TotoDown()
says what will happen when you click in the graphic area in Toto mode; TotoDrag()
defines what happens when you drag the mouse (with the button still pressed); TotoUp()
codes what happens when you release the mouse button
(If you want the action to be performed by a simple click, then you can simply create a single Toto()
function: it will be considered as a TotoDown()
, and nothing will happen when you drag and release the mouse)
As you can guess, these functions have a specific format: their arguments are mandatory, and the return value of TotoDrag()
has a precise meaning:
- When you first click somewhere, you define a starting point A in the scene: let's say that its coordinates are xA and yA (these are not the coordinates of the actual graphic point in the window: they are the coordinates of the point in the GeoMaestro scene).
- While you're dragging the mouse, you define the moving point D
- When you release the mouse button, you define an ending point B: xB, yB
This is already enough to explain what does the TotoDrag()
return value: it is an integer which determines what kind of graphic will be displayed while you drag the mouse. Here is the list of presets (in parenthesis the meaning of the pi parameters, see below for more):
- 0: nothing
- 1: segment from A to D
- 2: rectangle with opposite corners A and D
- 3: circle whose center is A
- 4: circle containing A whose center is D
- 5: circle whose center is the middle of AD
- 6: reference lignes: 2 lines crossing in A + 2 lines crossing in D + the line (A,D)
- 7: cake-like cut of the plane in 4 parts around A + segment AD (p1: number of the cut)
- 8: same, with 8 parts (p1: number of the cut)
- 9: same, with 16 parts (p1: number of the cut)
- 10: cut of the plane in rings centered in A and separated by 1 unit + segment AD
- 11: cut of the plane in rings centered in A and separated by 0.1unit + segment AD
- 12: transparent active grid 12*12 (p1, p2: coordinates of the choosen box)
- 13: transparent active grid 10*10 (p1, p2: coordinates of the choosen box)
- 14: transparent active grid 6*6 (p1, p2: coordinates of the choosen box)
- 15: transparent active grid 16*16 (p1, p2: coordinates of the choosen box)
- 16: active grid 12*12 with white background (p1, p2: coordinates of the choosen box)
- 17: active grid 10*10 with white background (p1, p2: coordinates of the choosen box)
- 18: active grid 6*6 with white background (p1, p2: coordinates of the choosen box)
- 19: active grid 16*16 with white background (p1, p2: coordinates of the choosen box)
- 20: pop-up menu (p1: chosen menu item)
For example, the provided Clavier
plug-in use the graphic preset 12 (check ClavierDrag()
in lib/lib_gen.k); ModNodur
uses 10 and 0; RandPick
uses 2; CtrlEvent
uses 20...
Now the arguments for the three functions (you can change their names, but not what they mean !):
function TotoDown(xA, yA, target, ch) { .. }
function TotoDrag(xD, yD, target) { .. }
# or TotoDrag(xD, yD, target, p1, p2, p3, pchange_flag)
function TotoUp(xB, yB, target, p1, p2, p3) { .. }
.. so you always have the coordinates of A, D and B available. Also:
ch is the current channel.
There are two set of parameters for TotoDrag()
, because TotoDrag()
is called in two distinct occasions: first at the very beginning of the process, in order to know what kind of display the plug-in asks for. At this points, it will be called with the only first three arguments. Later, TotoDrag()
is called for each motion of the mouse; then all arguments are used. So when you write the code for TotoDrag()
, take care that there is a test on nargs()
(see KeyKit documentation for more) if you want to use the four last parameters.
The types and values of p1, p2 and p3 depends on the graphic preset. For example, if the preset is an active grid (presets 12 to 15), p1 and p2 are the coordinates of the selected box in the grid. More about this later, since active grids are a powerful plug-in management tool.
pchange_flag is 1 if one of the parameters p1, p2, p3 just changed. With some displays (such as the grids), it makes you able to detect that the mouse moved from one area to another.
target is an array: by default it is empty ([]), but if the mouse is close enough to an event, it contains informations about the "targeted" event, that is:
- all fields composing the event (see here for more)
- the extra field
"alias"
which is 1 if you're targeting an event's alias, 0 otherwise
- the extra fields
"ch"
and "n"
which contain the indexes of the event in the Ev
array (see here for more)
Check the code for the ChooseDF
plug-in for a comprehensive example (it is in lib/chooseDF.k)
Here we have the code for a Demo plug-in which illustrates how you can use these parameters:
#------------------------------------------------------------------------------------
function DemoDown(x, y, target)
{
StartingPoint = xyd(x,y)
}
function DemoDrag(x, y, target)
{
print (x,y)
if (sizeof(target)>0) print ("this is event :", target)
return (0)
}
function DemoUp(x, y, target)
{
EndingPoint = xyd(x,y)
print("started at: ", StartingPoint)
print("ended at: ", EndingPoint)
}
#------------------------------------------------------------------------------------
... to see this code in action, cut/paste it in lib_gen.k, then add "Demo" to the array in choice_of_generators()
If KeyKit is opened, type
#include lib_gen.k
...at the console; and if GeoMaestro's GUI is opened, click on [REDRAW] to refresh it. You should now have the Demo plug-in available from the GUI.
Now, another example code (Demo2 plug-in) involving an active grid to create a set of (useless...) buttons. GenPalette
is the array containing the text that appears in the boxes; here it is a 6x6 array because we used preset 14:
#------------------------------------------------------------------------------------
function Demo2Down(x, y, target)
{
GenPalette[0] = [0 = "Yep", 1 = "Bouh", 5 = "Hmm"]
GenPalette[3] = [3 = "Bof"]
}
function Demo2Drag(x, y, target)
{
return (14)
}
function Demo2Up(x, y, target, i, j)
{
print("clicked in ("+string(i)+","+string(j)+") :",GenPalette[i][j])
}
#------------------------------------------------------------------------------------
If your plug-in is going to affect the Ev
scene (changing, adding or deleting events), you will find useful functions here.
As a last example, here is a Demo3 plug-in which creates a new event if you click an empty place, or kill an existing event if you click on it:
#------------------------------------------------------------------------------------
function Demo3(x , y, target, ch)
{
if (sizeof(target)>0)
{
RemoveEv(target["ch"], target["n"])
print ("deleted event :", target)
}
else
{
CreateNewEvent(x, y, ch, '')
print ("created an empty event in channel "+string(ch))
}
}
#------------------------------------------------------------------------------------
... in this example, the action occurs only when clicking, so Demo3Drag()
and Demo3Up()
are not required
Since a simple click-and-drag may not be enough for what you intend to do, TotoUp()
may return a string indicating that another plug-in has to be started just after Toto. ChooseDF
uses this a lot: for example, when you select the "sinus" box, ChooseDFUp()
returns the string "next:SinusDF"
so that the next click of the mouse starts SinusDFDown()
. This way we can have as many plug-ins as we want linked together to perform complex actions.
TotoUp()
can also return "NoRedraw"
, in which case the display will not be refreshed (this may be useful if it represents an information we want to keep during the next plug-in action). Use ";"
to combine strings, e.g. TotoUp()
can return "NoRedraw;next:SinusDF"
Note that this does not work with pop-up menus (graphic preset 20), which behave a bit differently.
UNDO capability
By default, you can not UNDO what you did with a plug-in. To have this capability, you must define a fourth function called TotoUNDO()
, which simply returns one of the following values:
-
DUMP_ALL
(dumps everything... you should not use that one !)
-
DUMP_UNDO
(dumps everything as allowed by the UNDO parameters... better nor use it either, as it saves too many things)
-
CHANGES_IN_EVENTS
(use this if your generator only makes changes in the Ev scene)
-
CHANGES_IN_DISPLAY
(if the generator changes the displayed objects; points, circles, etc...)
-
CHANGES_IN_SETTINGS
(if the generator makes changes in the distortion functions settings)
-
CHANGES_IN_BORNES
(if the generator simply zooms in or out)
-
CHANGES_IN_LIGNES
(if the generator works on the RL array and associated settings, like grouping mode). This one works differently:
- if the value returned by
TotoUNDO()
is CHANGES_IN_LIGNES
, the RL array itself is not dumped.
- if the returned value is
2*CHANGES_IN_LIGNES
, the RL array is dumped
- if the returned value is
n+CHANGES_IN_LIGNES
, only the item RL[n]
is dumped
Check the using the GUI from a program section for useful functions to be used in a plug-in code.
events with scripts
(more here later ... see tutorial 11 in the meantime )
using the GUI tool from inside a program (or from the console)
Although the internal methods of the GUI are not well documented yet, they are fully accessible from the console or from a program, which means that you can algorithmically operate the tool, or even mix interactive use of the GUI with automatic processes programmed in a KeyKit function.
All you need is the correct pointer to the tool object. Actually, the interesting methods in the GeoMaestro GUI belong to two different objects: one is the main global window (including all the right-side buttons), the other is the graphic window (what I usually call "graphic area" in this manual). The graphic window is supposed to be a child of the main window, but my programming is a bit messy as far as objects are concerned, so it would be better to consider this two objects as a symbiotic single thing. Still, you need two pointers to refer to the corresponding methods.
When a GUI is initialised, it stores a pointer to the main window in LastGMGUI
, and a pointer to the graphic window in LastGMGUIf
.
So, for example, if one single GUI is displayed, typing this at the console:
LastGMGUI.display("Or,Oi,Oj")
... will result in the tool displaying the points Or, Oi, Oj, as it would have done if you clicked the [display] button
(See tutorial 4 for an example of use)
Much shorter: you can define macros like
#define gDISP(c) LastGMGUI.display(c)
... then you simply have to type
gDISP("Or,Oi,Oj")
... to display the points as above
See the file lib/macros.k for a list of macros, that you can of course edit and change at your convenience (there's a macro for that, actually: GMmacros
, and also mU
to "#include" the changed macros.k)
Here are the methods for LastGMGUI
you can use to emulate the most useful GUI buttons:
# .display [display]
# .zoom(p, d) ... zooms on the area best fitting a disk of center p and radius d
# .op [operation]
# .hear [hear]
# .redo [redo]
# .snarf [snarf]
# .kill [kill]
# .newEv [new]
# .delsel [!delete!]
Note that if you open several instances of the GUI in the same page, only the last one will work properly. If you open several instances of the GUI on different pages, they will all work fine, and LastGMGUI
will refer to the last one you opened. If you delete it, it will refer to the previous instance. If this instance has been deleted meanwhile, it will not refer to anything: some operations and plug-ins will then be broken.
Note: the function
GeoGUIinpage()
... returns 1 if a GeoMaestro GUI is opened in the current page (Root), 0 otherwise.
See tutorial 9 for an example of GUI programming.
Graphic functions
To make it easy to perform displays inside the GUI graphic area, the following functions are defined (they are simply the basic KeyKit graphic methods, except for Geocercle()
which draws a cercle that can cross the window's limits; only for this usage should it be prefered to Geoellipse()
which is much faster):
Geocontains(xyarray) # LastGMGUIf.contains(xyarray)
Geoellipse(xyarray,...) #LastGMGUIf.ellipse(xyarray, ...)
Geocercle(x,y,r,...) #LastGMGUIf.cercle(x,y,r,...)
Geofillellipse(xyarray, ...) #LastGMGUIf.fillellipse(xyarray, ...)
Geofillrectangle(xyarray, ...) #LastGMGUIf.fillrectangle(xyarray, ...)
Georectangle(xyarray, ...) #LastGMGUIf.rectangle(xyarray, ...)
Geoline(xyarray, ...) #LastGMGUIf.line(xyarray, ...)
Geotcenter(string, xyarray, ...) #LastGMGUIf.textcenter(string, xyarray, ...)
Geotleft(string, xyarray, ...) #LastGMGUIf.textleft(string, xyarray, ...)
Geotright(string, xyarray, ...) #LastGMGUIf.textright(string, xyarray, ...)
Geoth() #LastGMGUIf.textheight()
Geotw() #LastGMGUIf.textwidth()
Geoxmax() #LastGMGUIf.xmax()
Geoxmin() #LastGMGUIf.xmin()
Geoymax() #LastGMGUIf.ymax()
Geoymin() #LastGMGUIf.ymin()
Also, we have functions converting the coordinates system from GeoMaestro scene to graphic display, and back:
xyDisp(x0, y0, x1, y1) # returns display coordinates (which should be used for graphics)
xDisp(x)
yDisp(y)
dDisp(d) # (d is a distance)
xyGeo(x0, y0, x1, y1) # returns GeoMaestro coordinates
xGeo(x)
yGeo(y)
dGeo(d)
The colors used by GeoMaestro are defined in the GMcolors()
function. You can use the following names instead of numbers in a color()
call:
COLOR_RED
COLOR_BLUE
COLOR_GREEN
COLOR_LIGHTGREEN
COLOR_BROWN
COLOR_LIGHTBROWN
COLOR_PALEGREY
COLOR_GREY
Custom backgrounds
While these functions can be called from a plug-in or any program (see the code for the DistMap
plug-in in lib/ChooseDF.k as an example), you can also use them to create a graphic background for the GUI: if a function called GUIbackground()
exists, it will be called each time the GUI is redrawed.
For example, with the following code:
function GUIbackground()
{
Geocercle(xDisp(1), yDisp(1), dDisp(1))
}
... a circle will appear in the graphic area of the GUI, independently from the Ev
scene or the displayed objects.
Another example:
function GUIbackground()
{PourToutEv(CircleAround)}
function CircleAround(ev)
{
Geocercle(xDisp(ev["x"]), yDisp(ev["y"]), dDisp(0.2))
return(ev)
}
.. this will draw a circle around each active event.
Alternatively, you may also register any number of functions in the RegGUIbackgrounds
array, like this:
RegGUIbackgrounds["myfunction"] = 1
... here myfunction()
will be called when the GUI is redrawed, exactly as if it was GUIbackground()
. Note that if there are several registered functions, there is no way to know in which order they will be called (GUIbackground
always come first, though).
A registered background may be toggled off by setting the corresponding array value to 0.
Also note that RegGUIbackgrounds
is saved and restored along with the main GUI, so that your background settings are kept between sessions.
Knowing what is currently displayed by the tool
The following methods for LastGMGUIf
returns the geometrical objects currently displayed in the GUI graphic area:
# .get_points()
# .get_pistes()
# .get_segments()
# .get_circles()
The value returned by one of these methods is an array with integer indexes: index 0 stores the number of objects. Each object has two extra fields: "nom"
, the object name, and "s"
, its selection flag (1 if selected, 0 if not)
See example code for functions calling such methods in GUI_utils.k. Also try functopic("GUI")
or functopic("object")
at the console.
Some useful functions are (see here for more):
DisplayedPoints({regexp})
SelectedPoints({regexp})
DisplayedCircles({regexp})
SelectedCircles({regexp})
DisplayedSegments({regexp})
SelectedSegments({regexp})
DisplayedPistes({regexp})
SelectedPistes({regexp})
PointsInXY(area)
CirclesInXY(area)
FullCirclesInXY(area)
SegmentsInXY(area)
FullSegmentsInXY(area)
ShowObjectsNames({col})
RenameO(name, newname)
-- Back --