Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-1788

overriden methods should allow threads during long Qt operations

    XMLWordPrintable

    Details

    • Type: Suggestion
    • Status: Closed
    • Priority: Not Evaluated
    • Resolution: Won't Do
    • Affects Version/s: 5.15.2, 6.2.2
    • Fix Version/s: None
    • Component/s: PySide
    • Labels:
      None
    • Platform/s:
      Linux/X11

      Description

      Hello, I'm facing a probleme related to 1176 and 803
      the overriden methods of my widgets are calling the super-class methods, but those do not allow threads despite being in native C++. Thus my background tasks as completely slowed down each time the native method is slow (performing rendering for instance).

      description of the problem

      What I'm doing is quite simple, working on a 3D view I have a widget doing rendering and navigation.
      Here is my initial implementation (which works well for everything but multithreading)

      # a widget class, doing rendering, and handling view navigation events
      class SceneView(QOpenGLWidget):
      	def __init__(self, ...):
      		super().__init__(parent)
      		...
      
      	# override event in order to get touchscreen events, mouse events, keyboard events ... all in the same callback
      	def event(self, evt):
      		if isinstance(evt, InputEvent):
      			self.inputEvent(evt)
      			if evt.isAccepted():	return True
      		
      		# call base class event handler
      		# it will take care of any other events, like resizing, rendering, ...
      		return super().event(evt)	# and this doesn't release the GIL, even while rendering
      
      	def paintGL(self):
      		self.makeCurrent()
      		self.render()
      		
      	def render(self):
      		# many openGL calls
      		# !! but no wait for openGL task termination:  this method is very fast then
      		...
      

      This works great until we need a thread performing anything frequently (either computations, but io tasks as well !) In my specific case, the background task is a socket communication

      my background thread has a loop that loops:

      • every 0.05s as planned when no Qt app is running
      • every 0.05s as planned when a Qt app is running with the SceneView idling
      • every 1s due to Qt slowing down, when the SceneView is frequently updated (a mouse movement for instance)

      So I agree, with the conclusion of 803 that most method should no release the GIL to keep up the GUI's performances, but the methods that are going to run for a while definitely should (in my case, QOpenGLWidget.event should)

      Here is the complete implementation of the widget: pymadcad/rendering

      Temporary fix

      There is a workaround for my specific case: having a different widget for receiving the events than for rendering. So we will not override the event method responsible for rendering.
      However this is more a trick than a solution: I'm obliged to create an invisible widget just to deal with event dispatching behaviors. Also It won't solve an issue with a slow method doing something else than rendering ...

      # a widget class, doing only rendering
      class SceneView(QOpenGLWidget):
      	def __init__(self, ...):
      		super().__init__(parent)
      		...
      		
      		self.handler = EmptyWidget(self)	# ugly trick: place an invisible widget to receive input events instead
      		
      	# NOTE: the base-class event() function is not overriden, so it does not hold the GIL, and other threads can live
      		
      	def resizeEvent(self, evt):
      		self.handler.resize(self.size())
      
      	def paintGL(self):
      		self.makeCurrent()
      		self.render()
      		
      	def render(self):
      		# many openGL calls
      		# !! but no wait for openGL task termination, so to call this method is very fast
      		...
      
      # an invisible widget
      class EmptyWidget(QWidget):
      	def __init__(self, parent):
      		super().__init__(parent)
      	
      	# override event in order to get touchscreen events, mouse events, keyboard events ... all in the same callback
      	# but this widget renders nothing, so to call it is very fast
      	def event(self, evt):
      		if isinstance(evt, InputEvent):
      			self.parent().inputEvent(evt)	# ugly trick: transmit events to the scene view
      			if evt.isAccepted():	return True
      		
      		return super().event(evt)	# and this doesn't release the GIL, even while rendering
      
      

      I noticed there is the same issue with PyQt5 5.15
       Could it be possible that in next versions of PySide, QOpenGLWidget.event and eventually QWidget.event would release the GIL ?

        Attachments

          Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

            Activity

              People

              Assignee:
              crmaurei Cristian Maureira-Fredes
              Reporter:
              jimy-byerley james byerley
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:

                  Gerrit Reviews

                  There are no open Gerrit changes