How to use a QTextBrowser instead of the console in python?

痴心易碎 提交于 2021-02-19 09:54:29

问题


I've constructed a window in pyqt5, which by clicking on "optimize" button, the program reads the "Gurobi-model.lp" file(click here to get the file), and optimizes it by the help of the Gurobi software. How can I display the logs of the Gurobi on a QTextBrowser?

I found some functions in the Gurobi such as OutputFlag, LogFile, LogToConsole. However, I searched a lot, and I didn't understand these functions might be helpful for me or not. Anybody can help me in this regard?

For those who are not familiar with Gurobi, the Gurobi optimizer uses python as an interface, and produces some logs that allows you to track the progress of the optimization. These logs are printed in the console during the optimization, and somehow, responding my question doesn't need to know anything about the Gurobi.

In the below code, I've found a way to show the logs in the QTextBrowser, but the logs are represented when the optimization process is completely done. I want the logs to be represented exactly during the optimization process.

import sys 
from PyQt5.QtWidgets import *
from gurobipy import *
from io import *


class MyWindow(QWidget): 

    def __init__(self): 
        QWidget.__init__(self) 

        self.pb = QPushButton(self.tr("optimize"))
        self.log_text = QTextBrowser()

        layout = QVBoxLayout(self)
        layout.addWidget(self.pb)
        layout.addWidget(self.log_text)
        self.setLayout(layout)

        self.pb.clicked.connect(self.optimize)

     def optimize(self):

        f = StringIO()
        sys.stdout = StringIO()

        self.m = read('Gurobi-model.lp')
        self.m.optimize()
        self.log_text.append(sys.stdout.getvalue() )


def main(): 
   app = QApplication(sys.argv) 
   w = MyWindow() 
   w.show() 
   sys.exit(app.exec_())       

if __name__ == "__main__": 
    main()

回答1:


the task of optimizing is heavy so it should not be executed in the same thread of the GUI, nor in the same process, for this you should use the multiprocessing module, on the other hand if you need to show the output of the console in the QTextBrowser you must use the logging module passing it through signal, (for the last part use the answer of this post)

import sys
import logging
import multiprocessing
from logging.handlers import QueueHandler, QueueListener
from PyQt5 import QtCore, QtWidgets
from gurobipy import *

class LogEmitter(QtCore.QObject):
    sigLog = QtCore.pyqtSignal(str)

class LogHandler(logging.Handler):
    def __init__(self):
        super().__init__()
        self.emitter = LogEmitter()

    def emit(self, record):
        msg = self.format(record)
        self.emitter.sigLog.emit(msg)

def long_task():
    m = read('Gurobi-model.lp')
    m.optimize()

def worker_init(q):
    qh = QueueHandler(q)
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(qh)

class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)

        self.pb = QtWidgets.QPushButton(self.tr("optimize"), 
            clicked=self.start_optimize)
        self.log_text = QtWidgets.QPlainTextEdit(readOnly=True)

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.pb)
        layout.addWidget(self.log_text)

        self.running = False
        handler = LogHandler()
        handler.emitter.sigLog.connect(self.log_text.appendPlainText)

        self.q = multiprocessing.Queue()
        self.ql = QueueListener(self.q, handler)
        self.ql.start()

        self.main_log = logging.getLogger('main')
        self.main_log.propagate = False
        self.main_log.setLevel(logging.INFO)
        self.main_log.addHandler(QueueHandler(self.q))
        self.pool = multiprocessing.Pool(1, worker_init, [self.q])

    @QtCore.pyqtSlot()
    def start_optimize(self):
        if not self.running:
            self.pool.apply_async(long_task, callback=self.handle_result)

    def handle_result(self, result=None):
        self.running = False

    def closeEvent(self, event):
        self.ql.stop()
        super(MyWindow, self).closeEvent(event)

def main(): 
   app = QtWidgets.QApplication(sys.argv) 
   w = MyWindow() 
   w.show() 
   sys.exit(app.exec_())       

if __name__ == "__main__": 
    main()


来源:https://stackoverflow.com/questions/54643280/how-to-use-a-qtextbrowser-instead-of-the-console-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!