Kev1n91
Kev1n91

Reputation: 3693

Pythonic way to copy list to a sip.voidptr from QSharedMemory

I am using PyQT5 and the QSharedMemory class. I am creating a shared memory which can hold up 6 1-byte elements. To copy these elments in the shared memory array I am looping over the elments from the python list like the following snippet:

f = shared_mem.data()
k = f.asarray()
memtocopy = [0,1,2,3,4,5]
for i in range(0,len(memtocopy)):
    k[i]  = memtocopy[i]
shared_mem.unlock()

Which seems very unpythonic and boilerplate-code like. I am wondering if there is a more suitable way of achieving the same result?

When using

k[:] = memtocopy

or:

k[:] = np.asarray(memtocopy,np.uint8)

It will fail with the error message:

TypeError: can only assign another array of unsigned char to the slice

The whole test code for reproducing looks like the following:

from PyQt5 import QtCore 

# Create shared memory and attach it
shared_mem = QtCore.QSharedMemory()
shared_mem.setNativeKey("test")
shared_mem.create(4*6)
shared_mem.attach()


# Fill in 
shared_mem.lock()
f = shared_mem.data()
k = f.asarray()

memtocopy = [0,1,2,3,4,5]

# Loop in question
for i in range(0,len(memtocopy)):
    k[i]  = memtocopy[i]

shared_mem.unlock()



# Read out
shared_mem.lock()
f1 = shared_mem.data()
k1 = f1.asarray()
shared_mem.unlock()

# Test results
if k1[0] == memtocopy[0]:
    print("success!")
else:
    print("fail!")

Upvotes: 2

Views: 968

Answers (2)

ekhumoro
ekhumoro

Reputation: 120798

Here's a simpler approach using struct and memoryview that reads and writes the data with a couple of one-liners :

import struct
from PyQt5 import QtCore

shared_mem = QtCore.QSharedMemory()
shared_mem.setNativeKey("test")
shared_mem.create(4*6)
shared_mem.attach()

memtocopy = [0,1,2,3,4,5]

try:

    # Fill in 
    shared_mem.lock()
    shared_mem.data()[:] = memoryview(struct.pack('=6i', *memtocopy))
    shared_mem.unlock()

    # Read out
    shared_mem.lock()
    # python3
    k = memoryview(shared_mem.data()).cast('i')
    # python2
    # k = struct.unpack('=6i', memoryview(shared_mem.data()))
    shared_mem.unlock()

    if k[3] == memtocopy[3]:
        print("success!")
    else:
        print("fail!")

finally:

    shared_mem.detach()

Upvotes: 2

Yann Vernier
Yann Vernier

Reputation: 15887

After a bit of fiddling about, I managed to produce one combination that functioned for me:

a = array.array('i', range(6))
f[:] = buffer(a)
b = array.array('i')
b.fromstring(buffer(f))

This relies on the buffer protocol for reading both ways. It is likely you can use the array directly with your k, and fromstring has been renamed to frombytes in later versions.

In Python 3.4, this worked:

a = array.array('i', range(6))
f[:] = memoryview(a).cast('B')
b = array.array('i')
b.frombytes(memoryview(f))

memoryview replaced buffer, but knew the elements were 4 bytes large, so it required an additional cast.

Upvotes: 1

Related Questions