Any function in DMS3.0 or later that runs in the background and allow user interaction (like FloatingModelessDialog) - dm-script

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()

Related

How to exclude certain images from autosave in Gatan Digital Micrograph (GMS) in DM-script

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
}
}

How to use dm script to realize the "save display as" function?

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.

How to change the width of a floating pallete in DM scripting

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)

Editor.GetEntity does not wait for user input (click)

I have two dwg files: PID.dwg & 3D.dwg
The use case is to run a function on PID.dwg and then on 3D.dwg -- particularly in this order.
The commands used in SendCommand below are from a separate DLL file that I load using NETLOAD prior to this function's execution.
Dim app As AcadApplication = CType(Application.AcadApplication, AcadApplication)
' Ctype( Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication,
' Autodesk.AutoCAD.Interop.AcadApplication )
If isPidAnd3dOpened() Then
' Activate PID document
app.ActiveDocument = acDocPid
'acDocPid.Activate()
acDocPid.SendCommand("DOSOMETHINGONPID" & vbCrLf)
' Activate 3D document
app.ActiveDocument = acDoc3d
'acDoc3d.Activate()
acDoc3d.SendCommand("DOSOMETHINGON3D" & vbCrLf)
End If
The function of "DOSOMETINGON3D" requires and input from the user using Editor.GetEntity.
However, when acDoc3d.SendCommand("DOSOMETHINGON3D" & vbCrLf) is executed, it does not pause to wait for user input.
What am I missing?
Probably You have to wait until the command DOSOMETHINGONPID is finished.
In ARX it would be something like this:
CString activeCMDName = _T("DOSOMETHINGONPID");
bool EOL = false;
while (!EOL)
{
CString cmds = Variable::Get(_T("CMDNAMES") );
if (cmds.Find( activeCMDName ) > 0 ) {
Command::Wait();
}
else {
EOL = true;
}
}
where
CString Variable::Get( CString name )
{
CString OutVal;
resbuf rb ;
acedGetVar(name, &rb);
OutVal.SetString(rb.resval.rstring);
acutDelString(rb.resval.rstring);
return OutVal ;
}
void Command::Wait()
{
ResBuf rb;
rb.Add(RTSTR , _T("\\"));
int ret = acedCmd(rb.GetFirst());
}
Sorry, I don't have this code in .net. Hope You will handle this.
First answer is correct, SendCommand cannot handle asynchronous commands. Here is a suggested solution in .Net:
//Create AutoCAD instance, then...
acadApp.ActiveDocument.SendCommand("(command \"NETLOAD\""+#"""C:\\acad\\networkdll\\SecondAssembly.dll"") ");
acadApp.ActiveDocument.SendCommand("#MYCOMMAND 0 ");
//Register EndCommand handler.
_DAcadApplicationEvents_EndCommandEventHandler handler = new
_DAcadApplicationEvents_EndCommandEventHandler(CommandEnded);
acadApp.EndCommand += handler;
waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
waitHandle.WaitOne();
acadApp.EndCommand -= handler;
//Close the startup drawing (this requires waiting # SendCommand) because
//Drawing will cause a COMException otherwise. 'Drawing is busy'
//Mostly likely since the ActiceDocument is the startup drawing.
Event Handler:
public void CommandEnded(string globalCommandName)
{
System.Windows.MessageBox.Show(globalCommandName + " just ended.");
waitHandle.Set();
}

How to show the custom PDF template while clicking the button

I want to show the PDF Template in new window while clicking the button in Sales Order. I created the button in sales order process using user event script. after that i'm unable to proceed it. It is possible to show the custom PDF template in new window while clicking the sales order?
My CODE:
USER EVENT SCRIPT:
// creating button in user event script before load event in view mode
unction userEventBeforeLoad(type, form, request){
if(type == 'view'){
var internalId = nlapiGetRecordId();
if (internalId != null) {
var createPdfUrl = nlapiResolveURL('SUITELET', 'customscript_back0rdered_itm_pdf', 'customdeploy_backord_itm_pdf_dep', false);
createPdfUrl += '&id=' + internalId;
//---add a button and call suitelet on click that button and it will open a new window
var addButton = form.addButton('custpage_printpdf', 'Print PDF', "window.open('" + createPdfUrl + "');");
}
else {
nlapiLogExecution('DEBUG', 'Error', 'Internaal id of the record is null');
}
}
}
SUITELET SCRIPT:
function suitelet(request, response){
var xml = "<?xml version=\"1.0\"?>\n<!DOCTYPE pdf PUBLIC \"-//big.faceless.org//report\" \"report-1.1.dtd\">\n";
xml += "<pdf>";
xml += "<head><macrolist><macro id=\"myfooter\"><p align=\"center\"><pagenumber /></p></macro></macrolist></head>";
xml += "<body size= \"A4\" footer=\"myfooter\" footer-height=\"0.5in\">";
var record = request.getParameter('internalId');
xml +="record"; //Add values(in string format) what you want to show in pdf
xml += "</body></pdf>";
var file = nlapiXMLToPDF(xml);
response.setContentType('PDF', 'Print.pdf ', 'inline');
response.write(file.getValue());
}
thanks in advance
The way I did it recently:
User Event Adds the Button that calls a suitelet (window.open('suitelet URL'))
Suitelet Renders the custom template
You can do the rendering like this insise a Suitelet (params: request, response), the custscript_pdf_template points to an html file on the cabinet using the NetSuite Advanced HTML syntax
var template = nlapiGetContext().getSetting('SCRIPT', 'custscript_pdf_template');
var purchaseOrder = nlapiLoadRecord('purchaseorder', tranId);
var xmlTemplate = nlapiLoadFile(template);
var renderer = nlapiCreateTemplateRenderer();
var file;
xmlTemplate = xmlTemplate.getValue();
renderer.setTemplate(xmlTemplate);
renderer.addRecord('record', purchaseOrder);
xmlTemplate = renderer.renderToString();
file = nlapiXMLToPDF(xmlTemplate);
resObj = file.getValue();
response.setContentType('PDF', 'printOut.pdf', 'inline');
response.write(resObj)