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_()
Related
I am trying to display console output of a python script in a QplainTextEdit widget in PyQt5.
I am getting this error:
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I have defined my objects in the pyqt GUI file and I believe that I have all the imports.
Update
I have amended the code in this question:
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtWidgets import QFileDialog, QPlainTextEdit
from PyQt5 import QtCore, QtGui, QtWidgets
from PIL import Image, ImageQt, ImageEnhance
# from PyQt5.QtGui import Qt
from pyqtgraph.examples.text import text
from covid19gui_V3 import Ui_MainWindow
import os
import sys
input_img = Image.open("/home/ironmantis7x/Documents/Maverick_AI/Python/keras-covid-19/maverickAI30k.png")
text_edit = QPlainTextEdit()
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
textWritten = QtCore.pyqtSignal(str)
def __init__(self, parent=None, **kwargs):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.ShowIButton.clicked.connect(self.do_test)
self.chooseStudy.clicked.connect(self.do_choosestudy)
self.RunButton_3.clicked.connect(self.do_runstudy)
self.scene = QtWidgets.QGraphicsScene(self)
self.graphicsView.setScene(self.scene)
w, h = input_img.size
self.pixmap_item = self.scene.addPixmap(QtGui.QPixmap())
# self.graphicsView.fitInView(QRectF(0, 0, w, h), Qt.KeepAspectRatio)
self.graphicsView.update()
self.plainTextEdit.update()
self.level = 1
self.enhancer = None
self.timer = QtCore.QTimer(interval=500, timeout=self.on_timeout)
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
def write(self, text):
self.textWritten.emit(str(text))
#QtCore.pyqtSlot()
def do_test(self):
# input_img = Image.open("/home/ironmantis7x/Documents/Maverick_AI/Python/keras-covid-19/maverickAI30k.png")
self.enhancer = ImageEnhance.Brightness(input_img)
self.timer.start()
self.ShowIButton.setDisabled(True)
#QtCore.pyqtSlot()
def on_timeout(self):
if self.enhancer is not None:
result_img = self.enhancer.enhance(self.level)
qimage = ImageQt.ImageQt(result_img)
self.pixmap_item.setPixmap(QtGui.QPixmap.fromImage(qimage))
if self.level > 7:
self.timer.stop()
self.enhancer = None
self.level = 0
self.ShowIButton.setDisabled(False)
self.level = 1
self.ShowIButton.setDisabled(False)
#QtCore.pyqtSlot()
def do_choosestudy(self):
dlg = QFileDialog()
dlg.setFileMode(QFileDialog.AnyFile)
if dlg.exec_():
filenames = dlg.selectedFiles()
f = open(filenames[0], 'r')
#QtCore.pyqtSlot()
def do_runstudy(self):
os.system("df -h")
# filetext = open('screenout.txt').read()
# filetext.close()
# textViewValue = self.plainTextEdit.toPlainText()
# QPlainTextEdit.appendPlainText(self, str(textViewValue))
# sys.stdout = self(textWritten=self.textWritten)
self.normalOutputWritten(text_edit)
def __del__(self):
# Restore sys.stdout
sys.stdout = sys.__stdout__
def normalOutputWritten(self, text_edit):
#cursor = self.plainTextEdit.textCursor()
#cursor.movePosition(QtGui.QTextCursor.End)
#cursor.insertText(text_edit)
self.plainTextEdit.appendPlainText(text_edit)
#self.plainTextEdit.ensureCursorVisible()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
How can I make this work correctly?
Update 2
I indeed DID do research into the topic and this is one of the main resources I used to try to solve the issue before I posted my question: How to capture output of Python's interpreter and show in a Text widget?
Update 3
I have revised my code in the post to reflect code suggestions in the link I used to help me with my issue.
I am still unable to get this to run correctly. I now get this error:
self.plainTextEdit.appendPlainText(text_edit) TypeError:
appendPlainText(self, str): argument 1 has unexpected type
'QPlainTextEdit'
I have a user interface, TableManagerWindow, that I've been maintaining and developing in Qt designer. After converting via pyuic to a *.py file, I was able to implement what Ferdinand Beyer had suggested in the link you provided above. Simple button to print text to terminal and it indeed does get appended to the QTextEdit widget via append(). Not sure this fits the bill for you for some reason, but I can vouch that it worked for me as well. I'm not savvy enough to get the nuance that is causing your issue, but figured I'd put this here just in case. Admins feel free to delete this if it's extraneous, but it works.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# Define a stream, custom class, that reports data written to it, with a Qt signal
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
class Ui_TableManagerWindow(object):
def setupUi(self, TableManagerWindow):
#define all of my widgets, layout, etc here
.
.
.
# Install a custom output stream by connecting sys.stdout to instance of EmmittingStream.
sys.stdout = EmittingStream(textWritten=self.output_terminal_written)
# Create my signal/connections for custom method
self.source_dir_button.clicked.connect(self.sourceDirButtonClicked)
self.retranslateUi(TableManagerWindow)
QtCore.QMetaObject.connectSlotsByName(TableManagerWindow)
def retranslateUi(self, TableManagerWindow):
.
.
.
#custom method that prints to output terminal. The point is to have this emmitted out to my QTextEdit widget.
def sourceDirButtonClicked(self):
for i in range(10):
print("The Source DIR button has been clicked " + str(i) + " times")
#custom method to write anything printed out to console/terminal to my QTextEdit widget via append function.
def output_terminal_written(self, text):
self.output_terminal_textEdit.append(text)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
TableManagerWindow = QtWidgets.QMainWindow()
ui = Ui_TableManagerWindow()
ui.setupUi(TableManagerWindow)
TableManagerWindow.show()
sys.exit(app.exec_())
I'm trying to get Maya's log(What it prints in script editor's output window) and add it to my custom PySide Qwidget.
Why this crashes Maya
QtWidgets, QtCore, QtGui
imoprt QtDesiner_UI
import maya.OpenMayaUI as OmUi
from shiboken2 import wrapInstance
import maya.OpenMaya as Om
def get_maya_window():
# Get Maya window
maya_main_window_ptr = OMUI.MQtUtil.mainWindow()
maya_main_window = wrapInstance(long(maya_main_window_ptr),
QtWidgets.QMainWindow)
return maya_main_window
class UiClass(QtWidgets.QMainWindow, QtDesiner_UI.MainWindow):
def __init__(self):
super(UiClass, self).__init__(get_maya_window())
OM.MCommandMessage.addCommandOutputFilterCallback(self.callback)
# some PySide Widgets here
self.console_widget = QtWidgets.QTextEdit()
self.main_form_layout.addRow(self.console_widget)
def callback(self, msg, mg, *args):
self.console_widget.append(msg)
def logic_stuff(self):
#maya commands here
import UI_Window
w = UI_Window.UiClass()
w.show()
And this doesn't
def callback(msg, mg ,*args):
console_widget.append(var)
OM.MCommandMessage.addCommandOutputFilterCallback(callback)
Here is the the working code that works partially. It should append log info into 'QtextEdit()' but after first operation Maya crashes(e.g. create sphere).
import maya.OpenMayaUI as OmUi
from shiboken2 import wrapInstance
from PySide2 import QtWidgets, QtCore, QtGui
import maya.OpenMaya as Om
def get_maya_window():
# Get Maya window
maya_main_window_ptr = OmUi.MQtUtil.mainWindow()
maya_main_window = wrapInstance(long(maya_main_window_ptr), QtWidgets.QMainWindow)
return maya_main_window
class MainWidget(QtWidgets.QMainWindow):
def __init__(self):
super(MainWidget, self).__init__(get_maya_window())
self.cent_ly = MW()
self.text_edit = QtWidgets.QTextEdit()
self.cent_ly.vlayout.addWidget(self.text_edit )
self.setCentralWidget(self.cent_ly )
self.callback_id = Om.MCommandMessage.addCommandOutputFilterCallback(self.callback)
def closeEvent(self, event):
Om.MMessage.removeCallback(self.callback_id)
def callback(self, message):
self.text_edit.append(message.strip())
class MW(QtWidgets.QWidget):
def __init__(self):
super(MW , self).__init__()
self.vlayout = QtWidgets.QVBoxLayout()
self.setLayout(self.vlayout)
w = MainWidget()
w.show()
And this one works without crashing Maya
import maya.OpenMayaUI as OmUi
from shiboken2 import wrapInstance
from PySide2 import QtWidgets, QtCore, QtGui
import maya.OpenMaya as Om
def get_maya_window():
# Get Maya window
maya_main_window_ptr = OmUi.MQtUtil.mainWindow()
maya_main_window = wrapInstance(long(maya_main_window_ptr), QtWidgets.QMainWindow)
return maya_main_window
class MainWidget(QtWidgets.QMainWindow):
def __init__(self):
super(MainWidget, self).__init__(get_maya_window())
self.cent_ly = MW()
self.text_edit = QtWidgets.QTextEdit()
self.cent_ly.vlayout.addWidget(self.text_edit)
self.setCentralWidget(self.cent_ly )
class MW(QtWidgets.QWidget):
def __init__(self):
super(MW , self).__init__()
self.vlayout = QtWidgets.QVBoxLayout()
self.setLayout(self.vlayout)
w = MainWidget()
w.show()
def callback(message, *args):
w.text_edit.append(message.strip())
callback_id = Om.MCommandMessage.addCommandOutputFilterCallback(callback)
# Om.MMessage.removeCallback(callback_id)
You need to remove the callback when you close the window. I was testing your code and it worked the first time, but subsequent times froze Maya.
class UiClass(QtWidgets.QMainWindow):
def __init__(self):
super(UiClass, self).__init__(get_maya_window())
self.callback_id = Om.MCommandMessage.addCommandOutputFilterCallback(self.callback)
def closeEvent(self, event):
Om.MMessage.removeCallback(self.callback_id)
Like #ababak said, the crashes happen because the object was destroyed, which happens when you close the window. So before that happens, you need to remove the callback.
You haven't shown the complete code of the first sample.
Those crashes happen when the callback is fired up but the original object has already been destroyed.
I assume you create the UiClass object but later it is garbage collected.
The second sample does not crash as you define your callback function in a global scope and it stays valid until you quit Maya.
working on simple GUI project. I've got some code from online,and found out how to connect the IP-webcam app, but the question is how do I use this code in my PyQt4 GUI so that the visual of the camera will be shown in the scroll-area widget.
This is the code i used:
import urllib
import cv2
import numpy as np
url='http://192.168.0.100:8080/shot.jpg'
while True:
imgResp=urllib.urlopen(url)
imgNp=np.array(bytearray(imgResp.read()),dtype=np.uint8)
img=cv2.imdecode(imgNp,-1)
# all the opencv processing is done here
cv2.imshow('test',img)
if ord('q')==cv2.waitKey(10):
exit(0)
As #furas points out, a possible option is to use numpy and cv2 to convert it to QPixmap and display it in a QLabel, and so that it looks like streaming run it in a loop.
But instead of getting complicated with all of the above, the simplest thing is to use QtNetwork to get the bytes and convert it directly to QPixmap and send it through signals:
from PyQt4 import QtCore, QtGui, QtNetwork
class IPWebcam(QtCore.QObject):
pixmapChanged = QtCore.pyqtSignal(QtGui.QPixmap)
def __init__(self, url, parent=None):
super(IPWebcam, self).__init__(parent)
self._url = url
self.m_manager = QtNetwork.QNetworkAccessManager(self)
self.m_manager.finished.connect(self._on_finished)
self.m_stopped = True
def start(self):
self.m_stopped = False
self._launch_request()
def stop(self):
self.m_stopped = True
def _launch_request(self):
request = QtNetwork.QNetworkRequest(QtCore.QUrl(self._url))
self.m_manager.get(request)
#QtCore.pyqtSlot(QtNetwork.QNetworkReply)
def _on_finished(self, reply):
ba = reply.readAll()
pixmap = QtGui.QPixmap()
if pixmap.loadFromData(ba):
self.pixmapChanged.emit(pixmap)
if not self.m_stopped:
self._launch_request()
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.m_label = QtGui.QLabel()
self.m_button = QtGui.QPushButton(
"Start", clicked=self.onClicked, checkable=True
)
lay = QtGui.QVBoxLayout(self)
lay.addWidget(self.m_label)
lay.addWidget(self.m_button)
self.resize(640, 480)
url = "http://192.168.0.100:8080/shot.jpg"
self.m_webcam = IPWebcam(url, self)
self.m_webcam.pixmapChanged.connect(self.m_label.setPixmap)
#QtCore.pyqtSlot(bool)
def onClicked(self, checked):
if checked:
self.m_button.setText("Stop")
self.m_webcam.start()
else:
self.m_button.setText("Start")
self.m_webcam.stop()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
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_())
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: