如何使用QTextBrowser代替python中的控制台?

时间:2019-02-12 05:18:58

标签: python python-3.x pyqt5 gurobi

我已经在pyqt5中构建了一个窗口,通过单击“优化”按钮,该程序将读取“ Gurobi-model.lp”文件(click here to get the file),并借助Gurobi软件对其进行优化。如何在QTextBrowser上显示Gurobi的日志?

我在Gurobi中找到了一些功能,例如OutputFlag,LogFile,LogToConsole。但是,我进行了很多搜索,但我不了解这些功能可能对我没有帮助。在这方面有人可以帮助我吗?

对于不熟悉Gurobi的用户,Gurobi优化器使用python作为界面,并生成一些日志,使您可以跟踪优化的进度。这些日志在优化期间会打印在控制台中,以某种方式,回答我的问题不需要了解有关Gurobi的任何信息。

在下面的代码中,我找到了一种在QTextBrowser中显示日志的方法,但是在优化过程完成后才表示日志。我希望在优化过程中准确地表示日志。

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 个答案:

答案 0 :(得分:2)

优化任务很繁重,因此不应在GUI的同一线程中或同一进程中执行优化,为此,应使用多处理模块,另一方面,如果需要显示在QTextBrowser中的控制台上,您必须使用通过信号传递信号的日志记录模块,(最后一部分使用本文的the answer

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()