Skip to content
This repository has been archived by the owner on Feb 1, 2023. It is now read-only.

Commit

Permalink
Updating to version 1.1.0, adding version and separate thread option
Browse files Browse the repository at this point in the history
  • Loading branch information
miile7 committed Sep 9, 2020
1 parent 97098cf commit e528ef4
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 24 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
Version 1.0.3
=============

- Adding `__version__` to module
- Adding VERSION file
- Adding `separate_thread` option to allow execution of dm-script in a separate dm-script thread

Version 1.0.2
=============

Expand Down
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.1.0
148 changes: 148 additions & 0 deletions example/example_separate_thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""This is an example file of how to use the `execdmscript` module."""

# = = = = = = = = = = = = = = IGNORE START = = = = = = = = = = = = = = = = = =
# Ignore the following code until the second line
#
# This code is for getting the __file__. GMS does not set the __file__ variable
# on running scripts. That causes the example file not to run properly except
# the execdmscript module is in one of the GMS plugin directories.
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
try:
import DigitalMicrograph as DM
in_digital_micrograph = True
except ImportError:
in_digital_micrograph = False

file_is_missing = False
try:
if __file__ == "" or __file__ == None:
file_is_missing = True
except NameError:
file_is_missing = True

if in_digital_micrograph and file_is_missing:
# the name of the tag is used, this is deleted so it shouldn't matter anyway
file_tag_name = "__python__file__"
# the dm-script to execute, double curly brackets are used because of the
# python format function
script = ("\n".join((
"DocumentWindow win = GetDocumentWindow(0);",
"if(win.WindowIsvalid()){{",
"if(win.WindowIsLinkedToFile()){{",
"TagGroup tg = GetPersistentTagGroup();",
"if(!tg.TagGroupDoesTagExist(\"{tag_name}\")){{",
"number index = tg.TagGroupCreateNewLabeledTag(\"{tag_name}\");",
"tg.TagGroupSetIndexedTagAsString(index, win.WindowGetCurrentFile());",
"}}",
"else{{",
"tg.TagGroupSetTagAsString(\"{tag_name}\", win.WindowGetCurrentFile());",
"}}",
"}}",
"}}"
))).format(tag_name=file_tag_name)

# execute the dm script
DM.ExecuteScriptString(script)

# read from the global tags to get the value to the python script
global_tags = DM.GetPersistentTagGroup()
if global_tags.IsValid():
s, __file__ = global_tags.GetTagAsString(file_tag_name);
if s:
# delete the created tag again
DM.ExecuteScriptString(
"GetPersistentTagGroup()." +
"TagGroupDeleteTagWithLabel(\"{}\");".format(file_tag_name)
)
else:
del __file__

try:
__file__
except NameError:
# set a default if the __file__ could not be received
__file__ = ""

# = = = = = = = = = = = = = = = IGNORE END = = = = = = = = = = = = = = = = = =

# The start of the example file

import os
import sys
import time
import threading

import DigitalMicrograph as DM

if __file__ != "":
# add the parent directory to the system path so the execdmscript file
# can be imported
base_path = str(os.path.dirname(os.path.dirname(__file__)))

if base_path not in sys.path:
sys.path.insert(0, base_path)

from execdmscript import exec_dmscript

try:
def do_something():
for i in range(100):
i += 1
print("Progress: {}".format(i))
DM.GetPersistentTagGroup().SetTagAsLong("__progress", i);
time.sleep(0.05)

thread = threading.Thread(target=do_something)
thread.start()

dialog_script = """
number update_task;
class ProgressDialog : UIFrame{
void updateDialog(object self){
number progress;
if(GetPersistentTagGroup().TagGroupGetTagAsLong("__progress", progress)){
self.DLGSetProgress("progress_bar", progress / 100);
self.validateView();
}
}
object init(object self){
TagGroup Dialog = DLGCreateDialog("Dialog");
TagGroup progress_bar = DLGCreateProgressBar("progress_bar");
progress_bar.DLGInternalpadding(150, 0);
Dialog.DLGAddElement(progress_bar);
update_task = AddMainThreadPeriodicTask(self, "updateDialog", 0.1);
self.super.init(Dialog);
return self;
}
}
// do not move this in the thread part, this will not work anymore
object progress_dlg = alloc(ProgressDialog).init();
"""
exec_script = """
if(!GetPersistentTagGroup().TagGroupDoesTagExist("__progress")){
GetPersistentTagGroup().TagGroupCreateNewLabeledTag("__progress");
GetPersistentTagGroup().TagGroupSetTagAsLong("__progress", 0);
}
progress_dlg.pose();
if(GetPersistentTagGroup().TagGroupDoesTagExist("__progress")){
GetPersistentTagGroup().TagGroupDeleteTagWithLabel("__progress");
}
RemoveMainThreadTask(update_task);
"""
with exec_dmscript(dialog_script, separate_thread=(exec_script, ), debug=False) as script:
pass

thread.join()
except Exception as e:
# dm-script error messages are very bad, use this for getting the error text and the
# correct traceback
print("Exception: ", e)
import traceback
traceback.print_exc()
4 changes: 3 additions & 1 deletion execdmscript/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@
from .execdmscript import exec_dmscript
from .execdmscript import get_dm_type
from .execdmscript import get_python_type
from .execdmscript import DMScriptWrapper
from .execdmscript import DMScriptWrapper

__version__ = "1.1.0"
90 changes: 68 additions & 22 deletions execdmscript/execdmscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,21 +583,18 @@ def getExecDMScriptCode(self) -> str:
dmscript, code, source, script, startpos
)

# the code for the readvars
code = self.getSyncDMCode()
dmscript, startpos = self._addCode(
dmscript, code, "<readvars>", self.getSyncDMCode, startpos
)

wait_for_signals = []
# execute in a separate thread
if isinstance(self.separate_thread, collections.Sequence):
code = self.getSeparateThreadStartCode()
dmscript, startpos = self._addCode(
dmscript, code, "<prepare separate thread>",
self.getSeparateThreadStartCode, startpos
)

for i, (kind, script) in enumerate(DMScriptWrapper.normalizeScripts(self.separate_thread)):
wait_for_signals.append(i)

code = self.getSeparateThreadStartCode(i)
dmscript, startpos = self._addCode(
dmscript, code, "<prepare separate thread>",
self.getSeparateThreadStartCode, startpos
)

if isinstance(kind, str):
kind = kind.lower()

Expand All @@ -618,11 +615,24 @@ def getExecDMScriptCode(self) -> str:
dmscript, code, source, script, startpos
)

code = self.getSeparateThreadEndCode()
dmscript, startpos = self._addCode(
dmscript, code, "<prepare separate thread>",
self.getSeparateThreadEndCode, startpos
)
code = self.getSeparateThreadEndCode(i)
dmscript, startpos = self._addCode(
dmscript, code, "<prepare separate thread>",
self.getSeparateThreadEndCode, startpos
)

# for i in wait_for_signals:
# code = self.getSeparateThreadWaitCode(i)
# dmscript, startpos = self._addCode(
# dmscript, code, "<wait for signal {}>".format(i),
# self.getSeparateThreadWaitCode, startpos
# )

# the code for the readvars
code = self.getSyncDMCode()
dmscript, startpos = self._addCode(
dmscript, code, "<readvars>", self.getSyncDMCode, startpos
)

return "\n".join(dmscript)

Expand Down Expand Up @@ -676,7 +686,7 @@ def _addCode(self, dmscript: list, code: typing.Union[list, tuple, str],

return dmscript, startpos + code_lines + 1

def getSeparateThreadStartCode(self) -> str:
def getSeparateThreadStartCode(self, index: int) -> str:
"""Get the dm-script code for executing the complete script in a
separate thread.
Expand All @@ -685,18 +695,25 @@ def getSeparateThreadStartCode(self) -> str:
code and end it with the code returned by
`DMScriptWrapper.getSeparateThreadEndCode()`.
Parameters
----------
index : int
The thread index
Returns
-------
str
The code to append to the dm-script code
"""

return "\n".join((
"class ExecDMScriptThread{} : Thread{{".format(self._creation_time_id),
"object thread_cancel_signal{}_{} = NewCancelSignal();".format(self._creation_time_id, index),
"object thread_done_signal{}_{} = NewSignal(0);".format(self._creation_time_id, index),
"class ExecDMScriptThread{}_{} : Thread{{".format(self._creation_time_id, index),
"void RunThread(object self){"
))

def getSeparateThreadEndCode(self) -> str:
def getSeparateThreadEndCode(self, index: int) -> str:
"""Get the dm-script code for executing the complete script in a
separate thread.
Expand All @@ -705,17 +722,46 @@ def getSeparateThreadEndCode(self) -> str:
code and end it with the code returned by
`DMScriptWrapper.getSeparateThreadEndCode()`.
Parameters
----------
index : int
The thread index
Returns
-------
str
The code to append to the dm-script code
"""

return "\n".join((
"// inform that the thread is done now",
"thread_done_signal{}_{}.setSignal();".format(self._creation_time_id, index),
"}", # end ExecDMScriptThread<id>::RunThread()
"}", # end ExecDMScriptThread<id> class
"alloc(ExecDMScriptThread{}).StartThread();".format(
self._creation_time_id
"alloc(ExecDMScriptThread{}_{}).StartThread();".format(
self._creation_time_id, index
)
))

def getSeparateThreadWaitCode(self, index: int) -> str:
"""Get the dm-script code for waiting to complete all separately
started threads.
Parameters
----------
index : int
The thread index
Returns
-------
str
The code to append to the dm-script code
"""

return "\n".join((
"// wait for the thread {}".format(index),
"thread_done_signal{id}_{i}.WaitOnSignal(infinity(), thread_cancel_signal{id}_{i});".format(
id=self._creation_time_id, i=index
)
))

Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

with open("README.md", "r") as fh:
long_description = fh.read()
with open("VERSION", "r") as fh:
version = fh.read()

setuptools.setup(
name="execdmscript",
version="1.0.2",
version=version,
author="miile7",
author_email="[email protected]",
description=("A python module for executing DM-Script from python in the " +
Expand Down

0 comments on commit e528ef4

Please sign in to comment.