Create SAFEARRAY of Strings in Python - python

I'm trying to call a COM method that requires a SafeArray of Strings to be passed as reference, which is then filled up with the method results. This is the code in VBA, which works flawlessly:
dimr RC as New RAS41.HECRASController
RC.Project_Open "c:\myProj.prj"
dim numMessages as Long
dim messages() as String
RC.Compute_CurrentPlan( numMessages, messages())
Now, I'm trying to do the same from with Python 3.4, using the win32com module. However, I'm stuck at trying to create the second parameter with the correct type, which according to combrowse.py should be "Pointer SafeArray String".
This was my first attempt:
import win32com
RC = win32com.client.Dispatch("RAS41.HECRASController")
RC.Project_Open("c:\\myProj.prj")
numMessages = 0
messages = []
RC.Compute_CurrentPlan(numMessages, messages)
I also tried constructing that variable as
messages = win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_BSTR, [])
but it didn't work either. Error messages look like this:
Traceback (most recent call last):
File "<pyshell#101>", line 1, in <module>
print(o.Compute_CurrentPlan(1,b))
File "<COMObject RAS41.HECRASController>", line 3, in Compute_CurrentPlan
File "C:\Python34\lib\site-packages\win32com\client\dynamic.py", line 282, in _ApplyTypes_
result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
TypeError: Objects for SAFEARRAYS must be sequences (of sequences), or a buffer object.

Make sure that you python variables are in the right format (Long and String). Try to use something like the following to get the variable types in shape:
messages = ['']
RC.Compute_CurrentPlan(long(numMessages), messages)
To be more flexible with your program you should check the variable types prior to the win32 call.

I realize this is an old question, but I ran into this issue and wanted to share the resolution. I was having issues defining the type of data for the first two arguments, but simply setting them to None works and your number of messages and compute messages are reported (I checked by assigning text = hec.Compute_CurrentPlan(None, None, True) and then print(test)). The third argument is Blocking Mode, set to True, meaning that the RAS computation will complete before moving to the next line of code. I am using Python 3.10.4 and HEC-RAS version 6.3.
import win32com.client
hec = win32com.client.Dispatch('RAS630.HECRASController')
hec.Project_Open(r"C:\myproj.prj")
hec.ShowRAS()
hec.Compute_CurrentPlan(None, None, True)
hec.QuitRAS()

Related

MicroPython on Wemos D1 esp8266:- TypeError: can't convert Pin to int

Code:
from machine import Pin
from machine import ADC
from time import sleep_ms
x = ADC(Pin(4, Pin.IN))
y = ADC(Pin(5, Pin.IN))
x.atten(ADC.ATTN_11DB)
y.atten(ADC.ATTN_11DB)
while True:
x_val = x.read()
y_val = y.read()
print('Current position:{},{}'.format(x_val,y_val))
sleep_ms(300)
Error:
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
TypeError: can't convert Pin to int
I want to know what's my mistake.
I tried to concatenate str to int and int to str. It did not work.
The TypeError means you are using an object (a variable) of a different type then expected. The error tells you that, in this case, on line 5, an int is expected, but a Pin was used in your code.
Line 5 of your code is:
x = ADC(Pin(4, Pin.IN))
and it contains a Pin indeed.
Looking into the documentation of the machine.ADC class shows this should be fine:
class machine.ADC(id, *, sample_ns, atten)
Access the ADC associated with a source identified by id. This id may be an integer (usually specifying a channel number), a Pin object, or other value supported by the underlying machine.
However, has the library always supported this?
Browsing through older versions of the library shows this has not always been the case. Up to version 1.11 it expected an int:
class machine.ADC(id=0, *, bits=12)
Create an ADC object associated with the given pin.
So I assume you are using an old version of the library. To fix the problem you should either update your library to the latest version (at least v1.12), or you should read the documentation of the version you are using and update your code accordingly. For older versions, you should write
x = ADC(4)
y = ADC(5)

How to handle Python format errors in log message template?

I sometimes mess up the format in template strings for Python's logging module, for example:
import logging
logging.warning('%d', 1, 2) # BROKEN
In the console I can see a warning message (stack trace omitted):
--- Logging error ---
Traceback (most recent call last):
File ".../python3.7/logging/__init__.py", line 1034, in emit
msg = self.format(record)
File ".../python3.7/logging/__init__.py", line 880, in format
return fmt.format(record)
File ".../python3.7/logging/__init__.py", line 619, in format
record.message = record.getMessage()
File ".../python3.7/logging/__init__.py", line 380, in getMessage
msg = msg % self.args
TypeError: not all arguments converted during string formatting
Call stack:
...
Message: '%d'
Arguments: (1, 2)
However, neither my message nor this warning is sent to the log handler that would actually write it to a log file, send it to logstash etc.
I know that I can find these bugs with pylint using logging-too-few-args (E1206) and logging-too-many-args (E1205) however I still would prefer some kind of runtime fallback in case one of them slips through.
So unless one monitors stderr from outside of Python these kind of bugs are easy to overlook.
Is there a way to still log a message with a marker and the essentials parts, for example:
[LogBug] message='%d', arguments=(1, 2)
That way, the original information would still be preserved and one could periodically scan log files for the [LogBug] marker.
Could you use try/except, maybe? Possibly using re to extract the message and arguments from the exception message
import re
try:
logging.warning('%d', 1, 2)
except TypeError as e:
message = re.search(r'Message: ([^\n]*)[\n$]', e.message).group(1)
arguments = re.search(r'Arguments: ([^\n]*)[\n$]', e.message).group(1)
logging.error("[LogBug] message=%s, arguments=%s",message, arguments)
# if you want, you could re-raise this exception
If you do go with this solution you might also have to accommodate for error handling in case the TypeError isn't quite in the format you expect and thus the regex won't work and will raise an AttributeError when you try to call .group() on it.
There's no limit to how deep you could go with this, potentially, but if you make really really sure that this check works properly then it might be able to catch the others.
Alternatively, instead of trusting the logging package to do argument insertion, if you're using a recent version of python you could use format strings instead, which leaves no room for that kind of ambiguity:
i, j = 1, 2
logging.warning(f"{i}")

CATIA V5 Automation with Python Script

I'm a Python beginner and am attempting to do some automation in CATIA (Dassault Systemes CAD pacakge) with it, but I've run into an issue that I've been unable to solve despite searching extensively for a solution.
I'm trying to mimic the behavior of this VBA macro that was written within CATIAs native editor interface:
Sub CATMain()
Dim drawingDocument1 As DrawingDocument
Set drawingDocument1 = CATIA.ActiveDocument
Dim selection1 As Selection
Set selection1 = drawingDocument1.Selection
selection1.Search "CATDrwSearch.DrwDimension,all"
For i = 1 To selection1.Count
Dim Dimension1 As DrawingDimension
Set Dimension1 = selection1.Item(i).Value
Dim DimDimValue As DrawingDimValue
Set DimDimValue = Dimension1.GetValue
DimDimValue.SetFormatPrecision 1, 0.001
Next
selection1.Clear
End Sub
To do so I wrote this Python script:
import win32com.client
CATIA = win32com.client.Dispatch('CATIA.Application')
ad = CATIA.ActiveDocument
sel = ad.Selection
sel.Search("CATDrwSearch.DrwDimension,all")
for i in range(1, sel.Count2+1):
aDim = sel.Item2(i).Value
aDimValue = aDim.GetValue
aDimValue.SetFormatPrecision(1,0.001)
sel.Clear
Everything works except for the last operation within the for loop which returns the error:
Traceback (most recent call last):
<bound method DrawingDimension.GetValue of <win32com.gen_py.CATIA V5
DraftingInterfaces Object Library.DrawingDimension instance at 0x67582896>>
File "C:/...", line 15, in <module>
aDimValue.SetFormatPrecision(1,0.001)
AttributeError: 'function' object has no attribute 'SetFormatPrecision'
Note that I used makepy to early bind the COM object otherwise Python doesn't recognize it (returns COMObject [unknown]), but from what I understand that shouldn't impact the script behavior.
I haven't been able to troubleshoot the error successfully because everything I can find suggests the object should have the attribute SetFormatPrecision. I've tried a bunch of other attributes that it should have as well, and none of them work. Because I'm trying to operate on a COM object, I'm not aware of a way to get a comprehensive list of legal attributes, or a way to get any information on the type of object I have stored in aDimValue
I inspected the makepy output file and it does include a function definition for SetFormatPrecision so my guess is I have a syntax issue, but I'm at a loss for what it is.
I know it's a narrowly focused question, but I'm hoping somebody with knowledge of CATIA Object Libraries sees this. And although I don't expect it, if somebody wants to go the extra mile, there's documentation on CATIAs Object Libraries here:
http://catiadoc.free.fr/online/CAAScdBase/CAAScdAutomationHome.htm
Drafting > Drafting Reference > DrawingDimValue
to get to the specific object I think I'm working with in aDimValue
Any help is appreciated. Thanks.
aDim.GetValue returns the function object, rather than calling the function. Use aDim.GetValue(). Same with sel.Clear() on the last line.

OpenCV 3.1 cv2.stereoCalibrate TypeError: an integer is required

Recently I managed to compile newest opencv 3.1 with cuda support.
After some tinkering I properly converted most of my python code from 2.4.x to 3.1.x wihout any problems.
But when it came time to try out the stereCalibrate capability, the error occured:
Exception in thread Thread-5:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "./stereo_compute.py", line 245, in calibrate
flags)
TypeError: an integer is required
Here is how I call the function itself:
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
30, 1e-56)
flags = (cv2.CALIB_FIX_ASPECT_RATIO +
cv2.CALIB_ZERO_TANGENT_DIST +
cv2.CALIB_SAME_FOCAL_LENGTH)
(value,
self.np_calib_data['lmtx'], self.np_calib_data['ldist'],
self.np_calib_data['rmtx'], self.np_calib_data['rdist'],
self.np_calib_data['R'], self.np_calib_data['T'],
self.np_calib_data['E'], self.np_calib_data['F']
) = cv2.stereoCalibrate(
object_points,
l_image_points,
r_image_points,
(image_size[1], image_size[0],),
self.np_calib_data['lmtx'],
self.np_calib_data['ldist'],
self.np_calib_data['rmtx'],
self.np_calib_data['rdist'],
self.np_calib_data['R'],
self.np_calib_data['T'],
self.np_calib_data['E'],
self.np_calib_data['F'],
flags,
criteria)
Everything runs in a thread, that's why it's mentioned in the exception.
I can't get the correct set of parameters.
In addition the call worked for me under 2.4.x version with the same set of data.
Please help!
I have noticed that with Python bindings for OpenCV, if a function has a parameter with default value, say, None, you often can't explicitly use this parameter with its default value. This is quite against normal Python conventions and expected behaviour.
For example, function cv2.goodFeaturesToTrack has parameter blockSize with default value None, so you would expect that calling
cv2.goodFeaturesToTrack(image=img, maxCorners=10, qualityLevel=0.1, minDistance=10, mask=None, blockSize=None)
would be the same as
cv2.goodFeaturesToTrack(image=img, maxCorners=10, qualityLevel=0.1, minDistance=10, mask=None)
but in fact, first way of using this function will result in
TypeError: an integer is required
So, with OpenCV you have to either not provide and argument, or provide correct value (and the one which is default, according to Python function/method signature, may not be correct).
You will have to check C++ sources to find actual default values.

comtypes: in call_with_inout, ctypes TypeError: 'c_double' object is not iterable

Im working with Agilent IVI drivers in Python 2.7.9 and can't seem to get 'proven' code to work on a particular Windows 7 machine. It executes successfully on other machines.
While this issue seems rather limited to one instrument, it appears to be a broader Python issue, so I turn to Stack Overflow for help. Any help or insight would be great.
The following code
# Import the TLB
from comtypes.client import GetModule, CreateObject
GetModule('AgInfiniium.dll')
# Pull in the coefficients and classes, we'll need those
from comtypes.gen.AgilentInfiniiumLib import *
# Instantiate the driver
OScope = CreateObject('AgilentInfiniium.AgilentInfiniium')
# Make constants of our settings
Address = "TCPIP0::10.254.0.222::INSTR"
resetOScope = False
# Open a connection to the scope
OScope.Initialize(Address,False,resetOScope,'')
# Make a measurement
print OScope.Measurements.Item("Channel1").ReadWaveformMeasurement(
MeasFunction=AgilentInfiniiumMeasurementAmplitude, MaxTime=10)
yields the following error:
Traceback (most recent call last):
File "P:\Aperture\Validation\WMI_BGA_Board\TestMatrixCode\scopeTest2.py", line 29, in <module>
print OScope.Measurements.Item("Channel1").ReadWaveformMeasurement(MeasFunction=AgilentInfiniiumMeasurementAmplitude ,MaxTime=10)
File "C:\Python27\lib\site-packages\comtypes-1.1.0-py2.7.egg\comtypes\__init__.py", line 656, in call_with_inout
rescode = list(rescode)
TypeError: 'c_double' object is not iterable
In my limited debugging attempts, I have seen that this call_with_inout
function tries to convert my Python arguments into arguments for the following C++ function:
public void ReadWaveformMeasurement(
AgilentInfiniiumMeasurementEnum MeasFunction,
AgilentInfiniiumTimeOutEnum MaxTime,
ref double pMeasurement)
It's creating some kind of variable for the pMeasurement pointer that ends up being type c_double, and then complains that it's not iterable.
At this point, this seems like it's local to this machine. I've gone to the extent of uninstalling Python, reinstalling the Agilent driver, and trying two versions of comtypes (1.1.0 and 1.1.1). Yet the problem persists. Any ideas? Thanks.

Categories