Now I'm trying to write a DM script to display a floating palette as shown in the following code. However, for example, a long string label as included in this code protrude from the palette window. So, I'm wondering how to change the palette window size (i.e, width in this case). Actually, some Gatan's official palette windows such as "Camera View", "Camera Acquire" etc. seem to be wider than the pallete created by this code. It will be appreciated if you share some wisdom. Thank you very much in advance.
Class MyDialog: UIFrame
{
TagGroup createDLGtgs( Object self ){
TagGroup DLGtgs, DLGItems
DLGtgs = DLGCreateDialog( "Test Dialog", DLGItems )
DLGItems.DLGAddElement( DLGCreateLabel( "Test of Gatan DigitalMicrograph scripting" ) )
DLGItems.DLGAddElement( DLGCreatePushButton( "OK", "testOK" ) )
return DLGtgs
}
//
Object Init( Object self ){
self.super.Init( self.createDLGtgs() )
return self
}
//
Void testOK( Object self ){
OKDialog( "Hello!" )
}
}
//
Object DialogOBJ = Alloc( MyDialog ).Init()
String DialogName = "Test Dialog"
String toolName = "Test Tool"
RegisterScriptPalette( DialogOBJ, DialogName, toolName )
OpenGadgetPanel(toolName)
Be aware that the situation of floating palettes is different between GMS 2.x and GMS 3.x.
Essentially, registered floating palettes are phased out in GMS 3.x with the new UI. They are still supported to some extend, but not all functionality remains and some behavior is buggy. For GMS 3.x it is generally better to launch scripted dialogs as modal or modeless dialogs and no longer install them as palettes.
This answer here is for GMS 2.x (Tested with GMS 2.3.3)
You need to add position tags to the dialog-describing tagGroup. You do this in your example code of the question above by adding to the TagGroup createDLGtgs( Object self ) method:
TagGroup positionTgs = DLGBuildPositionFromApplication()
positionTgs.TagGroupSetTagAsString( "Width", "Medium" )
DLGtgs.DLGPosition( positionTgs )
The string values accepted are:
"Small" Like the left-hand tool palettes of GMS 2.x.
"Medium" Like the right-hand tool palettes of GMS 2.x
"Wide" extra wide
There are, however, a few addtional options for the position tags, but they will not apply to floating palettes, just to regular modeless dialogs. You can set the width and height of a window either with an absolute size value or let it be automatically determined by the size of the content. Here is an example:
class CMyClass : UIFrame
{
void InitAndShow( object self ) {
Taggroup DLG,DLGItems
DLG = DLGCreateDialog("Test",DLGItems)
DLGItems.DLGAddElement( DLGCreateLabel( "Just some long text for extra width" ))
DLGItems.DLGAddElement( DLGCreateLabel( "A second line" ))
TagGroup positionTgs = DLGBuildPositionFromApplication()
positionTgs.TagGroupSetTagAsTagGroup( "Width", DLGBuildAutoSize() )
positionTgs.TagGroupSetTagAsTagGroup( "Height", DLGBuildAbsoluteSize( 45, "pixel" ) )
DLG.DLGPosition( positionTgs )
self.super.Init( DLG ).Display("Test")
}
}
Alloc(CMyClass).InitAndShow()
In the above DLGBuildPositionFromApplication() sets the application window as the reference frame for the dialog. One can then define the position of the dialog left|center|right and top|center|bottom to that frame using the command DLGBuildRelativePosition() with -1|0|1 as respective parameter, like in the example:
class CMyClass : UIFrame
{
void InitAndShow( object self ) {
Taggroup DLG,DLGItems
DLG = DLGCreateDialog("Test",DLGItems)
DLGItems.DLGAddElement( DLGCreateLabel( "Just some long text for extra width" ))
DLGItems.DLGAddElement( DLGCreateLabel( "A second line" ))
TagGroup positionTgs = DLGBuildPositionFromApplication()
positionTgs.TagGroupSetTagAsTagGroup( "Width", DLGBuildAutoSize() )
positionTgs.TagGroupSetTagAsTagGroup( "Height", DLGBuildAutoSize() )
// Appear top-right
positionTgs.TagGroupSetTagAsTagGroup( "X", DLGBuildRelativePosition( "Inside", 1 ) )
positionTgs.TagGroupSetTagAsTagGroup( "Y", DLGBuildRelativePosition( "Inside", -1 ) )
DLG.DLGPosition( positionTgs )
self.super.Init( DLG ).Display("Test")
}
}
Alloc(CMyClass).InitAndShow()
The reference-frame does not have to be the application window, though. You can specify any wanted using DLGBuildPosition(), which allows using f.e. a window rect and then placing the dialog right of it. And with the reference-frame, one can also size the dialog window relative to that using DLGBuildMatchSize() as in the following example:
class CMyClass : UIFrame
{
void InitAndShow( object self, documentWindow win ) {
Taggroup DLG,DLGItems
DLG = DLGCreateDialog("Test",DLGItems)
DLGItems.DLGAddElement( DLGCreateLabel( "Just some long text for extra width" ))
DLGItems.DLGAddElement( DLGCreateLabel( "A second line" ))
number t,l,b,r
win.WindowGetFrameBounds(t,l,b,r)
TagGroup positionTgs = DLGBuildPosition(t,l,b,r)
positionTgs.TagGroupSetTagAsTagGroup( "Width", DLGBuildAutoSize() )
positionTgs.TagGroupSetTagAsTagGroup( "Height", DLGBuildMatchSize() )
// Appear center-right outside the window
positionTgs.TagGroupSetTagAsTagGroup( "X", DLGBuildRelativePosition( "Outside", 1 ) )
positionTgs.TagGroupSetTagAsTagGroup( "Y", DLGBuildRelativePosition( "Inside", 0 ) )
DLG.DLGPosition( positionTgs )
self.super.Init( DLG ).Display("Test")
}
}
image img := RealImage("test",4,100,100)
img.ShowImage()
documentWindow win = img.ImageGetOrCreateImageDocument().ImageDocumentGetWindow()
Alloc(CMyClass).InitAndShow(win)
Related
Drag a corner of an image in DM triggers the image content refreshed to the new window size. What is the DM script command to trigger that refreshing?
Specifically, I want to add a text below the image. So, I get the imageDocument as root component, add a text annotation below the image (the vertical position of the text is beyond the bottom border of the image). Without "imgDoc.ImageDocumentSwitchToPageMode()", the text is not visible. When I change the image size with mouse, the image content is updated and the text is shown. Looking for script doing that.
Thanks,
The command you're seeking is void ImageDocumentOptimizeWindow( ImageDocument imgDoc )
but there is a bit more to the story:
You can add annotations (which really are Components ) to any other Component. An ImageDisplay is also a specialization of a Component. Thus, you may either add text as an annotation on an ImageDisplay - or on an ImageDocument.
If the first, then the text will move & resize when you move/resize the ImageDisplay, but if you add it to the ImageDocument's root component, it is "side-ordered" to and independent of the ImageDisplay on the page.
Here is a code example for either:
image test := RealImage("Test",4,512,512)
test=icol
test.ShowImage()
imageDisplay disp = test.ImageGetImageDisplay(0)
imageDocument doc = test.ImageGetOrCreateImageDocument()
If ( TwoButtonDialog("Add as part of Image or Page?","Image","Page") )
{
component text1 = NewTextAnnotation( 10, 550, "Under image as part of ImageDisplay", 20)
disp.ComponentAddChildAtEnd(text1)
doc.ImageDocumentOptimizeWindow()
}
else
{
component text2 = NewTextAnnotation( 10, 550, "Under image as part of ImageDocument", 20)
doc.ImageDocumentEnsurePlacedOnPage()
doc.ImageDocumentSwitchToPageMode()
doc.ImageDocumentGetRootComponent().ComponentAddChildAtEnd(text2)
}
To better understand the structure of things, you may want to run a script like the following on an ImageDocument:
void RecursiveListChildren( component parent, string preFix )
{
number numChild = parent.ComponentCountChildren()
Result("\n"+preFix+" [Type "+parent.ComponentGetType()+"]")
for( Number i =0; i<numChild; i++ )
RecursiveListChildren( parent.ComponentGetChild(i), prefix + "..." )
}
component root = GetFrontImageDocument().ImageDocumentGetRootComponent()
ClearResults()
RecursiveListChildren(root,".")
For the two documents of the first script, this will give you:
. [Type 24]
.... [Type 20]
....... [Type 13]
and
. [Type 24]
.... [Type 20]
.... [Type 13]
For new GMS 2.x or higher, the answer is void ImageDocumentOptimizeWindow( ImageDocument imgDoc ). Not tested.
For older GMS, a generic solution is like this,
void resetImageDisplay(image img) {
imageDisplay imgDsp=img.ImageGetImageDisplay(0)
imageDocument imgDoc = img.ImageGetOrCreateImageDocument()
imgDoc.ImageDocumentSwitchToPageMode()
imgDoc.ImageDocumentSwitchToImageMode(imgDsp)
}
void main() {
image img:=exprSize(512,512, random())
img.showImage()
imageDisplay imgDsp=img.ImageGetImageDisplay(0)
component comp1=imgDsp.NewTextAnnotation(200, 540, "text 1", 14)
component comp2=imgDsp.NewTextAnnotation(200, 560, "text 2", 18)
comp1.ComponentSetForegroundColor(0,0,0)
comp2.ComponentSetForegroundColor(0,0,0)
imgDsp.ComponentAddChildAtEnd(comp1)
imgDsp.ComponentAddChildAtEnd(comp2)
img.resetImageDisplay()
//img.setWindowSize(1024,1024)
}
main()
I am building a Shiny App where users have to complete several mandatory questions in the form of radioButtons, numericInputs, and textInputs in order to generate an output. To highlight which questions still need to be completed, I would like the label text to initially be rendered as "red", but then switch to "black" once a value has been selected and/or inputted into the widgets.
library(shiny)
ui <- fluidPage(
sidebarPanel(
radioButtons("my_radio_button", tags$p("Choose an Option", style = "color:red;"), choices = c("Option 1", "Option 2"), selected = character(0)),
)
)
server <- function(input, output, session) {
observeEvent(input$val, {
x <- input$my_radio_button
if (x == "Option 1" | x == "Option 2") {
## MAKE "Choose an Option" LABEL TURN BLACK
}
})
}
shinyApp(ui, server)
I found this example on stack exchange (R shiny conditionally change numericInput background colour) where they conditionally changed the background colour of the widget, but I don't know enough about programming to modify it to change the label text instead.
Any help would be greatly appreciated. Thanks!
You can use shinyjs
add shinyjs::useShinyjs() to the beginning of ui
add an id to the label param of the radioButtons ui element. I've used r_label in the example below
Observe for changes to input$my_radio_button, which trigger calls to shinyjs::html()
Full code below:
library(shiny)
library(shinyjs)
ui <- fluidPage(
shinyjs::useShinyjs(),
sidebarPanel(
radioButtons("my_radio_button",
label=tags$p("Choose an Option", style = "color:red;", id="r_label"),
choices = c("Option 1", "Option 2"), selected = character(0)),
)
)
server <- function(input, output, session) {
observeEvent(input$my_radio_button, {
shinyjs::html("r_label", "<p style='color:black'>Choose an Option</p>")
})
}
shinyApp(ui, server)
I am trying to mimic the autosave function in GMS v3 so that I can use in version 1 and 2. I would like to first acknowledge that the main bulk of the script originates from Dr Bernhard Schaffer's "How to script... Digital Micrograph Scripting Handbook". I have modified it a bit, so that any new image recorded by the camera can be autosave into the file. However, I met some problems because if I decide to click on live-view image and move the image around, or using live-fft, the live view image or the FFT image will be saved as well. One of the ideas I have is to use the taggroup information such as the "Acquisition:Parameters:Parameter Set Name" because for live view or live-FFT, this would be either in search or focus mode. Another idea is to use the document ID e.g iDocID = idoc.ImageDocumentGETID() to locate the ID of the live image. However, i am clueless then how to use this information to exclude them from autosaving. Can anyone point to me how i can proceed with this script?
Below is the script
Class PeriodicAutoSave : Object
{
Number output
PeriodicAutoSave(Object self) Result("\n Object ID"+self.ScriptObjectGetID()+" created.")
~PeriodicAutoSave(Object self) Result("\n Object ID"+self.ScriptObjectGetID()+" destroyed")
Void Init2(Object self, Number op)
output=op
Void AutoSave_SaveAll(Object self)
{
String path, name, targettype, targettype1, area, mag, mode, search, result1
ImageDocument idoc
Number nr_idoc, count, index_i, index, iDocID, iDocID_search
path = "c:\\path\\"
name = "test"
targettype=="Gatan Format (*.dm4)"
targettype1 = "dm4"
If (output) Result("\n AutoSave...")
nr_idoc = CountImageDocuments()
For (count = 1; count<nr_idoc; count++)
{
idoc = GetImageDocument(count) //imagedocument
index = 1 // user decide the index to start with
index_i= nr_idoc - index
If (idoc.ImageDocumentIsDirty())
{
idoc = getfrontimagedocument()
iDocID = idoc.ImageDocumentGetID()
TagGroup tg = ImageGetTagGroup(idoc.ImageDocumentGetImage(0)) // cannot declare an 'img' for this line as it will prompt an error?
tg.TagGroupGetTagAsString("Microscope Info:Formatted Indicated Mag", mag)
Try{
{
idoc.ImageDocumentSavetoFile( "Gatan Format", path+index_i+"-"+name+"-"+mag+".dm4")
idoc.ImageDocumentSetName(index_i + "-"+name+"-"+mag+".dm4")
idoc.ImageDocumentClean()
}
If (Output) Result("\n\t saving: "+idoc.ImageDocumentGetCurrentFile())
}
Catch{
Result("\n image cannot be saved at the moment:" + GetExceptionString())
Break
}
Result("\ Continue autosave...")
}
}
}
}
Void LaunchAutoSave()
{
Object obj = Alloc(PeriodicAutoSave)
obj.Init2(2)
Number task_id = obj.AddMainThreadPeriodicTask("AutoSave_SaveALL",6)
//Sleep(10)
while(!shiftdown()) 1==2
RemoveMainThreadTask(task_id)
}
LaunchAutoSave()
thank you very much for your pointers! I have tried and it works very well with my script. as the 'TagGroupDoesTagExist' only refers to the taggroup, I modified further to include the tags I want to filter e.g "Search" or "Focus" and it seems to work well. The script that I modified to your existing ones is as below :
If (idoc.ImageDocumentIsDirty())
{
//now find out what is a filter condition and skip if it is true
skip = 0
TagGroup tg = idoc.ImageDocumentGetImage(0).ImageGetTagGroup()
tg.TagGroupGetTagAsString("Microscope Info:Formatted Indicated Mag", mag)
tg.TagGroupGetTagAsString("Acquisition:Parameters:Parameter Set Name", mode)
skip = tg.TagGroupDoesTagExist("Acquisition:Parameters:Parameter Set Name")
if(skip && (mode == "Search" || mode== "Focus")) continue
Your idea of filtering is a good one, but there is something strange with your for loop.
in
nr_idoc = CountImageDocuments()
For (count = 1; count<nr_idoc; count++)
{
idoc = GetImageDocument(count) //imagedocument
you iterate over all currently open imageDocuments (except the first one!?) and get them one by one, but then in
If (idoc.ImageDocumentIsDirty())
{
idoc = getfrontimagedocument()
you actually get the front-most (selected) document instead each time. Why are you doing this?
Why not go with:
number nr_idoc = CountImageDocuments()
for (number count = 0; count<nr_idoc; count++)
{
imagedocument idoc = GetImageDocument(count)
If (idoc.ImageDocumentIsDirty())
{
// now find out what is a filter condition and skip if it is true
number skip = 0
TagGroup tg = idoc.ImageDocumentGetImage(0).ImageGetTagGroup()
skip = tg.TagGroupDoesTagExist("Microscope Info:Formatted Indicated Mag")
if (skip) continue
// do saving
}
}
In DMS3.42 or later, I am wondering if we have a function that the user is allowed to select an image to operate. Basically, the function needs to run in the background and allows user interaction, something like "FloatingModelessDialog" in the previous version (it seems "FloatingModelessDialog" is not supported in DMS3.0).
Thank you!
FloatingModelessDialog is still supported in GMS 3:
// $BACKGROUND$
number sem = NewSemaphore()
FloatingModelessDialog("Message", "YES", sem )
Result("\n Do continue stuff...")
// hold and wait for the dialog
number OK
Try { GrabSemaphore(sem); OK = 1; }
catch { OK = 0; break; }
Result("\n Button pressed:" + OK )
However, this is only a workaround solution and requires the script to be executed on a background thread (or GMS appears to be frozen.)
A more complete approach would be to create a dialog and have its buttons drive the actions, like in the example below:
class MyActionDialog : UIFrame{
void Launch( object self ){
// Build dialog
TagGroup DLGtgs, DLGItems
DLGtgs = DLGCreateDialog( "My Dialog", DLGItems )
TagGroup Button1 = DLGCreatePushButton( "Act on front image", "OnButtonAct" )
TagGroup Button2 = DLGCreatePushButton( "Close", "OnButtonClose" )
DLGItems.DLGAddElement( Button1 )
DLGItems.DLGAddElement( Button2 )
DLGtgs.DLGTableLayout( 2, 1, 0 )
// Create and display the modeless Dialog
self.init( DLGtgs )
self.Display( "My Dialog" )
}
void OnButtonAct( object self ){
image front
if ( !GetFrontImage(front) )
Result( "\n Please select an image first." )
else
Result("\n Acting on image [" + front.ImageGetLabel() + "]....")
}
void OnButtonClose( object self ){
Result( "\n Closing dialog actions." )
self.close()
}
}
Alloc(MyActionDialog).Launch()
It is easy to save the false color image to BMP or JPG by using "save display as" function in menu. But it fails to save a image stack as the seperated images with the displaying color. "save display as" only saves the front image of the stack. I could not click hundreds of times to save the whole stack! But I do not find the corresponding script function in the dm manual. How to realize it?
The command you're looking for is ImageDisplayGetExportImage() and there is actually an
example script in the F1 help documentation of latest GMS for that:
But the command - same as the menu item - will only act on the actual display, so you still need to iterate over the displayed layers by script using ImageDisplaySetDisplayedLayers()
So your script will be something like the following example:
image test:=RealImage("Stack",4,100,100,10)
test=sin(icol/iwidth*Pi()*2) * cos(itheta*iplane)
test.ShowImage()
imagedisplay disp = test.ImageGetImageDisplay(0)
disp.ImageDisplaySetColorTableByName("Rainbow")
number nz = test.ImageGetDimensionSize(2)
for( number z=0; z<nz; z++){
disp.ImageDisplaySetDisplayedLayers(z,z)
imageDisplay newdisp
image asDisplayedRGB := disp.ImageDisplayGetExportImage( 7, newdisp )
asDisplayedRGB.SetName( test.GetName() + "_" + z )
asDisplayedRGB.ShowImage()
}
EGUPerformActionWithAllShownImages("arrange")
I hope this is what you are looking for. The shown script allows you to save all images in the current workspace to a directory. You can specify the format, the directory and the name pattern. (It got a bit longer than I expected):
TagGroup formats = NewTagGroup();
formats.TagGroupSetTagAsString("Gatan Format", "dm4");
formats.TagGroupSetTagAsString("Gatan 3 Format", "dm3");
formats.TagGroupSetTagAsString("GIF Format", "gif");
formats.TagGroupSetTagAsString("BMP Format", "bmp");
formats.TagGroupSetTagAsString("JPEG/JFIF Format", "jpg");
formats.TagGroupSetTagAsString("Enhanced Metafile Format", "emf");
formats.TagGroupSetTagAsString("TIFF Format", "tif");
formats.TagGroupSetTagAsString("PCX Format", "pcx");
class FormatDialog : UIFrame{
TagGroup format_select;
number FormatDialogGetSelectedFormat(object self){
if(format_select.TagGroupIsValid()){
return format_select.DLGGetValue();
}
else{
return -1;
}
}
object init(object self){
TagGroup dlg, dlg_items;
dlg = DLGCreateDialog("Select the format", dlg_items);
dlg_items.DLGAddElement(DLGCreateLabel("Please select the export format"));
format_select = DLGCreateChoice(0);
format_select.DLGIdentifier("format_select");
for(number i = 0; i < formats.TagGroupCountTags(); i++){
string text;
formats.TagGroupGetIndexedTagAsString(i, text);
text = formats.TagGroupGetTagLabel(i) + " (" + text + ")";
format_select.DLGAddChoiceItemEntry(text);
}
dlg_items.DLGAddElement(format_select);
self.super.init(dlg);
return self;
}
}
object format_dialog = alloc(FormatDialog).init();
if(format_dialog.pose()){
number format = format_dialog.FormatDialogGetSelectedFormat();
if(format < 0 || format >= formats.TagGroupCountTags()){
throw("Invalid format is selected");
}
string save_format = formats.TagGroupGetTagLabel(format);
string save_extension;
formats.TagGroupGetIndexedTagAsString(format, save_extension);
string save_dir;
if(GetDirectoryDialog("Please select the path to save to", GetApplicationDirectory("open_save", 1), save_dir)){
string save_name;
if(GetString("Please set the file name (without extension). A number will be added to the end automatically.", "file_", save_name)){
for(number i = 0; i < CountImageDocuments(); i++){
ImageDocument doc = GetImageDocument(i);
doc.ImageDocumentSaveToFile(save_format, PathConcatenate(save_dir, save_name + i + "." + save_extension));
}
OKDialog("Saved " + CountImageDocuments() + " files.");
}
}
}
Note that you can add that to DigitalMicrographs menus. Save the posted code as a file, then use File > Install Script File and add the script to any of your existing menus or a new menu.