Search code examples
pythonpyqtpyqt4

`less +F logfile` in pyqt


I'm trying to figure out how to continuously update a text area with loginfo. I have the following (snippet from my code)

import sys 
import os
import PyQt4.QtGui as gui 
import PyQt4.QtCore as core

class ApplicationWindow(gui.QMainWindow):
    ''' 
    Our main application window
    '''
    def __init__(self):
        gui.QMainWindow.__init__(self)
        self.main_widget = gui.QWidget(self)
        l = gui.QHBoxLayout(self.main_widget)

        self.te = gui.QPlainTextEdit()
        self.rdock = gui.QDockWidget("Output",self)
        self.rdock.setWidget(self.te)
        self.te.setReadOnly(True)
        self.addDockWidget(core.Qt.RightDockWidgetArea,self.rdock)

        self.run_command("less +F test.txt")
        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)

    def run_command(self,fcmd):
        '''
        Runs our desired command and puts the output into the right dock
        '''
        cmd = str(fcmd)
        stdouterr = os.popen4(cmd)[1].read()
        self.te.setPlainText(stdouterr)

qApp = gui.QApplication(sys.argv)
aw = ApplicationWindow()
aw.setWindowTitle("%s" %progname)
aw.show()
sys.exit(qApp.exec_())

My problem here is that the program hangs. I would like to be able to continuously show the output and eventually I'll want to be able to kill that command and run less +F otherFile.txt. I'm not dedicated to using the less command, I just want to see the continuous tail end of a file.

I have tried using threading, like this, but to no avail

runThread = threading.Thread(target=self.run_command("less +F test.txt"))
runThread.daemon = True
runThread.start()

I am getting the impression that I need to run the ostream command on a different thread so that I'm not blocking the main application, but am unsure how best to do this.


Solution

  • Using threads is an option but not the best in your case, I would recommend using a timer as it is more user friendly with the GUI.

    timer = core.QTimer(self)
    timer.timeout.connect(lambda: self.run_command("less +F test.txt"))
    timer.start(10) # milliseconds
    

    Also comments that you want to change command, then I recommend you modify your run_command function to the following:

    def run_command(self):
        '''
        Runs our desired command and puts the output into the right dock
        '''
        stdouterr = os.popen4(self.cmd)[1].read()
        self.te.setPlainText(stdouterr)
    

    And then to change the command you just pass the new string to self.cmd:

    self.cmd = "less +F otherFile.txt" 
    

    Complete Example:

    class ApplicationWindow(gui.QMainWindow):
        ''' 
        Our main application window
        '''
        def __init__(self):
            gui.QMainWindow.__init__(self)
            self.main_widget = gui.QWidget(self)
            l = gui.QHBoxLayout(self.main_widget)
    
            self.te = gui.QPlainTextEdit()
            self.rdock = gui.QDockWidget("Output",self)
            self.rdock.setWidget(self.te)
            self.te.setReadOnly(True)
            self.addDockWidget(core.Qt.RightDockWidgetArea,self.rdock)
    
            self.main_widget.setFocus()
            self.setCentralWidget(self.main_widget)
    
            self.cmd = "less +F test.txt" 
            timer = core.QTimer(self)
            timer.timeout.connect(self.run_command)
            timer.start(10)
    
        def run_command(self):
            '''
            Runs our desired command and puts the output into the right dock
            '''
            stdouterr = os.popen4(self.cmd)[1].read()
            self.te.setPlainText(stdouterr)