fix RemoteSpeedTest shutdown errors

fix BufferError: cannot close exported pointers exist
for some reason, even though the ctypes object falls out of function
scope, it causes a lingering reference.
in any case, the use of ctypes is no longer necessary.

Don't change temporary files mid-way for Darwin
This fixes the CI issues on Darwin.

close the remote process on app shutdown.
This commit is contained in:
KIU Shueng Chuan 2021-01-26 22:00:19 +08:00
parent b54aa3914d
commit 2f4ceee1ce
3 changed files with 13 additions and 23 deletions

View File

@ -24,6 +24,8 @@ pg.setConfigOptions(antialias=True) ## this will be expensive for the local plo
view.pg.setConfigOptions(antialias=True) ## prettier plots at no cost to the main process! view.pg.setConfigOptions(antialias=True) ## prettier plots at no cost to the main process!
view.setWindowTitle('pyqtgraph example: RemoteSpeedTest') view.setWindowTitle('pyqtgraph example: RemoteSpeedTest')
app.aboutToQuit.connect(view.close)
label = QtGui.QLabel() label = QtGui.QLabel()
rcheck = QtGui.QCheckBox('plot remote') rcheck = QtGui.QCheckBox('plot remote')
rcheck.setChecked(True) rcheck.setChecked(True)

View File

@ -70,10 +70,6 @@ conditionalExamples = {
False, False,
reason="Test is being problematic on CI machines" reason="Test is being problematic on CI machines"
), ),
"RemoteGraphicsView.py": exceptionCondition(
not(platform.system() == "Darwin"),
reason="FileNotFoundError for pyqtgraph_shmem_* file"
),
"ProgressDialog.py": exceptionCondition( "ProgressDialog.py": exceptionCondition(
not(platform.system() == "Linux"), not(platform.system() == "Linux"),
reason="QXcbConnection: XCB error" reason="QXcbConnection: XCB error"

View File

@ -5,7 +5,7 @@ from .. import multiprocess as mp
from .GraphicsView import GraphicsView from .GraphicsView import GraphicsView
from .. import CONFIG_OPTIONS from .. import CONFIG_OPTIONS
import numpy as np import numpy as np
import mmap, tempfile, ctypes, atexit, sys, random import mmap, tempfile, os, atexit, sys, random
__all__ = ['RemoteGraphicsView'] __all__ = ['RemoteGraphicsView']
@ -78,10 +78,6 @@ class RemoteGraphicsView(QtGui.QWidget):
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
self.shmtag = newfile ## on windows, we create a new tag for every resize self.shmtag = newfile ## on windows, we create a new tag for every resize
self.shm = mmap.mmap(-1, size, self.shmtag) ## can't use tmpfile on windows because the file can only be opened once. self.shm = mmap.mmap(-1, size, self.shmtag) ## can't use tmpfile on windows because the file can only be opened once.
elif sys.platform == 'darwin':
self.shmFile.close()
self.shmFile = open(self._view.shmFileName(), 'r')
self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_READ)
else: else:
self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_READ) self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_READ)
self.shm.seek(0) self.shm.seek(0)
@ -159,6 +155,7 @@ class RemoteGraphicsView(QtGui.QWidget):
def close(self): def close(self):
"""Close the remote process. After this call, the widget will no longer be updated.""" """Close the remote process. After this call, the widget will no longer be updated."""
self._view.sceneRendered.disconnect()
self._proc.close() self._proc.close()
@ -224,25 +221,20 @@ class Renderer(GraphicsView):
self.shm = mmap.mmap(-1, size, self.shmtag) self.shm = mmap.mmap(-1, size, self.shmtag)
elif sys.platform == 'darwin': elif sys.platform == 'darwin':
self.shm.close() self.shm.close()
self.shmFile.close() fd = self.shmFile.fileno()
self.shmFile = tempfile.NamedTemporaryFile(prefix='pyqtgraph_shmem_') os.ftruncate(fd, size + 1)
self.shmFile.write(b'\x00' * (size + 1)) self.shm = mmap.mmap(fd, size, mmap.MAP_SHARED, mmap.PROT_WRITE)
self.shmFile.flush()
self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_WRITE)
else: else:
self.shm.resize(size) self.shm.resize(size)
## render the scene directly to shared memory ## render the scene directly to shared memory
ctypes_obj = ctypes.c_char.from_buffer(self.shm, 0) if QT_LIB == 'PyQt5':
if QT_LIB.startswith('PySide'): img_ptr = int(sip.voidptr(self.shm))
# PySide2, PySide6 elif QT_LIB == 'PyQt6':
img_ptr = ctypes_obj img_ptr = sip.voidptr(self.shm)
else: else:
# PyQt5, PyQt6 # PySide2, PySide6
img_ptr = sip.voidptr(ctypes.addressof(ctypes_obj)) img_ptr = self.shm
if QT_LIB == 'PyQt6':
img_ptr.setsize(size)
self.img = QtGui.QImage(img_ptr, self.width(), self.height(), QtGui.QImage.Format_ARGB32) self.img = QtGui.QImage(img_ptr, self.width(), self.height(), QtGui.QImage.Format_ARGB32)