1

My OptionsViz class is working on its own. However, when I throw in the asyncio stuff it doesn't show anything updating. The loop is needed for code that I have removed for brevity, so please don't throw that away.

import sys
import asyncio
from qasync import QEventLoop
from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget

class OptionViz:
  def __init__(self, app):
    self.app = app
    self.p = pg.plot()
    self.p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
    self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
    self.p.setLabel("bottom", "Index", units="B")
    self.curve = self.p.plot()

    self.data = np.random.normal(size=(50,5000))
    self.ptr = 0
    self.lastTime = time()
    self.fps = None

    timer = QtCore.QTimer()
    timer.timeout.connect(self.update)
    timer.start(0)

  def update(self):
      self.curve.setData(self.data[self.ptr%10])
      self.ptr += 1
      now = time()
      dt = now - self.lastTime
      self.lastTime = now
      if self.fps is None:
          fps = 1.0/dt
      else:
          s = np.clip(dt*3., 0, 1)
          self.fps = self.fps * (1-s) + (1.0/dt) * s
      self.p.setTitle('%0.2f fps' % fps)
      self.app.processEvents()  ## force complete redraw for every plot

async def main(app):
  # some await task here
  viz = OptionViz(app)
  # more await code here

if __name__ == '__main__':
  app = QApplication(sys.argv)
  loop = QEventLoop(app)
  asyncio.set_event_loop(loop)
  loop.create_task(main(app))
  loop.run_forever()
3
  • what are # some await task here and # more await code here? Commented Feb 3, 2020 at 5:19
  • In your code "main" it is not awaitable so it will be executed instantly. Do you understand that it is an awaitable function? Commented Feb 3, 2020 at 5:20
  • Are you going to answer some of my questions? Commented Feb 3, 2020 at 5:25

1 Answer 1

1

Qt is not compatible with asyncio so several libraries have been implemented such as quamash, asyncqt, qasync, etc. to make it compatible. In the case of quamash and asyncqt they have a bug that does not allow to execute but the qasync library so that it has solved it(execute pip install qasync to install the package).

On the other hand, the main method is not awaitable since it does not execute a time-consuming task but it is executed instantly so I have had to restructure your project:

import sys
import asyncio
from qasync import QEventLoop
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np


class OptionViz:
    def __init__(self, app):
        self.app = app
        p = pg.plot()
        p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
        p.setRange(QtCore.QRectF(0, -10, 5000, 20))
        p.setLabel("bottom", "Index", units="B")
        self.curve = p.plot()


async def main(viz):
    data = np.random.normal(size=(50, 5000))
    ptr = 0
    while True:
        viz.curve.setData(data[ptr % 10])
        await asyncio.sleep(0.1)
        ptr += 1


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)
    viz = OptionViz(app)
    loop.create_task(main(viz))
    loop.run_forever()

Again, the "main" function only creates an "OptionViz" object that does not take a long time is not awaitable so it is absurd to use async in that case. It seems that the OP does not understand the operation of asyncio.

By restructuring your code we can make the function awaitable so OptionViz must be a QObject to use the asyncSlot decorator, in addition the QTimer must be a child of the QObject so that its life cycle increases.

import sys
import asyncio
from qasync import QEventLoop, asyncSlot
from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg
import numpy as np
from time import time


class OptionViz(QtCore.QObject):
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.p = pg.plot()
        self.p.setWindowTitle("pyqtgraph example: PlotSpeedTest")
        self.p.setRange(QtCore.QRectF(0, -10, 5000, 20))
        self.p.setLabel("bottom", "Index", units="B")
        self.curve = self.p.plot()

        self.data = np.random.normal(size=(50, 5000))
        self.ptr = 0
        self.lastTime = time()
        self.fps = None

        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update)
        timer.start(0)

    @asyncSlot()
    async def update(self):
        self.curve.setData(self.data[self.ptr % 10])
        self.ptr += 1
        now = time()
        dt = now - self.lastTime
        self.lastTime = now
        if self.fps is None:
            fps = 1.0 / dt
        else:
            s = np.clip(dt * 3.0, 0, 1)
            self.fps = self.fps * (1 - s) + (1.0 / dt) * s
        self.p.setTitle("%0.2f fps" % fps)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    loop = QEventLoop(app)
    asyncio.set_event_loop(loop)
    viz = OptionViz(app)
    loop.run_forever()
Sign up to request clarification or add additional context in comments.

6 Comments

I updated my code to match your solution. But I'm not getting the chart data updates.
@BAR I suspect that you are not clear about the basic concepts of when a function should be used "async" and when not.
It is important that my main() function is called as async as it has await code inside it.
If I do it like i have it, no updates come through.
@BAR If you provide a real minimal reproducible example then I could tell you why it is failing, but with what you provide it is impossible for me to help you.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.