2

I'm attempting to use PyQt to talk to an ActiveX COM object in Python. This method works in Python:

myobj.dynamicCall('SetAbsMovePos(int, double)', [0, 10])

However, I can't send arguments by reference. From the docs, the method I wish to call is:

int GetNumHWUnits (int lHWType, int& plNumUnits)

and the basic example code is:

QVariantList params = ...
int result = object->dynamicCall("GetNumHWUnits(int, int&)", params).toInt();

I presume the equivalent code in Python, using ctypes, should be:

num = ctypes.c_int()
myobj.dynamicCall('GetNumHWUnits(int, int&)', [6, ctypes.byref(num)])

but num is never updated.

What's the equivalent code or workaround in Python, and how can I send and later read an argument of type int& using Python?

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
101
  • 8,514
  • 6
  • 43
  • 69
  • The code looks correct according to the documentation: https://docs.python.org/2/library/ctypes.html#passing-pointers-or-passing-parameters-by-reference You know you need to use `num.value` to get the updated value? – Aaron Digulla Aug 19 '14 at 09:01
  • Yes, strictly it should be `num.value`, but that doesn't seem to be the root cause here. – 101 Aug 19 '14 at 10:49
  • Looking at the PyQt4 source code (method `dynamicCall()` in `qaxbase.sip`) this is what happens to the arguments, could it be the original references to the objects are lost? `QVariant *v = new QVariant(a1->at(i));` `PyObject *v_obj = sipConvertFromNewType(v, sipType_QVariant, NULL);` – 101 Aug 20 '14 at 03:00
  • Oh, `QVariant` is involed? See my answer. – Aaron Digulla Aug 20 '14 at 13:03
  • 3
    Does this answer your question? [Passing an integer by reference in Python](https://stackoverflow.com/questions/15148496/passing-an-integer-by-reference-in-python) – AMC Oct 06 '20 at 01:30

3 Answers3

2

You could try to pass a list with just one element:

def funct(a):
    a[0] = 4

b = [3]
funct(b)
print b[0]

Output: 4

jonie83
  • 1,136
  • 2
  • 17
  • 28
1

QVariant is involved:

    QVariant dynamicCall(const char *, QList<QVariant> & /GetWrapper/);
%MethodCode
        Py_BEGIN_ALLOW_THREADS
        sipRes = new QVariant(sipCpp->dynamicCall(a0, *a1));
        Py_END_ALLOW_THREADS

        // Update the input list with the (possibly) new values.
        for (SIP_SSIZE_T i = 0; i < PyList_GET_SIZE(a1Wrapper); ++i)
        {
            QVariant *v = new QVariant(a1->at(i));
            PyObject *v_obj = sipConvertFromNewType(v, sipType_QVariant, NULL);

In a nutshell, QVariant is a wrapper to pass a value by reference.

Which means by passing c_int, you have wrapped the value twice. Try to pass a real QVariant instead of c_int as second argument. Without this, the automatic type coercion in PyQt would take your c_int, wrap its value in a QVariant, call dynamicCall. During the call, the new QVariant is updated but there is no connection to the original c_int so your instance won't change.

This should work:

num = QVariant(0)
myobj.dynamicCall('GetNumHWUnits(int, int&)', [6, num])
print repr(num.value())
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • That looks a lot better but unfortunately doesn't work. I'm fairly sure the calls order is correct as the equivalent code works in LabVIEW. – 101 Aug 21 '14 at 00:33
1

Got the solution from the PyQt mailing list. A reference to the list of arguments is needed, as only that list is updated; the original QVariant objects never are. Seems fairly obvious in retrospect!

typ = QVariant(6)
num = QVariant(0)
args_list = [typ, num]

self.dynamicCall('GetNumHWUnits(int, int&)', args_list)

print 'Num of HW units =', args_list[1].toInt()[0]

A full code example is shown in this post.

Community
  • 1
  • 1
101
  • 8,514
  • 6
  • 43
  • 69
  • 1
    In PyQt5, the value of a `QVariant` can be obtained from its [`value()` method](https://www.riverbankcomputing.com/static/Docs/PyQt5/pyqt_qvariant.html). – ekhumoro Aug 01 '21 at 10:27