Browse Source

Move eventloop to its own file

master
Nils 4 years ago
parent
commit
5f79015e23
  1. 9
      template/engine/api.py
  2. 90
      template/qtgui/eventloop.py
  3. 76
      template/qtgui/mainwindow.py

9
template/engine/api.py

@ -263,7 +263,8 @@ class Callbacks(object):
def _channelChanged(self, channel):
"""A single channel changed its parameters. The soundfont stays the same."""
exportDict = session.data.exportChannel(channel)
session.data.updateChannelJackMetadaPrettyname(channel)
session.data.updateChannelAudioJackMetadaPrettyname(channel)
session.data.updateChannelMidiInJackMetadaPrettyname(channel)
for func in self.channelChanged:
func(channel, exportDict)
@ -285,17 +286,13 @@ def startEngine(nsmClient):
It gets called by client applications before their own startEngine.
Stopping the engine is done via pythons atexit in the session.
session.eventLoop is overwritten by template.qtgui.mainWindow . It is the first action
it takes after imports.
"""
logger.info("Starting template api engine")
assert session
assert callbacks
session.nsmClient = nsmClient
session.eventLoop.directConnect(callbacks._checkPlaybackStatusAndSendSignal)
session.eventLoop.fastConnect(callbacks._checkPlaybackStatusAndSendSignal)
session.eventLoop.fastConnect(callbacks._setPlaybackTicks)
session.eventLoop.fastConnect(cbox.get_new_events) #global cbox.get_new_events does not eat dynamic midi port events.
session.eventLoop.slowConnect(callbacks._checkBBTAndSendSignal)

90
template/qtgui/eventloop.py

@ -0,0 +1,90 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2020, Nils Hilbricht, Germany ( https://www.hilbricht.net )
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
This application is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")
from PyQt5 import QtCore
class EventLoop(object):
def __init__(self):
"""The loop for all things GUI and controlling the GUI (e.g. by a control midi in port)
directConnect is 0ms (see below)
fastConnect 20ms
slowConnect 100ms
verySlowConnect 200ms
0 ms means "if there is time". 10ms-20ms is smooth. 100ms is still ok.
Influences everything. Control Midi In Latency, playback cursor scrolling smoothnes etc.
But not realtime. This is not the realtime loop. Converting midi into instrument sounds
or playing back sequenced midi data is not handled by this loop at all.
Creating a non-qt class for the loop is an abstraction layer that enables the engine to
work without modification for non-gui situations. In this case it will use its own loop,
like python async etc.
A qt event loop needs the qt-app started. Otherwise it will not run.
We init the event loop outside of main but call start from the mainWindow.
"""
self.fastLoop = QtCore.QTimer()
self.slowLoop = QtCore.QTimer()
self.verySlowLoop = QtCore.QTimer()
def fastConnect(self, function):
self.fastLoop.timeout.connect(function)
def slowConnect(self, function):
self.slowLoop.timeout.connect(function)
def verySlowConnect(self, function):
self.verySlowLoop.timeout.connect(function)
def fastDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.fastLoop.timeout.disconnect(function)
def slowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.slowLoop.timeout.disconnect(function)
def verySlowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.verySlowLoop.timeout.disconnect(function)
def start(self):
"""The event loop MUST be started after the Qt Application instance creation"""
logger.info("Starting fast qt event loop")
self.fastLoop.start(20)
logger.info("Starting slow qt event loop")
self.slowLoop.start(100)
logger.info("Starting very slow qt event loop")
self.verySlowLoop.start(200)
def stop(self):
logger.info("Stopping fast qt event loop")
self.fastLoop.stop()
logger.info("Stopping slow qt event loop")
self.slowLoop.stop()
logger.info("Stopping very slow qt event loop")
self.verySlowLoop.stop()

76
template/qtgui/mainwindow.py

@ -40,7 +40,9 @@ from .menu import Menu
from .resources import *
from .about import About
from .helper import setPaletteAndFont
from .eventloop import EventLoop
from template.start import PATHS, qtApp
#Client modules
from engine.config import * #imports METADATA
import engine.api as api #This loads the engine and starts a session.
@ -48,80 +50,6 @@ from qtgui.designer.mainwindow import Ui_MainWindow #The MainWindow designer fil
from qtgui.resources import *
from qtgui.constantsAndConfigs import constantsAndConfigs
class EventLoop(object):
def __init__(self):
"""The loop for all things GUI and controlling the GUI (e.g. by a control midi in port)
directConnect is 0ms (see below)
fastConnect 20ms
slowConnect 100ms
verySlowConnect 200ms
0 ms means "if there is time". 10ms-20ms is smooth. 100ms is still ok.
Influences everything. Control Midi In Latency, playback cursor scrolling smoothnes etc.
But not realtime. This is not the realtime loop. Converting midi into instrument sounds
or playing back sequenced midi data is not handled by this loop at all.
Creating a non-qt class for the loop is an abstraction layer that enables the engine to
work without modification for non-gui situations. In this case it will use its own loop,
like python async etc.
A qt event loop needs the qt-app started. Otherwise it will not run.
We init the event loop outside of main but call start from the mainWindow.
"""
self.directLoop = QtCore.QTimer()
self.fastLoop = QtCore.QTimer()
self.slowLoop = QtCore.QTimer()
self.verySlowLoop = QtCore.QTimer()
def directConnect(self, function):
self.directLoop.timeout.connect(function)
def fastConnect(self, function):
self.fastLoop.timeout.connect(function)
def slowConnect(self, function):
self.slowLoop.timeout.connect(function)
def verySlowConnect(self, function):
self.verySlowLoop.timeout.connect(function)
def fastDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.fastLoop.timeout.disconnect(function)
def slowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.slowLoop.timeout.disconnect(function)
def verySlowDisconnect(self, function):
"""The function must be the exact instance that was registered"""
self.verySlowLoop.timeout.disconnect(function)
def start(self):
"""The event loop MUST be started after the Qt Application instance creation"""
logger.info("Starting direct qt event loop")
self.directLoop.start(0)
logger.info("Starting fast qt event loop")
self.fastLoop.start(20)
logger.info("Starting slow qt event loop")
self.slowLoop.start(100)
logger.info("Starting very slow qt event loop")
self.verySlowLoop.start(200)
def stop(self):
logger.info("Stopping direct qt event loop")
self.directLoop.stop()
logger.info("Stopping fast qt event loop")
self.fastLoop.stop()
logger.info("Stopping slow qt event loop")
self.slowLoop.stop()
logger.info("Stopping very slow qt event loop")
self.verySlowLoop.stop()
api.session.eventLoop = EventLoop()
#QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DontUseNativeMenuBar) #Force a real menu bar. Qt on wayland will not display it otherwise.

Loading…
Cancel
Save