Why windows user affects how built python application calls pyside/pyqt paintEvent? - python

I've built python 3.3 application using pyqt4 and cx_freeze under windows user 'user1'.
If I run exe as windows 'user1' everything works.
If I run exe as windows 'user2' buttons in app are in wrong positions. I've figured out that widgets paintEvent is not being called (that's where i position buttons - don't ask why :)).
Any ideas why paintEvent method would be called differently just because different windows user is running exe?
Thank you for any insight!
UPDATE:
I seems that a single line is the problem. Take a look at commented out line in init method of MainWindow class (test3.py). At the bottom I attached images showing what happens for different windows users running compiled exe if this line (un)commented.
Why is this happening?
test3.py code to recreate the problem:
from PyQt4 import QtGui as pqg
from PyQt4 import QtCore as pqc
import sys
import pyqtgraph.opengl as gl
from pyqtgraph.opengl.shaders import *
import numpy as np
class AnimWidgBase(pqg.QWidget):
def __init__(self, *args, **kwargs):
super(AnimWidgBase, self).__init__(*args, **kwargs)
# Create 3D graphics widget
self.model_view = GLView()
# Create layout and add widgets
self.create_layout()
def create_layout(self):
vbox = pqg.QVBoxLayout()
vbox.addWidget(self.model_view)
self.setLayout(vbox)
class GLView(gl.GLViewWidget):
def __init__(self, parent=None):
super(GLView, self).__init__(parent)
self.setSizePolicy(pqg.QSizePolicy.Expanding, pqg.QSizePolicy.Expanding)
self.default_width = 500
self.default_height = 500
self.setMinimumSize(pqc.QSize(self.default_width, self.default_height))
def paintGL(self, *args, **kwargs):
gl.GLViewWidget.paintGL(self, *args, **kwargs)
#for testing
self.loc=np.array([[1,1,1],[2,2,2],[3,3,3]])
self.data=np.array(['1','2','3'])
#print gcs labels
for i in [0,1,2]:
self.renderText(self.loc[i,0], self.loc[i,1], self.loc[i,2],self.data[i])
class GeometryWidget(AnimWidgBase):
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
def create_layout(self):
super(self.__class__,self).create_layout()
self._button = pqg.QPushButton('Test button', self)
def paintEvent(self,event):
# Position the button so that it moves when resizing
window_width=self.rect().width()
window_height=self.rect().height()
w = 140 #this is overridden by css
h = 33
x = (window_width - w)
y = 0.2*window_height
self._button.setGeometry(x,y,w,h)
class MainWindow(pqg.QFrame):
"""Main window of the application.
"""
def __init__(self):
"""Constructor, builds the GUI."""
super(MainWindow, self).__init__()
self.stacked_widget = pqg.QStackedWidget()
self.vbox_gobal = pqg.QVBoxLayout()
self.vbox_gobal.addWidget(self.stacked_widget)
self.setLayout(self.vbox_gobal)
self.load_geometry()
# Uncomment this line to prevent GeometryWidget paintEvent being called !
#self.stacked_widget.setContentsMargins(-10, -10, -10, -10)
# -----------
def load_geometry(self):
self.geom_widg=GeometryWidget()
self.stacked_widget.addWidget(self.geom_widg)
self.stacked_widget.setCurrentWidget(self.geom_widg)
def main():
app = pqg.QApplication(sys.argv)
app.processEvents()
main_window = MainWindow()
app.processEvents()
main_window.show()
return sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here is setup.py:
from cx_Freeze import setup, Executable
base = None
options = {
'build_exe': {
'packages': ['OpenGL'],
'includes': ['PyQt4'],
},
}
executables = [
Executable('test3.py', base=base)
]
setup(name='test',
version='0.1',
description='test for testing tests',
options=options,
executables=executables
)
User1, offending line commented:
User1, offending line UNcommented:
User2, offending line commented:
User2, offending line UNcommented:

Related

How multiple run ros.init_node () in one python script?

I have written a program in python with ROS. It contains a GUI for "robot teleoperation", and in its MainWindow I added 3 widgets (rviz, joystick, button panel). When I start MainWindow I get the following error:
raise rospy.exceptions.ROSException("rospy.init_node() has already been called with different arguments: "+str(_init_node_args))
rospy.exceptions.ROSException: rospy.init_node() has already been
called with different arguments: ('teleop_twist_keyboard',
['MainWindow.py'], False, None, False, False).
Joystick.py and Button.py contain ros.init_node() function. In MainWindow I instantiate Joystick and Button class and add them to MainWindow. I need to call ros.init_node() several times to communicate with various nodes.
directory structure
main window example
code main window
import sys
import PyQt5
import threading
from Joystick import Joystick
from QWidget_rviz import Rviz
from BaseArmPosition import BaseArmPosition
class MainWindow(PyQt5.QtWidgets.QMainWindow):
def __init__(self,*args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.title = 'Robot teleoperation'
self.left = 10
self.top = 10
self.width = 1920
self.height = 1080
self.joy = Joystick(maxDistance=50,MinimumSize=100,EclipseX=-20,EclipseY=40)
self.rviz = Rviz()
#self.arm_position = BaseArmPosition()
self.initUI()
def initUI(self):
self.central_widget = PyQt5.QtWidgets.QWidget()
self.setCentralWidget(self.central_widget)
grid = PyQt5.QtWidgets.QGridLayout(self.centralWidget())
grid.addWidget(self.joy, 0, 2)
grid.addWidget(self.rviz, 0, 0)
#grid.addWidget(self.arm_position, 0, 1)
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.show()
app = PyQt5.QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
code buttons widget, joistick.py has the same
import PyQt5
import rospy
from std_msgs.msg import String
class BaseArmPosition(PyQt5.QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super(BaseArmPosition, self).__init__(*args, **kwargs)
self.initUI()
def initUI(self):
self.pushButton_moveInit = PyQt5.QtWidgets.QPushButton("moveInit",self)
self.pushButton_moveLeft = PyQt5.QtWidgets.QPushButton("moveLeft",self)
self.pushButton_moveRight = PyQt5.QtWidgets.QPushButton("moveRight",self)
layout = PyQt5.QtWidgets.QVBoxLayout(self)
layout.addWidget(self.pushButton_moveRight)
layout.addWidget(self.pushButton_moveLeft)
layout.addWidget(self.pushButton_moveInit)
self.pushButton_moveInit.clicked.connect(self.moveInit)
self.pushButton_moveLeft.clicked.connect(self.moveLeft)
self.pushButton_moveRight.clicked.connect(self.moveRight)
rospy.init_node("controller_ur")
self.pub_arm = rospy.Publisher("/controller_ur/order",String,queue_size=1)
def moveInit(self):
moveInit = "moveInit"
msg = String()
msg.data = moveInit
self.pub_arm.publish(moveInit)
def moveLeft(self):
moveInit = "moveLeft"
msg = String()
msg.data = moveInit
self.pub_arm.publish(moveInit)
def moveRight(self):
moveInit = "moveRight"
msg = String()
msg.data = moveInit
self.pub_arm.publish(moveInit)
A ROS node can only be initialized once in your program. You should centralize the initialization of the node at the beginning of the main script, and the rest of imported modules should not try to initialize a new node, as that is not allowed.
If what you want is the different sub-modules to deal with different data, then you should just create separate topics within the same node.
Put this somewhere towards the top of your script before import statements.
import os
os.system("rosrun my_package_name my_node_script.py")

QWidget not loading child elements when QRunnable is also called

I'm working in a data processing desktop application with Python 3.7 and PySide2 on which requires me to load data from several large (approx 250k rows) excel files into the program's processing library. For this I've set up in my application a simple popup (called LoadingPopup) which includes a rotating gif and a simple caption, and also some code that loads the data from the excel files into a global object using pandas. Both of these things work as intended when run on their own, but if I happen to create a loading dialog and a QRunnable worker in the same scope of my codebase, the widgets contained in loading widget (a gif and a simple caption) will simply not show.
I've tried changing the parent type for my widget from QDialog to QWidget, or initializing the popup (the start() function) both outside and inside the widget. I'm not very experienced with Qt5 so I don't know what else to do.
import sys, time, traceback
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
from TsrUtils import PathUtils
class WorkerSignals(QObject):
finished = Signal()
error = Signal(tuple)
result = Signal(object)
class TsrWorker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(TsrWorker, self).__init__()
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
#Slot()
def run(self):
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
else:
self.signals.result.emit(result)
finally:
self.signals.finished.emit()
class LoadingPopup(QWidget):
def __init__(self):
super().__init__()
self.message = "Cargando"
self.setMinimumSize(350,300)
self.setWindowIcon(\
QIcon(PathUtils.ruta_absoluta('resources/icons/tsr.png')))
self.setWindowTitle(self.message)
self.layout = QVBoxLayout(self)
self.setLayout(self.layout)
self.movie = QMovie(self)
self.movie.setFileName("./resources/img/spinner.gif")
self.movie.setCacheMode(QMovie.CacheAll)
self.movie.start()
self.loading = QLabel(self)
self.loading.setMovie(self.movie)
self.loading.setAlignment(Qt.AlignCenter)
self.layout.addWidget(self.loading)
self.lbl = QLabel(self.message, self)
self.lbl.setAlignment(Qt.AlignCenter)
self.lbl.setStyleSheet("font: 15pt")
self.layout.addWidget(self.lbl)
class MyMainApp(QApplication):
def __init__(self, args):
super().__init__()
self.l = LoadingPopup()
self.l.show()
w = TsrWorker(time.sleep, 5)
w.signals.finished.connect(self.terminado)
w.run()
def terminado(self):
print('timer finished')
self.l.hide()
if __name__ == '__main__':
app = MyMainApp(sys.argv)
sys.exit(app.exec_())
I've changed the actual data loading part of the application in the example with a time.sleep function. MY expected results are that I should be able to have the LoadingPopup show up with a gif moving, and then it should close once the QRunnable finishes.
You should not call the run method directly since you will have the heavy task run on the GUI thread freezing it. You must launch it using QThreadPool:
class MyMainApp(QApplication):
def __init__(self, args):
super().__init__()
self.l = LoadingPopup()
self.l.show()
w = TsrWorker(time.sleep, 5)
w.signals.finished.connect(self.terminado)
# w.run()
QThreadPool.globalInstance().start(w) # <---
def terminado(self):
print('timer finished')
self.l.hide()

Using the QWizard next slot in pyside

I've created a wizard with Pyside.
On one page, I create a new thread, which starts an installer.
When the installer is ready, the Wizard should go forward automatically, without clicking the next button.
I've read the pyside documentation, and now my understanding is, that QWizard has a next function. But how can I use this function?
My test is working fine:
from PySide.QtGui import *
from PySide.QtCore import *
...
...
class Install(QWizardPage):
def __init__(self, parent=None):
super(Install, self).__init__(parent)
def initializePage(self):
self.setTitle("Install")
label = QLabel("Install")
label.setWordWrap(True)
layout = QVBoxLayout()
self.progressBar = QProgressBar(self)
self.progressBar.setRange(0,1)
self.progressBar.setRange(0,0)
layout.addWidget(self.progressBar)
layout.addWidget(label)
self.setLayout(layout)
self.myTask = TaskThread()
self.myTask.start()
self.myTask.taskFinished.connect(self.Finished)
def Finished(self):
print("finish")
def isComplete(self):
return False
class TaskThread(QThread):
taskFinished = Signal()
def run(self):
a = 0
while a != 10000:
print("test")
a += 1
self.taskFinished.emit()
And when I try to use the next function I try:
self.CallNext = QWizard().next
self.myTask.taskFinished.connect(self.CallNext)
And also:
self.myTask.taskFinished.connect(QWizard().next)
But this is not working
This connection should be done in the context where the QWizard and QWizardPage exist, but before that we must move the creation of the QThread to the constructor, for example in the following example I do in the main:
class Install(QWizardPage):
def __init__(self, parent=None):
super(Install, self).__init__(parent)
self.myTask = TaskThread()
def initializePage(self):
[...]
self.setLayout(layout)
self.myTask.start()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
wizard = QWizard()
install = Install()
install.setTitle("installer")
install.myTask.taskFinished.connect(wizard.next)
wizard.addPage(install)
page = QWizardPage()
page.setTitle("next Page")
wizard.addPage(page)
wizard.show()
sys.exit(wizard.exec_())

Multithreading with PySide : is my structure viable?

I'm running into a very strange error while using QThread in PySide. As I run my code, when I click on the button that is supposed to launch the thread and send Signal, I get :
AttributeError: 'builtin_function_or_method' object has no attribute 'displayText'
at line :
self.displayMessage.connect(self.window.displayText)
NB : displayText is a method that I defined in my MainWindow(QWidget) class, while displayMessage is the signal to be emitted.
This error seems, according to the Internet, to occur in various situations, and I couldn't find one that suits my case yet. Therefore I have 2 questions :
Have you guys ever met this error before in a similar situation, and could you (if yes) give me some tips ?
Am I doing it right ? I can't post my code directly down here, so I created a short verifiable example in which I used the same process. Unfortunately, it seems to work perfectly. Please tell me if at least my construction is correct.
Thank you very much.
EDIT : I eventually figured out that I have forgotten a self somewhere in my big code, but the actual error was hidden to me behind this one that I didn't know. I'm still interested in whether or not this code is reliable, and am fully open to suggestions of improvement.
Without thread
When you click "GO !" and try to move the window, you can see that it freezes for a second.
#!/usr/bin/env python
# -*- encoding : utf-8 -*-
import sys
import time
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QWidget):
def __init__(self, qt_app):
QWidget.__init__(self)
worker = Worker(self)
self.qt_app = qt_app
self.setGeometry(100, 100, 220, 40)
self.infoPanel = QTextEdit(self)
self.infoPanel.setReadOnly(True)
self.goButton = QPushButton('GO !', self)
self.goButton.clicked.connect(lambda: worker.displayOnWindow())
self.layout = QVBoxLayout()
self.layout.addWidget(self.infoPanel)
self.layout.addWidget(self.goButton)
self.setLayout(self.layout)
def launch(self):
self.show()
self.qt_app.exec_()
class Worker():
def __init__(self, window):
self.counter = 0
self.window = window
def displayOnWindow(self):
time.sleep(1)
self.window.infoPanel.append(str(self.counter))
self.counter += 1
if __name__=='__main__':
qt_app = QApplication(sys.argv)
mw = MainWindow(qt_app)
mw.launch()
With thread
Now you can move the window without trouble, since the sleeping thread is not the one that displays the window. I've written a sub-class for my thread because in the original code there are some functions that will be called by several buttons.
#!/usr/bin/env python
# -*- encoding : utf-8 -*-
import sys
import time
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QWidget):
def __init__(self, qt_app):
QWidget.__init__(self)
worker = SubWorker(self)
self.qt_app = qt_app
self.setGeometry(100, 100, 220, 40)
self.infoPanel = QTextEdit(self)
self.infoPanel.setReadOnly(True)
self.goButton = QPushButton('GO !', self)
self.goButton.clicked.connect(lambda: worker.start())
self.layout = QVBoxLayout()
self.layout.addWidget(self.infoPanel)
self.layout.addWidget(self.goButton)
self.setLayout(self.layout)
def launch(self):
self.show()
self.qt_app.exec_()
#Slot(int)
def displayOnWindow(self, i):
self.infoPanel.append(str(i))
class Worker(QThread):
displayMessage = Signal(int)
def __init__(self, window):
QThread.__init__(self)
self.counter = 0
self.window = window
self.displayMessage.connect(self.window.displayOnWindow)
def run(self, *args, **kwargs):
return QThread.run(self, *args, **kwargs)
class SubWorker(Worker):
def __init__(self, window):
Worker.__init__(self, window)
def run(self):
time.sleep(1)
self.displayMessage.emit(self.counter)
self.counter += 1
if __name__=='__main__':
qt_app = QApplication(sys.argv)
mw = MainWindow(qt_app)
mw.launch()

Embedding IPython Qt console in a PyQt application

I'd like to embed an IPython qt console widget in a PyQt application I am working on. The code provided below (and adapted from https://stackoverflow.com/a/9796491/1332492) Accomplishes this for IPython v0.12. However, this crashes in IPython v0.13 at the line self.heartbeat.start() with RuntimeError: threads can only be started once. Commenting out this line brings up the widget, but doesn't respond to user input.
Does anyone know how to achieve the equivalent functionality for IPython v0.13?
"""
Adapted from
https://stackoverflow.com/a/9796491/1332492
"""
import os
import atexit
from IPython.zmq.ipkernel import IPKernelApp
from IPython.lib.kernel import find_connection_file
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
from PyQt4 import QtCore
class IPythonLocalKernelApp(IPKernelApp):
DEFAULT_INSTANCE_ARGS = ['']
#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()
class IPythonConsoleQtWidget(RichIPythonWidget):
_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):
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()
def main(**kwargs):
kernelapp = IPythonLocalKernelApp.instance()
kernelapp.start()
widget = IPythonConsoleQtWidget()
widget.connect_kernel(connection_file=kernelapp.connection_file)
widget.show()
return widget
if __name__ == "__main__":
from PyQt4.QtGui import QApplication
app = QApplication([''])
main()
app.exec_()
Traceback for v0.13
RuntimeError Traceback (most recent call last)
/Users/beaumont/terminal.py in <module>()
80 from PyQt4.QtGui import QApplication
81 app = QApplication([''])
---> 82 main()
global main = <function main at 0x106d0c848>
83 app.exec_()
/Users/beaumont/terminal.py in main(**kwargs={})
69 def main(**kwargs):
70 kernelapp = IPythonLocalKernelApp.instance()
---> 71 kernelapp.start()
kernelapp.start = <bound method IPythonLocalKernelApp.start of <__main__.IPythonLocalKernelApp object at 0x106d10590>>
72
73 widget = IPythonConsoleQtWidget()
/Users/beaumont/terminal.py in start(self=<__main__.IPythonLocalKernelApp object>, argv=[''])
33 """
34 self.initialize(argv)
---> 35 self.heartbeat.start()
self.heartbeat.start = <bound method Heartbeat.start of <Heartbeat(Thread-1, started daemon 4458577920)>>
36
37 if self.poller is not None:
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyc in start(self=<Heartbeat(Thread-1, started daemon 4458577920)>)
487 raise RuntimeError("thread.__init__() not called")
488 if self.__started.is_set():
--> 489 raise RuntimeError("threads can only be started once")
global RuntimeError = undefined
490 if __debug__:
491 self._note("%s.start(): starting thread", self)
RuntimeError: threads can only be started once
Ok, this code seems to do the trick (i.e. it puts a non-blocking ipython interpreter in a Qt widget, which can be embedded into other widgets). Keywords passed to terminal_widget get added to the namespace of the widget
import atexit
from IPython.zmq.ipkernel import IPKernelApp
from IPython.lib.kernel import find_connection_file
from IPython.frontend.qt.kernelmanager import QtKernelManager
from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.utils.traitlets import TraitError
from PyQt4 import QtGui, QtCore
def event_loop(kernel):
kernel.timer = QtCore.QTimer()
kernel.timer.timeout.connect(kernel.do_one_iteration)
kernel.timer.start(1000*kernel._poll_interval)
def default_kernel_app():
app = IPKernelApp.instance()
app.initialize(['python', '--pylab=qt'])
app.kernel.eventloop = event_loop
return app
def default_manager(kernel):
connection_file = find_connection_file(kernel.connection_file)
manager = QtKernelManager(connection_file=connection_file)
manager.load_connection_file()
manager.start_channels()
atexit.register(manager.cleanup_connection_file)
return manager
def console_widget(manager):
try: # Ipython v0.13
widget = RichIPythonWidget(gui_completion='droplist')
except TraitError: # IPython v0.12
widget = RichIPythonWidget(gui_completion=True)
widget.kernel_manager = manager
return widget
def terminal_widget(**kwargs):
kernel_app = default_kernel_app()
manager = default_manager(kernel_app)
widget = console_widget(manager)
#update namespace
kernel_app.shell.user_ns.update(kwargs)
kernel_app.start()
return widget
app = QtGui.QApplication([])
widget = terminal_widget(testing=123)
widget.show()
app.exec_()
The accepted answer by #ChrisB is fine for IPython version 0.13, but it doesn't work with newer versions. From the examples section of the IPython kernel repository on github, this is the way to do it in v1.x+ (currently tested with 4.0.1), which has the feature that the console and kernel are in the same process.
Here is an example, based on the official one, which gives a convenience class that can be easily plugged into an application. It's setup to work with pyqt4 and IPython 4.0.1 on Python 2.7:
(Note: you'll need to install the ipykernel and qtconsole packages)
# Set the QT API to PyQt4
import os
os.environ['QT_API'] = 'pyqt'
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
from PyQt4.QtGui import *
# Import the console machinery from ipython
from qtconsole.rich_ipython_widget import RichIPythonWidget
from qtconsole.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport
class QIPythonWidget(RichIPythonWidget):
""" Convenience class for a live IPython console widget. We can replace the standard banner using the customBanner argument"""
def __init__(self,customBanner=None,*args,**kwargs):
if not customBanner is None: self.banner=customBanner
super(QIPythonWidget, self).__init__(*args,**kwargs)
self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel()
kernel_manager.kernel.gui = 'qt4'
self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels()
def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()
guisupport.get_app_qt4().exit()
self.exit_requested.connect(stop)
def pushVariables(self,variableDict):
""" Given a dictionary containing name / value pairs, push those variables to the IPython console widget """
self.kernel_manager.kernel.shell.push(variableDict)
def clearTerminal(self):
""" Clears the terminal """
self._control.clear()
def printText(self,text):
""" Prints some plain text to the console """
self._append_plain_text(text)
def executeCommand(self,command):
""" Execute a command in the frame of the console widget """
self._execute(command,False)
class ExampleWidget(QWidget):
""" Main GUI Widget including a button and IPython Console widget inside vertical layout """
def __init__(self, parent=None):
super(ExampleWidget, self).__init__(parent)
layout = QVBoxLayout(self)
self.button = QPushButton('Another widget')
ipyConsole = QIPythonWidget(customBanner="Welcome to the embedded ipython console\n")
layout.addWidget(self.button)
layout.addWidget(ipyConsole)
# This allows the variable foo and method print_process_id to be accessed from the ipython console
ipyConsole.pushVariables({"foo":43,"print_process_id":print_process_id})
ipyConsole.printText("The variable 'foo' and the method 'print_process_id()' are available. Use the 'whos' command for information.")
def print_process_id():
print 'Process ID is:', os.getpid()
def main():
app = QApplication([])
widget = ExampleWidget()
widget.show()
app.exec_()
if __name__ == '__main__':
main()
A 2016 update working in PyQt5:
from qtpy import QtGui
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport
class ConsoleWidget(RichJupyterWidget):
def __init__(self, customBanner=None, *args, **kwargs):
super(ConsoleWidget, self).__init__(*args, **kwargs)
if customBanner is not None:
self.banner = customBanner
self.font_size = 6
self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel(show_banner=False)
kernel_manager.kernel.gui = 'qt'
self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels()
def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()
guisupport.get_app_qt().exit()
self.exit_requested.connect(stop)
def push_vars(self, variableDict):
"""
Given a dictionary containing name / value pairs, push those variables
to the Jupyter console widget
"""
self.kernel_manager.kernel.shell.push(variableDict)
def clear(self):
"""
Clears the terminal
"""
self._control.clear()
# self.kernel_manager
def print_text(self, text):
"""
Prints some plain text to the console
"""
self._append_plain_text(text)
def execute_command(self, command):
"""
Execute a command in the frame of the console widget
"""
self._execute(command, False)
if __name__ == '__main__':
app = QtGui.QApplication([])
widget = ConsoleWidget()
widget.show()
app.exec_()
IPython 0.13 version with some cleanups:
#coding: utf-8
'''
Updated for IPython 0.13
Created on 18-03-2012
Updated: 11-09-2012
#author: Paweł Jarosz
'''
import atexit
from PySide import QtCore, QtGui
from IPython.zmq.ipkernel import IPKernelApp
from IPython.lib.kernel import find_connection_file
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
DEFAULT_INSTANCE_ARGS = ['qtconsole','--pylab=inline', '--colors=linux']
class IPythonLocalKernelApp(IPKernelApp):
#catch_config_error
def initialize(self, argv=DEFAULT_INSTANCE_ARGS):
"""
argv: IPython args
example:
app = QtGui.QApplication([])
kernelapp = IPythonLocalKernelApp.instance()
kernelapp.initialize()
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 you don't need kernelapp part, just widget part and:
# widget.connect_kernel(connection_file='kernel-16098.json')
# where kernel-16098.json is the kernel name
widget.show()
namespace = kernelapp.get_user_namespace()
nxxx = 12
namespace["widget"] = widget
namespace["QtGui"]=QtGui
namespace["nxxx"]=nxxx
app.exec_()
"""
super(IPythonLocalKernelApp, self).initialize(argv)
self.kernel.eventloop = self.loop_qt4_nonblocking
self.kernel.start()
self.start()
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 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):
def connect_kernel(self, connection_file, heartbeat = False):
"""
connection_file: str - is the connection file name, for example 'kernel-16098.json'
heartbeat: bool - workaround, needed for right click/save as ... errors ... i don't know how to
fix this issue. Anyone knows? Anyway it needs more testing
example1 (standalone):
app = QtGui.QApplication([])
widget = IPythonConsoleQtWidget()
widget.set_default_style(colors='linux')
widget.connect_kernel(connection_file='some connection file name')
app.exec_()
example2 (IPythonLocalKernelApp):
app = QtGui.QApplication([])
kernelapp = IPythonLocalKernelApp.instance()
kernelapp.initialize()
widget = IPythonConsoleQtWidget()
# Green text, black background ;)
widget.set_default_style(colors='linux')
widget.connect_kernel(connection_file='kernelapp.get_connection_file())
app.exec_()
"""
km = QtKernelManager(connection_file=find_connection_file(connection_file), config=self.config)
km.load_connection_file()
km.start_channels(hb=heartbeat)
self.kernel_manager = km
atexit.register(self.kernel_manager.cleanup_connection_file)
def main():
app = QtGui.QApplication([])
kernelapp = IPythonLocalKernelApp.instance()
kernelapp.initialize()
widget = IPythonConsoleQtWidget()
widget.set_default_style(colors='linux')
widget.connect_kernel(connection_file=kernelapp.get_connection_file())
# if you connect to outside app kernel you don't need kernelapp part,
# just widget part and:
# widget.connect_kernel(connection_file='kernel-16098.json')
# where kernel-16098.json is the kernel name
widget.show()
namespace = kernelapp.get_user_namespace()
nxxx = 12
namespace["widget"] = widget
namespace["QtGui"]=QtGui
namespace["nxxx"]=nxxx
app.exec_()
if __name__=='__main__':
main()
Possibly helping others researching this: I came across this example:
https://github.com/gpoulin/python-test/blob/master/embedded_qtconsole.py
Tested and works with PySide, IPython 2.1.0, Python 3.4.1. It appears I can even use matplotlib directly.
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
from PySide import QtGui, QtCore
class EmbedIPython(RichIPythonWidget):
def __init__(self, **kwarg):
super(RichIPythonWidget, self).__init__()
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel = self.kernel_manager.kernel
self.kernel.gui = 'qt4'
self.kernel.shell.push(kwarg)
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.textEdit = QtGui.QTextEdit()
but1 = QtGui.QPushButton('write')
but1.clicked.connect(self.but_write)
but2 = QtGui.QPushButton('read')
but2.clicked.connect(self.but_read)
self.a = {'text': ''}
self.console = EmbedIPython(testing=123, a=self.a)
self.console.kernel.shell.run_cell('%pylab qt')
vbox = QtGui.QVBoxLayout()
hbox = QtGui.QHBoxLayout()
vbox.addWidget(self.textEdit)
vbox.addWidget(self.console)
hbox.addWidget(but1)
hbox.addWidget(but2)
vbox.addLayout(hbox)
b = QtGui.QWidget()
b.setLayout(vbox)
self.setCentralWidget(b)
def but_read(self):
self.a['text'] = self.textEdit.toPlainText()
self.console.execute("print('a[\\\'text\\\'] = \"'+ a['text'] +'\"')")
def but_write(self):
self.textEdit.setText(self.a['text'])
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
Updated answer for:
Python 3.8
IPython 7.22.0
QtConsole 5.0.3
Modified from previous answer.
from qtpy.QtWidgets import QApplication
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport
class ConsoleWidget(RichJupyterWidget):
def __init__(self, customBanner=None, *args, **kwargs):
super(ConsoleWidget, self).__init__(*args, **kwargs)
if customBanner is not None:
self.banner = customBanner
self.font_size = 10
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel(show_banner=False)
self.kernel_manager.kernel.gui = 'qt'
self.kernel_client = self._kernel_manager.client()
self.kernel_client.start_channels()
def stop():
self.kernel_client.stop_channels()
self.kernel_manager.shutdown_kernel()
guisupport.get_app_qt4().exit()
self.exit_requested.connect(stop)
def push_vars(self, variableDict):
"""
Given a dictionary containing name / value pairs, push those variables
to the Jupyter console widget
"""
self.kernel_manager.kernel.shell.push(variableDict)
def clear(self):
"""
Clears the terminal
"""
self._control.clear()
def print_text(self, text):
"""
Prints some plain text to the console
"""
self._append_plain_text(text)
def execute_command(self, command):
"""
Execute a command in the frame of the console widget
"""
self._execute(command, False)
if __name__ == '__main__':
app = QApplication([])
widget = ConsoleWidget()
widget.show()
app.exec_()

Categories