How to embed a Python interpreter in a PyQT widget

后端 未结 5 1955
忘掉有多难
忘掉有多难 2020-11-29 00:49

I want to be able to bring up an interactive python terminal from my python application. Some, but not all, variables in my program needs to be exposed to the interpreter.

相关标签:
5条回答
  • 2020-11-29 01:27

    Not sure of what you want exactly but have tried to save the widget content into a a temporary file and pass it to a standard python interpreter with Popen ?

    Doc is here : http://docs.python.org/release/2.6.5/library/subprocess.html#subprocess.Popen

    Example :

    import tempfile, os, sys, subprocess
    
    # get the code
    code = get_widget_content()
    
    # save the code to a temporary file
    file_handle, file_path = tempfile.mkstemp()
    tmp_file = os.fdopen(file_handle, 'w')
    tmp_file.write(code)
    tmp_file.close()
    
    #execute it
    p = subprocess.Popen([sys.executable, file_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
    # wait for the command to complete 
    p.wait()
    
    # retrieve the output:
    pyerr = p.stderr.readlines()
    pyout = p.stdout.readlines()
    
    # do what ever you want with it
    print(pyerr)
    print(pyout)
    
    0 讨论(0)
  • 2020-11-29 01:30

    You might look into using threads to keep the UI responsive while printing big loops. This would also help keep your tracebacks clean.

    Keeping variables in a dict is the way to go – it's what Python itself does internally. As far as exposing “some, but not all” of them, consider just exposing them all. Much easier. If you're concerned about security, beware that you can't reliably hide anything in Python.

    As for the hideous cursor/text manipulation: take advantage of the fact that you have a GUI. With a terminal, you just have one “text box”, but in Qt, it might be more appropriate to have a log/result view and a separate command box.

    The log view would display the entered commands and results in a read-only textbox.

    The command textbox would allow you to enter a command cleanly.

    This approach is used in some web frameworks – e.g. via WebError:

    enter image description here

    0 讨论(0)
  • 2020-11-29 01:31

    Bit late I know, but I recommend the code.InteractiveConsole class: http://docs.python.org/py3k/library/code.html#code.InteractiveConsole

    0 讨论(0)
  • 2020-11-29 01:33

    First draft of updated version of my code to support IPython 0.13

    '''
    Created on 18-03-2012
    
    @author: Paweł Jarosz
    '''
    import os, sys
    import atexit
    
    from PySide import QtCore, QtGui
    
    from IPython.zmq.ipkernel import IPKernelApp
    from IPython.lib.kernel import find_connection_file, connect_qtconsole
    from IPython.frontend.qt.kernelmanager import QtKernelManager
    from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
    from IPython.config.application import catch_config_error
    
    class IPythonLocalKernelApp(IPKernelApp):
        """IPython kernel application with nonblocking loop, running in dedicated thread.
        example:
            app = QtGui.QApplication([])
            kernelapp = IPythonLocalKernelApp.instance()
            kernelapp.start()
            namespace = kernelapp.get_user_namespace()
            namespace["QtGui"]=QtGui
            namespace["QtCore"]=QtCore
            app.exec_()"""
        #DEFAULT_INSTANCE_ARGS starting commandline
        DEFAULT_INSTANCE_ARGS = ['qtconsole','--pylab=inline', '--colors=linux']
    
        @catch_config_error
        def initialize(self, argv=None):
            super(IPythonLocalKernelApp, self).initialize(argv)
            self.kernel.eventloop = self.loop_qt4_nonblocking
    
        def loop_qt4_nonblocking(self, kernel):
            """Non-blocking version of the ipython qt4 kernel loop"""
            kernel.timer = QtCore.QTimer()
            kernel.timer.timeout.connect(kernel.do_one_iteration)
            kernel.timer.start(1000*kernel._poll_interval)
    
        def start(self, argv=DEFAULT_INSTANCE_ARGS):
            """Starts IPython kernel app
                argv: arguments passed to kernel
            """
            self.initialize(argv)
            #self.heartbeat.start()
            #if self.poller is not None:
            #    self.poller.start()
    
            self.kernel.start()
            super(IPythonLocalKernelApp, self).start()
    
    
        def get_connection_file(self):
            """Returne current kernel connection file."""
            return self.connection_file
    
        def get_user_namespace(self):
            """Returns current kernel userspace dict"""
            return self.kernel.shell.user_ns
    
    class IPythonConsoleQtWidget(RichIPythonWidget):
        """Ipython console Qt4+ widget
            Usage example:
                app = QtGui.QApplication([])
                kernelapp = IPythonLocalKernelApp.instance()
                kernelapp.start()
                namespace = kernelapp.get_user_namespace()
                widget = IPythonConsoleQtWidget()
                widget.set_default_style(colors='linux')
                widget.connect_kernel(connection_file=kernelapp.get_connection_file())
                # if you won't to connect to remote kernel:
                widget.connect_kernel(connection_file='kernel-16098.json')
    
                widget.show()
    
                namespace["widget"] = widget
                namespace["QtGui"]=QtGui
                namespace["QtCore"]=QtCore
    
                app.exec_()"""
        _connection_file = None
    
        def __init__(self, *args, **kw):
            RichIPythonWidget.__init__(self, *args, **kw)
            self._existing = True
            self._may_close = False
            self._confirm_exit = False
    
        def _init_kernel_manager(self):
            km = QtKernelManager(connection_file=self._connection_file, config=self.config)
            km.load_connection_file()
            km.start_channels(hb=self._heartbeat)
            self.kernel_manager = km
            atexit.register(self.kernel_manager.cleanup_connection_file)
    
        def connect_kernel(self, connection_file, heartbeat=False):
            """Connect's to ipython kernel.
            connection_file    - connection file to use
            heartbeat          - should start heartbeat server? Workaround for problems with inproc embedded kernels
                                 (right click save image as/save as html kills kernel heartbeat/pool(??) serwer """
    
            self._heartbeat = heartbeat
            if os.path.exists(connection_file):
                self._connection_file = connection_file
            else:
                self._connection_file = find_connection_file(connection_file)
    
            self._init_kernel_manager()
    
    
    
    app = QtGui.QApplication([])
    kernelapp = IPythonLocalKernelApp.instance()
    kernelapp.start()
    namespace = kernelapp.get_user_namespace()
    widget = IPythonConsoleQtWidget()
    widget.set_default_style(colors='linux')
    widget.connect_kernel(connection_file=kernelapp.get_connection_file())
    # if you won't to connect to remote kernel:
    # widget.connect_kernel(connection_file='kernel-16098.json')
    
    widget.show()
    
    namespace["widget"] = widget
    namespace["QtGui"]=QtGui
    namespace["QtCore"]=QtCore
    
    app.exec_()
    
    0 讨论(0)
  • 2020-11-29 01:36

    It sounds like you did something similar to my Veusz application, https://veusz.github.io/. I thought you might find it useful to see a more complete implementation. I can't post hyperlinks but have a look at windows/consolewindow.py for the widget class. Commands are executed by the document/commandinterpreter.py class. The interface is defined in document/commandinterface.py. It's mostly done manipulating a dict however.

    0 讨论(0)
提交回复
热议问题