Defining a series of functions in DigitalMicrograph scripting - dm-script

I have a set of functions inside a class that I need to define. Each passes a different value into another function:
void function00(object self, taggroup tg) self.otherfunction(tg,0,0)
void function01(object self, taggroup tg) self.otherfunction(tg,0,1)
void function02(object self, taggroup tg) self.otherfunction(tg,0,2)
void function03(object self, taggroup tg) self.otherfunction(tg,0,3)
void function04(object self, taggroup tg) self.otherfunction(tg,0,4)
I have 100 of these functions and I'd prefer not to define each one separately. Considering the above example I'd like to do something like:
for(number i=0; i<5; i++){
void function0+i(object self, taggroup tg) self.otherfunction(tg,0,i)
}
which doesn't work on it's own. Any suggestions?
For some more context I create a series of check boxes inside 2 for loops with the following:
BOXinsides.DLGAddElement(DLGCreateCheckBox(label,0,"function"+i+j).DLGIdentifier("#function"+i+j))
and I need to define all the functions in some sensible way.

DigitalMicrograph scripting does not allow this type of template code. However, you can solve your problem by linking all checkbox items to the same action-method. The signature of the action method passed in the TagGroup which is the checkbox item itself. You can use this to derive information from it, for example by looking at a checkbox property such as its title:
class myUI : UIframe
{
void generalFunction( object self , tagGroup checkTg )
{
// checkTg is the taggroup of the checkbox which fired the method.
// Use its Title to get back the running value!
string label = checkTg.DLGGetTitle()
Result( "\n label of checkbox:" + label )
number i = val( right( label, len( label ) - 1 ) )
Result( "\n running index:" + i )
}
TagGroup CreateCheckboxes( object self )
{
TagGroup checkboxGroup = DLGCreateGroup()
for ( number i = 0 ; I < 5 ; i++ )
{
checkboxGroup.DLGAddElement( DLGCreateCheckBox( "C" + I , 0 , "generalFunction" ) )
}
return checkboxGroup
}
TagGroup CreateDLGTags( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog( "Test" , dlgitems )
dlgitems.DLGAddElement( self.CreateCheckboxes() )
return dlg
}
object Init( object self )
{
return self.super.init( self.CreateDLGTags() )
}
}
// MAIN SCRIPT calling the dialog
{
Object dlg = Alloc(myUI).Init()
dlg.pose()
}
You can also 'attach' information directly to the checkbox. Checkboxes are - as all dialog items - really just specific TagGroup objects to which you can add whatever you like. In the example below, I'm adding an additional tag with a random number:
class myUI : UIframe
{
void generalFunction( object self , tagGroup checkTg )
{
// checkTg is the taggroup of the checkbox which fired the method.
// Use its Title to get back the running value!
string label = checkTg.DLGGetTitle()
Result( "\n label of checkbox:" + label )
number rnd
if ( checkTG.TagGroupGetTagAsNumber( "Random NR", rnd ) )
{
Result( "\n Random number:" + rnd )
}
}
TagGroup CreateCheckboxes( object self )
{
TagGroup checkboxGroup = DLGCreateGroup()
for ( number i = 0; I < 5 ; i++ )
{
TagGroup checkbox = DLGCreateCheckBox( "C" + I , 0 , "generalFunction" )
checkbox.TagGroupSetTagAsNumber( "Random NR", Random() )
checkboxGroup.DLGAddElement( checkbox )
}
return checkboxGroup
}
TagGroup CreateDLGTags( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog( "Test" , dlgitems )
dlgitems.DLGAddElement( self.CreateCheckboxes() )
return dlg
}
object Init( object self )
{
return self.super.init( self.CreateDLGTags() )
}
}
// MAIN SCRIPT calling the dialog
{
Object dlg=Alloc(myUI).Init()
dlg.pose()
}

Related

How to disable the entry box while the checkbox is not selected

Here is the script of a checkbox and an entry box in a dialog. I am wondering how to disable the entry while the checkbox is not selected.
class TestUI : UIFrame {
number true, false
TestUI(object self) {
true = 1;
false = 0;
result("[TestUI] constructed\n")
};
~TestUI(object self) {
result("[TestUI] destructed\n")
};
void CreateDialog(object self) {
TagGroup dialog = DLGCreateDialog("Test")
TagGroup cb = DLGCreateCheckbox("IsCheck",false).DLGAnchor("West")
TagGroup parameter
TagGroup entry = DLGCreateRealField("Parameter : ",parameter,5,8,1)
dialog.DLGAddElement(cb)
dialog.DLGAddElement(entry)
self.init(dialog).Display("TestUI")
};
};
{
alloc(TestUI).CreateDialog()
}
Thanks in advance.
After reviewing the manual, I found the solution.
The entry can be enabled/disable by adding a function and using "SetElementIsEnabled" to enable/disable the function while the checkbox is changing.
Initially, the entry is disabled by "DLGEnabled".
Here is the script:
class TestUI : UIFrame {
number true, false
TagGroup para1, para2
TestUI(object self) {
true = 1;
false = 0;
result("[TestUI] constructed\n")
};
~TestUI(object self) {
result("[TestUI] destructed\n")
};
void SelectionAct(object self, TagGroup tgItem) {
// To change the status while the checkbox is changing.
self.SetElementIsEnabled("BtnGroup",tgItem.DLGGetValue())
};
void BtnResponse(object self) {
result("Parameter 1 : "+para1.DLGGetValue()+"\n")
result("Parameter 2 : "+para2.DLGGetValue()+"\n")
};
void CreateDialog(object self) {
// the box is not selected when initiation
// To enable/disable the entry by building "SelectionAct" function
TagGroup cb = DLGCreateCheckbox("IsCheck",false,"SelectionAct").DLGAnchor("West")
TagGroup entry1 = DLGCreateRealField("Parameter 1 : ",para1,5,5,3)
TagGroup entry2 = DLGCreateRealField("Parameter 2 : ",para2,6,5,3)
// print the value
TagGroup Btn = DLGCreatePushButton("Print","BtnResponse").DLGFill("X").DLGIdentifier("btn")
// Group the entries and button, we can change their status with ease.
TagGroup EntryGroup = DLGGroupItems(entry1,entry2).DLGTableLayout(2,1,0).DLGIdentifier("entries")
TagGroup BtnGroup = DLGGroupItems(EntryGroup,Btn).DLGTableLayout(1,2,0).DLGIdentifier("BtnGroup")
// disable the entries and btn when initiation
DLGEnabled(BtnGroup,false)
// create dialog
TagGroup dialog = DLGCreateDialog("Test")
dialog.DLGAddElement(cb)
dialog.DLGAddElement(BtnGroup)
self.init(dialog).Display("TestUI")
};
};
{
alloc(TestUI).CreateDialog()
}
result:

How to stop a background thread when an image window is closed in dm-script

I'm trying to write a dm-script containing a background thread as following example code. In this code, I want to stop the background thread when the image window is closed. I think that some event-listener is probably required in this case. Could you advise me about how to control (i.e., stop) a background thread by closing event of an image window? If you could suggest some revision of my code or show your example code, I would be grateful for your cooperation.
// $BACKGROUND$
//
Class CBackground : Thread
{
Number isRunning
Number imgID
Image tmpIMG
//
Void Init( Object self, Number iID ){
imgID = iID
tmpIMG := GetImageFromID( imgID )
}
//
Void StopRunning( Object self ){
isRunning = 0
}
//
Number GetIsRunning( Object self ){
return isRunning
}
//
Void RunThread( Object self ){
Result("Background thread is starting ......")
isRunning = 1
while (isRunning)
{
tmpIMG = random()
sleep(0.5)
}
Result(" finished !!" + "\n")
}
}
//
Void Main(){
Object cbkg = alloc(CBackground)
Image IMG := RealImage("test",4,64,64)
IMG = random()
IMG.ShowImage()
IMG.SetWindowSize(512,512)
cbkg.Init(IMG.GetImageID())
cbkg.StartThread()
}
//
Main()
And here is an example of a script which uses a window-closed listener to abort the task.
Class CBackgroundWithListeners
{
Number isRunning
Number imgID
Image tmpIMG
Number winListenID
// Constructor and Destructor method for debugging reason
// Always automatically called when object gets created or removed from memory
CBackgroundWithListeners(object self) {
Result( "\n Creating object " + self.ScriptObjectGetClassName() )
Result( " with ID: " + self.ScriptObjectGetID() )
}
~CBackgroundWithListeners(object self) {
Result( "\n Destroying object " + self.ScriptObjectGetClassName() )
Result( " with ID: " + self.ScriptObjectGetID() )
}
// Init Method
Void Init( Object self, Number iID ){
imgID = iID
tmpIMG := GetImageFromID( imgID )
If ( !tmpIMG.ImageIsValid() )
Throw( "Image of ID " + imgID + " not found." )
if ( 0 == tmpIMG.ImageCountImageDisplays() )
Throw( "Image of ID " + imgID + " has no display." )
DocumentWindow win = tmpIMG.ImageGetOrCreateImageDocument().ImageDocumentGetWindow()
if ( !win.WindowIsValid() )
Throw( "Image of ID " + imgID + " has no window." )
winListenID = win.WindowAddWindowListener( self, "window_closed:HandleClosedAction;" )
}
//
Void StopRunning( Object self ){
isRunning = 0
}
//
Number GetIsRunning( Object self ){
return isRunning
}
//
Void HandleClosedAction(object self, number e_fl, DocumentWindow Win)
{
self.StopRunning()
win.WindowRemoveWindowListener( winListenID )
}
//
Void RunThread( Object self ){
Result("Background thread is starting ......")
isRunning = 1
while (isRunning)
{
tmpIMG = random()
sleep(0.5)
}
Result(" finished !!" + "\n")
}
}
//
Void Main(){
Object cbkg = alloc(CBackgroundWithListeners)
Image IMG := RealImage("test",4,64,64)
IMG = random()
IMG.ShowImage()
IMG.SetWindowSize(512,512)
cbkg.Init(IMG.GetImageID())
cbkg.StartThread()
}
//
Main()
You can not stop a background thread from outside that thread, i.e. you can not interrupt a thread. In order to stop a background thread, the according code needs to have a stop-condition and exit itself.
In order to steer this stop-condition from another thread, you need to "communicate" somehow between two threads. This can be done in many different ways. The easiest is to use a simple number variable which is checked by the background running code but can be set via any other "outside" code. An example for this can be found in this answer here.
Instead of a simple variable, one could also use some commonly accessible place like f.e. the global tags. Alternatively, some more sophistacted threading synchronisation objects like signals, mutexes and semphores are defined for the scripting language and describe in the help documentation here:
How an external thread inserts the 'break' into the background running thread can also be done in many different ways. One is - as in the exampe above - have a user interaction via some open dialog. Another, as mentioned by the author, is to have some event-listener code to trigger this.
The following example attaches a key-listener to the image, so that (with this image frontmost and selected) a user can press the ESC button to stop the thread.
I am using the provided script with minimum modifications for showing this:
// $BACKGROUND$
//
Class CBackground : Thread
{
Number isRunning
Number imgID
Number keyListenID
Image tmpIMG
//
Void Init( Object self, Number iID ){
imgID = iID
tmpIMG := GetImageFromID( imgID )
ImageDisplay disp = tmpIMG.ImageGetImageDisplay(0)
keyListenID = disp.ImageDisplayAddKeyHandler( self, "KeyListenAction" )
}
//
Void StopRunning( Object self ){
isRunning = 0
}
//
Number GetIsRunning( Object self ){
return isRunning
}
/////////////////////////////////////////////////////////////////////////////
Number KeyListenAction(Object self, ImageDisplay disp, Object keydesc )
{
number b_keyhandled = 0
If ( keydesc.MatchesKeyDescriptor("esc") )
{
disp.ImageDisplayRemoveKeyHandler( keyListenID )
self.StopRunning()
Result( "\nSend stopping flag, unregister Key-Listeners" )
b_keyhandled = 1
}
return b_keyhandled;
}
//
Void RunThread( Object self ){
Result("Background thread is starting ......")
isRunning = 1
while (isRunning)
{
tmpIMG = random()
sleep(0.5)
}
Result(" finished !!" + "\n")
}
}
//
Void Main(){
Object cbkg = alloc(CBackground)
Image IMG := RealImage("test",4,64,64)
IMG = random()
IMG.ShowImage()
IMG.SetWindowSize(512,512)
cbkg.Init(IMG.GetImageID())
cbkg.StartThread()
}
//
Main()
However, there are a few things I would do differently:
Start the background thread with the proper command rather then the old '$$BACKGROUND$$' method.
Encapsulate as much as possible into the class
Add a few security checkes
Also add a windows closed event listener, so that closing the image window also stops the thread
Add some debug-code to show when an object is created and when it is removed from memory
Have the keylistener to pause/unpause the action.

How to disable/enable dialog elements

In this short dialog, I am trying to enable/disable the integer field. The DLGEnabled() command does not seem to do anything here:
class BTW_Dialog : UIFrame
{
BTW_Dialog(object self) { Result( "\n Object `" + self.ScriptObjectGetClassName() + "` ID:" + self.ScriptObjectGetID() + " created." ); }
~BTW_Dialog(object self) { Result( "\n Object `" + self.ScriptObjectGetClassName() + "` ID:" + self.ScriptObjectGetID() + " destroyed." ); }
TagGroup CreateDLGTagGroup( object self )
{
// Dialog building method
TagGroup DLGtgs, DLGItems
DLGtgs = DLGCreateDialog( "Analyze", DLGItems );
TagGroup RadioList = DLGCreateRadioList( 0, "AActOnRadio" )
RadioList.DLGAddRadioItem( "LP", 0 ).DLGIdentifier("0").DLGSide( "Left" );
RadioList.DLGAddRadioItem( "LF", 1 ).DLGIdentifier("1").DLGSide( "Left" );
DLGitems.DLGAddElement(RadioList).DLGAnchor("West");
TagGroup field = DLGCreateIntegerField( 55, 4 ).DLGSide( "Left" ).DLGIdentifier("xyz");
DLGitems.DLGAddElement(field).DLGAnchor("West");
return DLGtgs
}
object LaunchAsModelessDialog( object self )
{
self.init( self.CreateDLGTagGroup() );
self.Display( "Analyze" );
return self
}
void AActOnRadio( object self, tagGroup itemTG )
{
number radioButtonState = itemTG.DLGGetValue();
vtagGroup xyz_tag = self.LookupElement("xyz")
if(radioButtonState)
{ // trying to disable integer field: <<<-------||
DLGEnabled( xyz_tag, 0)
}
}
}
Alloc(BTW_Dialog).LaunchAsModeLessDialog();
Is there any other command to disable and/or hide the integer field when the radio button is pressed? Thanks.
The command you're looking for is
void SetElementIsEnabled( ScriptObject, String identifier, Boolean is_enabled )
i.e. in your example replace
DLGEnabled( xyz_tag, 0)
by
self.SetElementIsEnabled( "xyz", 0 )
Note, there is a similar command to make a dialog element "hidden", which is
void SetElementIsShown( ScriptObject, String identifier, Boolean is_shown )

How to sort an ObjectList in Digital Micrograph

Is it any possibilities to sort the ObjectList object?
For example:
class MissPlane: object
{
number d_local
object init(object self, number d)
{
d_local=d
return self
}
number getD(object self)
{
return d_local
}
void print(object self)
{
result("d="+d_local+"\n")
}
}
number h,k,l,hmax
hmax=2
result("start----------------"+datestamp()+"----------------------\n")
Object PlaneList
PlaneList=Alloc(ObjectList)
for(l=-hmax;l<=hmax;l++){
for(k=-hmax;k<=hmax;k++){
for(h=-hmax;h<=hmax;h++){
Object MPObject=Alloc(MissPlane)
MPObject.init(random())
PlaneList.AddObjectToList(MPObject)
MPObject.print()
}
}
}
And finally I need to sort it by d.
PS. ObjectList is not fully documented in the DM manual.
You can use the DM command sort to achieve sorting of either TagLists or ObjectList. In both cases you have to create a "sorting" class which defines a method comparing two elements and returning if the second element is bigger than the first.
The following example is for TagLists, sorting a numeric tagList to have increasing numbers.
Class Sorter
{
// The signature of the following method must not be changed.
Number Bigger( Object self, TagGroup TG1, Number index1, TagGroup TG2, Number index2 )
{
Number N1, N2
TG1.TagGroupGetIndexedTagAsNumber( index1, N1 )
TG2.TagGroupGetIndexedTagAsNumber( index2, N2 )
return (N1<N2)?1:0
}
}
TagGroup CreateRandomList( Number n )
{
TagGroup list = NewTagList()
For ( Number i = 0 ; i < n ; i++ )
list.TagGroupInsertTagAsNumber( i, Random() )
return list
}
void Main()
{
TagGroup unsorted = CreateRandomList( 10 )
unsorted.TagGroupOpenBrowserWindow( "unsorted", 0 )
Object sortObject = Alloc( Sorter )
Sort( 0, unsorted, sortObject, "Bigger" ) // The first parameter specifies ‘stable’ search
unsorted.TagGroupOpenBrowserWindow( "sorted", 0 )
}
Main()
The next example is for ObejctLists, sorting an objectList. In this case, objects of the list are simply "wrapped numbers" and the soring again achieves an increasing numbers.
class MyObject
{
Number value
Object Init( Object self, Number in ) { value = in; return self; }
Number Get( Object self ) { return value; }
}
Class Sorter
{
// The signature of the following method must not be changed.
Number HigherValue( Object self, Object obj1, Object obj2 )
{
return ( obj1.Get() < obj2.Get() ) ? 1 : 0
}
}
Object CreateRandomList( Number n )
{
Object obList = Alloc(ObjectList)
For ( Number i = 0 ; i < n ; i ++ )
{
Object newOBJ = Alloc(MyObject).Init( Random() )
obList.AddObjectToList( newOBJ )
}
return obList
}
void PrintObjects( Object obList )
{
ForEach( Object ob; obList )
Result( "value:" + ob.Get() + "\n" )
}
void Main()
{
Object unsorted = CreateRandomList( 7 )
Result( "\nBefore sort:\n" )
unsorted.PrintObjects()
Result( "\nAfter sort:\n" )
Sort( 0, unsorted, Alloc(Sorter), "HigherValue" )
unsorted.PrintObjects()
}
Main()
Both code examples have been found from this website.
The following example create a simple "bubble-sort" method to sort your object list:
class MissPlane: object
{
number d_local
object init(object self, number d)
{
d_local=d
return self
}
number getD(object self)
{
return d_local
}
void print(object self)
{
result("d="+d_local+"\n")
}
}
object sortList( object in )
{
object copy = in.ScriptObjectClone() // Making a copy avoids messing with your original list...
object sorted=Alloc(objectlist)
while( copy.SizeOfList() )
{
object t = copy.ObjectAt(0)
for (number c=1; c<copy.SizeOfList(); c++ )
{
if ( t.GetD() > copy.ObjectAt(c).GetD() )
t = copy.ObjectAt(c); // found a "smaller" object
}
sorted.AddObjectToList(t) // add the smallest item found
copy.RemoveObjectFromList(t)
}
return sorted
}
number h,hmax
hmax=20
result("start----------------"+datestamp()+"----------------------\n")
Object PlaneList
PlaneList=Alloc(ObjectList)
for(h=-0;h<hmax;h++){
Object MPObject=Alloc(MissPlane)
MPObject.init(random())
PlaneList.AddObjectToList(MPObject)
}
result("start---------------- ORIGINAL ----------------------\n")
ForEach( object o; PlaneList )
o.print()
Object sorted = sortList(PlaneList)
result("start---------------- SORTED ----------------------\n")
ForEach( object o; sorted)
o.print()

How could I open more than one image with a single dialog in Digital Micrograph script?

I am writing in DigialMicrograph scripting. I want a script to open more than one image in a single dialog, a multi-select image dialog, similar to when you go to any Windows open dialog, select several images and press Ok.
I think that this is possible but I haven't found a way to do it. I want to introduce this specific code in my script, to avoid image-opening prior to running the script.
I know the function OpenDialog but this only allows opening of a single image. Could anybody show me some script or a function which can help me further?
Thanks.
What you are asking for might be a bit more involved. There is no multiple open function in the scripting language, so you can only create it yourself, involving multiple steps:
An input for a base-folder (This you could prompt the user for with a GetDirectoryDialog dialog)
Using the command GetFilesInDirectory to retrieve a list (TagList) of files in that folder
Process that list to filter out what you are interested in
Build a custom-dialog (derived from UIFrame) which lists the files with either checkboxes, multiple drop-down menus, or a list-box to select from.
Act on the selection made in your custom dialog, i.e. open all those files
The following script is an example doing that.
class MyMultiSelect : UIframe
{
string root
TagGroup FileList
TagGroup DLGlist
TagGroup CreateDLG( object self )
{
TagGroup dlg,dlgitems
dlg = DLGCreateDialog( "MultiOpen", dlgItems )
number nMax = fileList.TagGroupCountTags();
TagGroup listitems
DLGlist = DLGCreateList( listitems, 90, nMax+1 )
DLGlist.DLGMultipleSelect(1)
for (number i=0; i<nMax; i++)
{
string name
if ( FileList.TagGroupGetIndexedTagAsString(i,name) )
listitems.DLGAddListItem( name, 0 )
}
dlgitems.DLGAddElement(DLGlist)
return dlg
}
TagGroup CreateFilteredFileList( object self )
{
// Get all files
TagGroup list = GetFilesInDirectory( root, 1 )
// Filter all DM3 files
TagGroup filtered = NewTagList()
for( number i = 0; i<list.TagGroupCountTags(); i++)
{
TagGroup entry
string file = ""
list.TagGroupGetIndexedTagAsTagGroup(i,entry)
entry.TagGroupGetTagAsString( "Name", file )
if ( -1 != find(file,".dm3") )
filtered.TagGroupInsertTagAsString(Infinity(),file)
}
return filtered
}
TagGroup GetSelectedList( object self )
{
TagGroup selList = NewTagList()
TagGroup itemList
DLGlist.TagGroupGetTagAsTagGroup( "Items", itemList )
for ( number i=0; i<itemList.TagGroupCountTags(); i++ )
{
number isSelected = 0
TagGroup entry
itemList.TagGroupGetIndexedTagAsTagGroup(i,entry)
entry.TagGroupGetTagAsBoolean( "Selected", isSelected )
if ( isSelected )
{
string filename
entry.TagGroupGetTagAsString( "Label", fileName )
selList.TagGroupInsertTagAsString( Infinity(),fileName)
}
}
return selList
}
void OpenSelectedFiles( object self )
{
TagGroup files = self.GetSelectedList()
number nFiles = files.TagGroupCountTags()
if ( 0 == nFiles )
Throw( "No files selected" )
Result( "\n Opening "+nFiles+" files now..")
for ( number i=0; i<nFiles; i++ )
{
string filename
files.TagGroupGetIndexedTagAsString(i,fileName)
string path = root + "\\" + filename
Result( "\n Opening: "+path)
OpenImage(path).ShowImage()
}
}
Object Init( object self, string rootInput )
{
root = rootInput
FileList = self.CreateFilteredFileList( )
return self.super.Init( self.CreateDLG() )
}
}
// Main
{
string rootDir
GetDirectoryDialog( NULL, "Select Folder from which to multi-open", GetApplicationDirectory("current",0), rootDir )
object dlg = Alloc(MyMultiSelect).Init(rootDir)
dlg.pose()
dlg.OpenSelectedFiles()
}