i need a scheduler in a Textual application to periodically query an external data source. As a test i've tried to use APscheduler to call a tick() function every second.
However nothing happens although the scheduler should be started.
What is going on and how to debug this?
from textual.app import App, ComposeResult
from textual.containers import Horizontal, Vertical
from textual.widgets import *
from apscheduler.schedulers.background import BackgroundScheduler
class HeaderApp(App):
def __init__(self, *args, **kwargs):
self.sched = BackgroundScheduler()
self.sched.add_job(self.tick,'interval', seconds=1)
self.sched.start()
super(HeaderApp, self).__init__(*args, **kwargs)
def compose(self) -> ComposeResult:
yield Header()
yield TextLog()
def tick(self):
text_log = self.query_one(TextLog)
text_log.write("tick")
def on_mount(self):
text_log = self.query_one(TextLog)
text_log.write(self.sched.running)
if __name__ == "__main__":
app = HeaderApp()
app.run()
I'm not familiar with apscheduler, but since you haven't had a response, could you use the builtin set_interval which will call a method at regular intervals?
from textual.app import App, ComposeResult
from textual.containers import Horizontal, Vertical
from textual.widgets import *
class HeaderApp(App):
def compose(self) -> ComposeResult:
yield Header()
yield TextLog()
def tick(self):
text_log = self.query_one(TextLog)
text_log.write("tick")
def on_mount(self):
self.set_interval(1, self.tick)
if __name__ == "__main__":
app = HeaderApp()
app.run()
Related
I have a program which compares to DB table values and i have created a GUI in PyQt5. I have created two threads one for querying each table and then program has to wait till two threads are completed. My code below
from PySide2 import QtWidgets
from PySide2 import QtGui
from PySide2 import QtCore
from Main_interface import Ui_mainWindow
import pandas as pd
class mainWindow(QtWidgets.QMainWindow, Ui_mainWindow):
sqlClicked1 = QtCore.Signal(str)
sqlClicked2 = QtCore.Signal(str)
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.setupUi(self)
self.thread = QtCore.QThread(self)
self.thread.start()
self.obj = Worker()
self.obj.moveToThread(self.thread)
self.sqlClicked.connect(self.obj.runsql_MC)
self.sqlClicked1.connect(self.obj.runsql_IRI)
self.obj.error.connect(self.on_error)
def run_report(self):
sqlquery1 = "Select * from table1"
sqlquery2 = "Select * from table2"
df1 = self.sqlClicked1.emit(sqlquery1)
df2 = self.sqlClicked2.emit(sqlquery2)
self.sqlClicked1.finished.connect(self.on_finished)
self.sqlClicked2.finished.connect(self.on_finished)
print("SQL execution is done")
#Then i am calling function to compare two dataframes
class Worker(QtCore.QObject):
finished = QtCore.Signal()
result = QtCore.Signal(object)
#QtCore.Slot(str)
def runsql_MC(self, sqlquery_MC):
print("Thread1 is working")
try:
df1 = pd.read_sql(sql=sqlquery_MC, con=cnxn)
except:
traceback.print_exc()
else:
self.signals.result.emit(df1) # Return the result of the processing
finally:
self.signals.finished.emit() # Done
#QtCore.Slot(str)
def runsql_IRI(self, sqlquery_IRI):
print("Thread2 is working")
try:
df2 = pd.read_sql(sql=sqlquery_IRI, con=cnxn)
except:
traceback.print_exc()
else:
self.signals.result.emit(df2)
finally:
self.signals.finished.emit()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
my_app = mainWindow()
my_app.show()
sys.exit(app.exec_())
self.sqlClicked1.emit(sqlquery1) and self.sqlClicked2.emit(sqlquery2) is calling corresponding threads runsql_MC() and runsql_IRI. Then I need to wait till two threads are completed to start comparison process. Currently its not happening.
Although your code is not an MRE, show your ignorance of various concepts.
The emission of a signal does not imply obtaining the data as a result since it will be sent asynchronously.
In your code even if you invoke 2 queries does not imply that each one runs on different threads since the worker lives in a single thread.
Your runsql_MC and runsql_IRI methods are redundant since they are a template of the same thing.
In addition to other errors such as that there is no object/signal called sqlClicked, you have not declared the object signals, etc.
The idea is to have a worker who lives in a different thread for each query, and create a class that handles the workers waiting for the data and eliminating when they have finished their work.
from functools import partial
import sqlite3
import pandas as pd
from PySide2 import QtCore, QtGui, QtWidgets
class Worker(QtCore.QObject):
finished = QtCore.Signal()
result = QtCore.Signal(object)
#QtCore.Slot(str)
def runsql(self, query):
cnxn = sqlite3.connect("test.db")
print("Thread1 is working")
try:
df1 = pd.read_sql(sql=query, con=cnxn)
except:
traceback.print_exc()
else:
self.result.emit(df1) # Return the result of the processing
finally:
self.finished.emit() # Done
class SqlManager(QtCore.QObject):
results = QtCore.Signal(list)
def __init__(self, parent=None):
super().__init__(parent)
self.workers_and_threads = {}
self.dataframes = []
def execute_queries(self, queries):
for query in queries:
thread = QtCore.QThread(self)
thread.start()
worker = Worker()
worker.result.connect(self.onResults)
worker.moveToThread(thread)
self.workers_and_threads[worker] = thread
# launch task
wrapper = partial(worker.runsql, query)
QtCore.QTimer.singleShot(0, wrapper)
#QtCore.Slot(object)
def onResults(self, result):
worker = self.sender()
thread = self.workers_and_threads[worker]
thread.quit()
thread.wait()
del self.workers_and_threads[worker]
worker.deleteLater()
self.dataframes.append(result)
if not self.workers_and_threads:
self.results.emit(self.dataframes)
self.dataframes = []
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.push_button = QtWidgets.QPushButton("Run Report")
self.push_button.clicked.connect(self.run_report)
self.setCentralWidget(self.push_button)
self.manager = SqlManager(self)
self.manager.results.connect(self.onResults)
#QtCore.Slot()
def run_report(self):
sqlquery1 = "Select * from table1"
sqlquery2 = "Select * from table2"
queries = [sqlquery1, sqlquery2]
self.manager.execute_queries(queries)
self.push_button.setEnabled(False)
#QtCore.Slot(list)
def onResults(self, dataframes):
print(dataframes)
self.push_button.setEnabled(True)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
my_app = MainWindow()
my_app.show()
sys.exit(app.exec_())
I'm not quite sure if it is written in your code but Have you ever uses a join() sentence to wait till those threads end? You should be using it when you awake those two threads inside your def __init__() method from your mainWindow class
I'm trying to display the time in a FixedText widget in an NPSAppManaged application.
So far I've got this:
import npyscreen
import datetime
import threading
from time import sleep
class MainForm(npyscreen.Form):
def create(self):
self.add(npyscreen.FixedText, value = "Time")
def afterEditing(self):
self.parentApp.setNextForm(None)
def set_value(self):
return "Tom"
class TestApp(npyscreen.NPSAppManaged):
def onStart(self):
self.registerForm("MAIN", MainForm())
thread_time = threading.Thread(target=self.update_time,args=())
thread_time.daemon = True
thread_time.start()
def update_time(self):
while True:
# self.f.wStatus2.value = datetime.datetime.now().ctime()
# self.f.wStatus2.display()
sleep(1)
if __name__ == "__main__":
App = TestApp()
App.run()
I'm just not sure how to reference the .value parameter for the widget from the thread and update it. What should I be doing?
You need to assign the npyscreen.FixedText to a variable as such:
def create(self):
self.w_time = self.add(npyscreen.FixedText, value = "Time")
Now you can use self.w_time.value to access it.
import npyscreen
import datetime
import threading
from time import sleep
class MainForm(npyscreen.Form):
def create(self):
self.date = self.add(npyscreen.TitleText, value = str(datetime.datetime.now()), editable=False, name='Something')
def afterEditing(self):
self.parentApp.setNextForm(None)
def set_value(self):
return "Tom"
class TestApp(npyscreen.NPSAppManaged):
def onStart(self):
self.textual = MainForm()
self.registerForm("MAIN", self.textual)
thread_time = threading.Thread(target=self.update_time,args=())
thread_time.daemon = True
thread_time.start()
def update_time(self):
while True:
self.textual.date.value = str(datetime.datetime.now())
self.textual.display()
sleep(1)
if __name__ == "__main__":
App = TestApp()
App.run()
I have the following setting:
import sys
from flask import Flask
from flask.ext import restful
from model import Model
try:
gModel = Model(int(sys.argv[1]))
except IndexError, pExc:
gModel = Model(100)
def main():
lApp = Flask(__name__)
lApi = restful.Api(lApp)
lApi.add_resource(FetchJob, '/')
lApp.run(debug=True)
class FetchJob(restful.Resource):
def get(self):
lRange = gModel.getRange()
return lRange
if __name__ == '__main__':
main()
Is there a way to instantiate the Model-class inside the main()-function? Here, the Flask framework instantiates the FetchJob-class, so that I cannot provide it the parameters it forwards during the instantiation process.
I don't like to have global variables as this messes up the whole design ...
I think this should work, although I'm not familiar with Flask:
import functools
def main():
try:
gModel = Model(int(sys.argv[1]))
except IndexError as pExc:
gModel = Model(100)
lApp = Flask(__name__)
lApi = restful.Api(lApp)
lApi.add_resource(functools.partial(FetchJob, gModel), '/')
lApp.run(debug=True)
class FetchJob(restful.Resource):
def __init__(self, obj, *args, **kwargs):
restfult.Resource.__init__(self, *args, **kwargs)
self.obj = obj
def get(self):
lRange = self.obj.getRange()
return lRange
I'm trying to understand why the following code immediately exits, but works if I create the thread in the main context and not in a second object?
from PyQt4 import QtCore
import time
import sys
class SomeObject(QtCore.QObject):
finished = QtCore.pyqtSignal()
def longRunning(self):
count = 0
while count < 5:
time.sleep(1)
print "Increasing"
count += 1
self.finished.emit()
class SecondObject(QtCore.QObject):
def __init__(self, app):
QtCore.QObject.__init__(self)
objThread = QtCore.QThread()
obj = SomeObject()
obj.moveToThread(objThread)
obj.finished.connect(objThread.quit)
objThread.started.connect(obj.longRunning)
objThread.finished.connect(app.exit)
objThread.start()
def usingMoveToThread():
app = QtCore.QCoreApplication([])
SecondObject(app)
sys.exit(app.exec_())
if __name__ == "__main__":
usingMoveToThread()
Thanks in Advance for any help!
I've found the problem. Apparently you need to hold on to the new Thread's reference otherwise the Application will just quit...
I'm trying to build a PyQt app which (among other things) has the ability via a QTextEdit Box to function like a serial terminal program (HyperTerminal, TeraTerm, etc.) I've read through a few examples from the PySerial page and I think I've managed to get the receive data thread working properly but maybe not as efficiently as possible.
My problem is how do I take the last typed character in the QTextEdit box and send that out the serial connection? I've tried using the textChanged signal that QTextEdit emits, but that then sends everything that I type AND that it receives. I've tried setting up an eventFilter in my main GUI class, but I can't figure out how to get that over to the serial function in another file. Do I want to have a separate thread that listens for a signal emitted from the eventFilter? How do I do that? Is there a more elegant way to do this?
I'm sure I've just managed to overthink this and the solution is simple, but I'm somewhat struggling with it. I'll attach the relevant code snippets (not a full code set) and perhaps somebody can point me in the right direction. If anybody also thinks that the threading that I'm doing could be done in a more efficient manner, then please relay that to me as well!
Thanks for any help that anybody can provide!
Main File:
import sys
from PyQt4 import QtGui
from MainGUI import TestGUI
from SerialClasses import *
from SerialMiniterm import *
class StartMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(StartMainWindow, self).__init__(parent)
self.ui = TestGUI()
self.ui.setupUi(self)
self.ui.serialTextEditBox.installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.KeyPress and source is self.ui.serialTextEditBox):
# print some debug statements to console
if (event.key() == QtCore.Qt.Key_Tab):
print ('Tab pressed')
print ('key pressed: %s' % event.text())
print ('code pressed: %d' % event.key())
# do i emit a signal here? how do i catch it in thread?
self.emit(QtCore.SIGNAL('transmitSerialData(QString)'), event.key())
return True
return QtGui.QTextEdit.eventFilter(self, source, event)
def serialConnectCallback(self):
self.miniterm = SerialMiniterm(self.ui, self.SerialSettings)
self.miniterm.start()
temp = self.SerialSettings.Port + 1
self.ui.serialLabel.setText("<font color = green>Serial Terminal Connected on COM%d" % temp)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setStyle("Cleanlooks")
myapp = StartMainWindow()
myapp.show()
sys.exit(app.exec_())
SerialMiniterm.py:
import serial
from PyQt4 import QtGui, QtCore
def character(b):
return b
class SerialMiniterm(object):
def __init__(self, ui, SerialSettings):
self.SerialSettings = SerialSettings
self.ui = ui
self.serial = serial.Serial(self.SerialSettings.Port, self.SerialSettings.BaudRate, parity=self.SerialSettings.Parity, rtscts=self.SerialSettings.RTS_CTS, xonxoff=self.SerialSettings.Xon_Xoff, timeout=1)
self.repr_mode = self.SerialSettings.RxMode
self.convert_outgoing = self.SerialSettings.NewlineMode
self.newline = NEWLINE_CONVERISON_MAP[self.convert_outgoing]
self.dtr_state = True
self.rts_state = True
self.break_state = False
def _start_reader(self):
"""Start reader thread"""
self._reader_alive = True
self.receiver_thread = ReaderThread(self.alive, self._reader_alive, self.repr_mode, self.convert_outgoing, self.serial)
self.receiver_thread.connect(self.receiver_thread, QtCore.SIGNAL("updateSerialTextBox(QString)"), self.updateTextBox)
self.receiver_thread.start()
def _stop_reader(self):
"""Stop reader thread only, wait for clean exit of thread"""
self._reader_alive = False
self.receiver_thread.join()
def updateTextBox(self, q):
self.ui.serialTextEditBox.insertPlainText(q)
self.ui.serialTextEditBox.moveCursor(QtGui.QTextCursor.End)
#print "got here with value %s..." % q
def start(self):
self.alive = True
self._start_reader()
# how do i handle transmitter thread?
def stop(self):
self.alive = False
def join(self, transmit_only=False):
self.transmitter_thread.join()
if not transmit_only:
self.receiver_thread.join()
class ReaderThread(QtCore.QThread):
def __init__(self, alive, _reader_alive, repr_mode, convert_outgoing, serial, parent=None):
QtCore.QThread.__init__(self, parent)
self.alive = alive
self._reader_alive = _reader_alive
self.repr_mode = repr_mode
self.convert_outgoing = convert_outgoing
self.serial = serial
def __del__(self):
self.wait()
def run(self):
"""loop and copy serial->console"""
while self.alive and self._reader_alive:
data = self.serial.read(self.serial.inWaiting())
if data: #check if not timeout
q = data
self.emit(QtCore.SIGNAL('updateSerialTextBox(QString)'), q)
Something like this?
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
class Terminal(QtGui.QPlainTextEdit):
def keyPressEvent(self, event):
print event.text()
return QtGui.QPlainTextEdit.keyPressEvent(self, event)
term = Terminal()
term.show()