Yet still you decided to follow much worse alternatives, just because they were at hand
Because they are standard, well understood parts of Windows which everyone is guaranteed to have.
Even if the ActiveScripting versions of Phyton followed the standards set by the first party languages and worked perfectly, we'd still discourage using them because everyone who wanted to use a script written in another language would have to install extra stuff, worry about version conflicts, etc., which just isn't worth it for what is usually a small difference in syntax that shouldn't matter for the typical length and complexity of a script.
(Being able to call DLLs is a valid thing, of course, but very niche. If it came to it, we could provide that capability to JScript and VBScript via our own objects.)
I've tried the latest beta and got a different error
:Error at line 43, position 0
sV = Script.vars
^
Traceback (most recent call last):
File "<Script Block >", line 43, in OnInit
sV = Script.vars
...\win32comext\axscript\client\pyscript.py", line 105, in __getattr__
return getattr(self._scriptItem_.dispatchContainer,attr)
...\win32com\client\dynamic.py", line 527, in __getattr__
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeError: Script.vars
(0x80020009)
Is there a way to fix it?
FYI extracts of the code from the files mentioned:
Extract from pyscript
def __getattr__(self, attr):
# If a known subitem, return it.
try:
return self._scriptItem_.subItems[attr.lower()].attributeObject
except KeyError:
# Otherwise see if the dispatch can give it to us
if self._scriptItem_.dispatchContainer:
#Next Line is #105
return getattr(self._scriptItem_.dispatchContainer,attr)
raise AttributeError(attr)
Extract from dynamic.py
# Delegate to property maps/cached items
retEntry = None
if self._olerepr_ and self._oleobj_:
# first check general property map, then specific "put" map.
retEntry = self._olerepr_.propMap.get(attr)
if retEntry is None:
retEntry = self._olerepr_.propMapGet.get(attr)
# Not found so far - See what COM says.
if retEntry is None:
try:
if self.__LazyMap__(attr):
if attr in self._olerepr_.mapFuncs: return self._make_method_(attr)
retEntry = self._olerepr_.propMap.get(attr)
if retEntry is None:
retEntry = self._olerepr_.propMapGet.get(attr)
if retEntry is None:
retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
except pythoncom.ole_error:
pass # No prop by that name - retEntry remains None.
if not retEntry is None: # see if in my cache
try:
ret = self._mapCachedItems_[retEntry.dispid]
debug_attr_print ("Cached items has attribute!", ret)
return ret
except (KeyError, AttributeError):
debug_attr_print("Attribute %s not in cache" % attr)
# If we are still here, and have a retEntry, get the OLE item
if not retEntry is None:
invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
debug_attr_print("Getting property Id 0x%x from OLE object" % retEntry.dispid)
try:
ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
except pythoncom.com_error as details:
if details.hresult in ERRORS_BAD_CONTEXT:
# May be a method.
self._olerepr_.mapFuncs[attr] = retEntry
return self._make_method_(attr)
raise
debug_attr_print("OLE returned ", ret)
return self._get_good_object_(ret)
# no where else to look.
# Next line is 527
raise AttributeError("%s.%s" % (self._username_, attr))
I have no idea what it even means
Script object does not have vars attribute.Why do you think it should? What are you actually doing and how?
It's documented as having one here, at least:
https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Scripting/Script.htm
Join the club
They do, and Script.vars.get("ConfigSettingName")
allows me to use user config values (with this helper script), which is what I'm trying to do by pasting some of the plumbing from my JS scripts
SOA#1 (Polish for Standard Administrator's Answer #1 ) - WOMPC
"Script object does not have vars attribute" -> I meant that this is what the error means.
Hm, I think I haven't made any typos, but this does nothing on the latest beta (12.20.8, but also tried with the pre-script beta 12.20.5), so Doesn'tWOMPC .
I also don't really understand why it would do anything since in the column scripts the initData._FlagAsMethod('AddColumn')
works, i.e. the col.method
is invoked, only when columns are refershed
Is the initData._FlagAsMethod('AddCommand')
supposed to be called on every script refresh automatically (or is there some other event there that triggers it)? And if I add a direct call in the OnInit function OnTestCmd("")
, I get the same attribute error mentioned above
However, when I change Script.vars
to DOpus.vars
(but still I need to add an OnTestCmd("")
in the OnInit
function, I get this log
...TestPy.pys: script_cmd_data
...TestPy.pys: test
Test script I'm using
def OnInit(initData): #Init script
initData.name = "test";
initData.version = "1.0";
initData.copyright = "20";
# initData.url = "";
initData.desc = "test";
initData.default_enable = True;
initData.min_version = "12.5";
initData._FlagAsMethod('AddCommand')
cmd = initData.AddCommand()
cmd.name = 'TestCmd'
cmd.method = 'OnTestCmd'
cmd.desc = initData.desc
cmd.label = 'TestCmd'
def OnTestCmd(script_cmd_data):
test = Script.vars
for v in vars():
DOpus.Output(v)
Sorry, didn't understand it
OK, first, there is stupid mistake in my sample, it should be for "v in test". It does not print out anything then, but I guess this is expected (or not?) - there is no error though.
Second, OnInit is called automatically by DOpus on script refresh (that is when loading DOpus or when script file is saved - maybe there are some other occasions as well). You don't call it manually.
Third, the sample below works without errors, it does not print anything either from Script.vars or DOpus.vars, but I think this is expected. It does not fail silently either. DO version: Directory Opus Pro 12.20 Build 7394 x64
Note win32traceutil import. It allows your to print() in order to debug - you can see output in PythonWin> Tools> Trace Collector Debugging Tool
import win32traceutil
# Called by Directory Opus to initialize the script
def OnInit(initData):
initData.name = "test";
initData.version = "1.0";
initData.copyright = "(c) 2020 Łukasz";
# initData.url = "https://resource.dopus.com/viewforum.php?f=35";
initData.desc = "test";
initData.default_enable = True;
initData.min_version = "12.0";
initData._FlagAsMethod('AddCommand')
cmd = initData.AddCommand()
cmd.name = 'TestCmd'
cmd.method = 'OnTestCmd'
cmd.desc = initData.desc
cmd.label = 'TestCmd'
def OnTestCmd(script_cmd_data):
DOpus.Output(1)
test = Script.vars
DOpus.Output(2)
for v in test:
DOpus.Output(v)
DOpus.Output(3)
test = DOpus.vars
DOpus.Output(4)
for v in test:
DOpus.Output(v)
DOpus.Output(5)
I call it from this button:
<?xml version="1.0"?>
<button backcol="none" display="both" textcol="none">
<label>Nowy przycisk</label>
<icon1>#newcommand</icon1>
<function type="normal">
<instruction>TestCmd</instruction>
</function>
</button>
there is no error because the script simply doesn't execute that part. Once I add a call to that function OnTestCmd("")
, then it shows an error
Yeah, that part I know, I just had to connection to how that is relevant to another, not OnInit, function (now that you mention a button, it's clear — the button calls on this part)
Let me check your script later and see if it works...
Yeah, I always include it, that's where I see the vars created and such that I mentioned earlier, but didn't know about the fact taht print()
outputs there, thanks, that's very helpful in trying to understand how things work!!!
The script executes to the end, as you can see via numbers 1-5 printed to DOpus log panel.
How do you call OnTestCmd? It is a scripted DOpus command, so it should be called like a built-in command.
What is the actual code you run and how do you run it?
It does work now, thanks! I was just saying that previously I simply forgot I needed a button to run your script example
I've also already been able to do some successful testing:
- setting/getting
script.vars
- writing to
Script.config.Name1
and then reading from it - creating a Map to store config description, although had to confusingly use different types of parentheses (assign
myMap["MapKey"]
vs readmyMap("MapKey")
)
DOpus._FlagAsMethod('Create')
factory = DOpus.Create()
factory._FlagAsMethod('Map')
myMap = factory.Map()
myMap("MapKey") = "MapValue" # cannot assign to function call (0x80020009)
myMap["MapKey"] = "MapValue" # Works
DOpus.Output(myMap["MapKey"]) # in32com\client\dynamic.py", line 257, in __getitem__: raise TypeError("This object does not support enumeration")
DOpus.Output(myMap("MapKey")) # Works
So it looks very promising
Here is a post where some scripting surprises are explained (like using () for collections) Scripts (Python): Getting up to speed with Python + sample script
You may want to avoid DOpus pseudo-types (like Map), however I am not sure whether dict or list would be accepted by DOpus in place of them (you would need to do a conversion anyway).
Thanks, I actually have this post copied to my local guide for reference (I mean, that's where I got those _FlagAsMethod
s from), just haven't read it completely and thoroughly enough and didn't know the answer was already there
doesn't seem like it, I get objects of type 'dict' can not be converted to a COM VARIANT
You mean something like this?
dict = {'DEBUG': 'DbgDescription1', 'DEBUG2': 'DbgDescription2'}
myMap = factory.Map()
for i, k in enumerate(dict):
myMap[k] = dict[k]
inputData.config_desc = myMap
Is this still preferable over using the Map directly (also curious to know why)?
Regarding using python types vs DOpus, it's just a matter of convenience in fact, but it's easy to fall into DOpus types hole when you forget that they really are just necessary COM-related workarounds.
I will try to add some smart handling of DOpusFactory stuff in dopus_proxy.
By the way, even when I define some config initData.config.CfgName1
that are supposed to be also attached to the Script.config
, this fails:
for v in Script.config:
DOpus.Output(v)
...so I can't enumerate all my config Names and also can't get them like Script.config("CfgName1")
(I get COM Error: Invalid index) instead of Script.config.CfgName1
(I thought I could store config values in one loop instead of having to manually address each)
This is expected, right, due to these COM workarounds and such?
Can you paste the whole block of code? Hard to follow what you have on mind.
See comment #1, #2, #3 in OnTestCmd1 (this is simplified, I use Sv.set("hideAttr", Sc.hideAttr)
type of commands in a separate function that can be called in def OnScriptConfigChange(configChangeData):
to update script vars)
import win32traceutil
def OnInit(iData): #Init script
iData.name = "test";
iData.version = "1.0";
iData.copyright = "20";
# iData.url = "";
iData.desc = "test";
iData.default_enable = True;
iData.min_version = "12.5";
iData.config.DebugOutput = True;
iData.config.DEBUG = "dbgF";
iData.config.hideAttr = "a"
DOpus._FlagAsMethod('Create')
factory = DOpus.Create()
factory._FlagAsMethod('Map') # Map["Set"]= "" # Map("Get")
dict = {'DEBUG': 'DbgDescription', 'hideAttr': 'hideAttrDescription'}
myMap = factory.Map()
for i, k in enumerate(dict):
myMap[k] = dict[k]
iData.config_desc = myMap
iData._FlagAsMethod('AddCommand')
cmd = iData.AddCommand()
cmd.name = 'TestCmd1'
cmd.method = 'OnTestCmd1'
cmd.desc = iData.desc
cmd.label = 'TestCmd1'
def OnTestCmd1(scriptCmdData):
Sv, Sc = Script.vars, Script.config
#1# I can manually set script variables rom script configs
Sv.set("hideAttr", Sc.hideAttr)
# Sv.set("DEBUG", Sc.DEBUG)
# #... and later retrieve them
dbg(Sv.get("hideAttr"))
#2# But I can't enumerate Script.config
for cfg in Sc:
dbg(cfg)
# And I can't set in a way that would accept strings
Sv.set("hideAttr", Sc["hideAttr"]) #TypeError: This object does not support enumeration
Sv.set("hideAttr", Sc("hideAttr")) # COM Error: Invalid index.
#3# So I can't get iterate config properties to save them like so
for cfg in Sc:
Sv.set(cfg, Sc(cfg))
dbg(Sv.get("DEBUG")) # Fails
def dbg(text):
try:
if (Script):
pass
if (0 or Script.config.DebugOutput):
DOpus.Output(str(text))
except Exception as e:
DOpus.Output(str(type(e)))
print(e) # print(e.hresult, e.strerror, e.excepinfo, e.argerror)
DOpus.Output(str(e))
I don't have the beta DO, so I cannot play with Script, but some remarks:
- Script.config looks like a namespace, not collection, so no surprise it's not iterable. No surprise that you cannot access it with setitem/call notation either.
- To avoid all this conundrums, I would simply pickled/shelved my stuff into known location. As all this would probably remain in disk cache throughout all script execution, filesystem overhead of accessing data would be totally negligible.
- If you insist on Script API, put there a known item with index of all other stuff you attach to it.