Create Flap in GTK 4 with PyGObject, Libadwaita - python

I'm trying to create a Flap in Libadwaita using PyGObject but I only get an empty window.
My code looks like this:
import gi
gi.require_version(namespace='Gtk', version='4.0')
gi.require_version(namespace='Adw', version='1')
from gi.repository import Gio, Gtk
from gi.repository import Adw
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_title(title='Python and GTK 4 Adwaita: PyGObject Adw.Flap')
box = Gtk.Box()
self.set_child(child=box)
adw_flap = Adw.Flap.new()
self.set_child(child=adw_flap)
class Application(Gtk.Application):
def __init__(self):
super().__init__(application_id='adw.flap.demo',
flags=Gio.ApplicationFlags.FLAGS_NONE)
def do_startup(self):
Gtk.Application.do_startup(self)
def do_activate(self):
win = self.props.active_window
if not win:
win = MainWindow(application=self)
win.present()
def do_shutdown(self):
Gtk.Application.do_shutdown(self)
if __name__ == '__main__':
import sys
app = Application()
app.run(sys.argv)
I'm creating a GTK window with a box inside (do I need to create the box or can I place the flap directly inside the GTK window?) and inside the box I'm trying to create the flap. Where am I going wrong in my code?

I made an example using Adw.Flap() together with Gtk.StackSidebar().
# -*- coding: utf-8 -*-
"""Python e GTK 4: PyGObject Adw.Flap()."""
import gi
gi.require_version(namespace='Gtk', version='4.0')
gi.require_version(namespace='Adw', version='1')
from gi.repository import Gio, Gtk
from gi.repository import Adw
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.set_title(title='Python e GTK 4: PyGObject Adw.Flap()')
self.set_default_size(width=int(1366 / 2), height=int(768 / 2))
self.set_size_request(width=int(1366 / 2), height=int(768 / 2))
vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=12)
self.set_child(child=vbox)
headerbar = Gtk.HeaderBar.new()
self.set_titlebar(titlebar=headerbar)
# Botão que abre e fecha o Flap.
flap_Toggle_button = Gtk.ToggleButton.new()
flap_Toggle_button.set_icon_name(icon_name='sidebar-show-right-rtl-symbolic')
flap_Toggle_button.connect('clicked', self.on_flap_button_toggled)
headerbar.pack_start(child=flap_Toggle_button)
self.adw_flap = Adw.Flap.new()
self.adw_flap.set_reveal_flap(reveal_flap=False)
self.adw_flap.set_locked(locked=True)
vbox.append(child=self.adw_flap)
stack = Gtk.Stack.new()
self.adw_flap.set_content(content=stack)
# Página 1
box_page_1 = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=6)
stack.add_titled(child=box_page_1, name='pagina1', title='Página 1')
label_page_1 = Gtk.Label.new(str='Página 1')
label_page_1.set_halign(align=Gtk.Align.CENTER)
label_page_1.set_valign(align=Gtk.Align.CENTER)
label_page_1.set_hexpand(expand=True)
label_page_1.set_vexpand(expand=True)
box_page_1.append(child=label_page_1)
# Página 2
box_page_2 = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=6)
stack.add_titled(child=box_page_2, name='pagina2', title='Página 2')
label_page_2 = Gtk.Label.new(str='Página 2')
label_page_2.set_halign(align=Gtk.Align.CENTER)
label_page_2.set_valign(align=Gtk.Align.CENTER)
label_page_2.set_hexpand(expand=True)
label_page_2.set_vexpand(expand=True)
box_page_2.append(child=label_page_2)
# StackSidebar gerencia a troca entre os stack.
stack_sidebar = Gtk.StackSidebar.new()
stack_sidebar.set_stack(stack=stack)
self.adw_flap.set_flap(flap=stack_sidebar)
def on_flap_button_toggled(self, widget):
self.adw_flap.set_reveal_flap(not self.adw_flap.get_reveal_flap())
class Application(Adw.Application):
def __init__(self):
super().__init__(application_id='br.natorsc.Exemplo',
flags=Gio.ApplicationFlags.FLAGS_NONE)
def do_startup(self):
Gtk.Application.do_startup(self)
def do_activate(self):
win = self.props.active_window
if not win:
win = MainWindow(application=self)
win.present()
def do_shutdown(self):
Gtk.Application.do_shutdown(self)
if __name__ == '__main__':
import sys
app = Application()
app.run(sys.argv)
The code used as an example is in my repository:
https://github.com/natorsc/gui-python-gtk.
Whenever possible I am adding new examples.

Related

How to move from Login(Dialog) to main window (PyQt5)

I'm developing an application in Python with PyQt5 for GUI, the login screen is already working and now I don't now hoe to switch to the main screen when login is successful.
Tried to insert a mainwindow.show inside loginFunction but didn't work.
I'm coding step by step and aquiring knowledge in every step. But in this one I got stucked.
import sys
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore
from PyQt5.QtWidgets import QDialog, QApplication, QWidget, QStackedWidget
import sqlite3
class TelaBemVindo(QDialog):
def __init__(self):
super(TelaBemVindo, self).__init__()
loadUi("bem_vindo.ui", self)
self.loginscreen.clicked.connect(self.abre_login)
self.novousuario.clicked.connect(self.abre_criacao)
def abre_login(self):
login = TelaDeLogin()
widget.addWidget(login)
widget.setCurrentIndex(widget.currentIndex()+1)
def abre_criacao(self):
create = TelaDeCriacao()
widget.addWidget(create)
widget.setCurrentIndex(widget.currentIndex()+1)
class TelaDeLogin(QDialog):
def __init__(self):
super(TelaDeLogin, self).__init__()
loadUi("login.ui", self)
self.senhalogin.setEchoMode(QtWidgets.QLineEdit.Password)
self.loginbutton.clicked.connect(self.loginFunction)
def loginFunction(self):
usuario = self.usuariologin.text()
senha = self.senhalogin.text()
if len(usuario) == 0 or len(senha) == 0:
self.error.setText("Por favor preencha todos os campos")
else:
conn = sqlite3.connect("database.db")
cur = conn.cursor()
query = 'SELECT senha FROM login_info WHERE usuario=\''+usuario+"\'"
cur.execute(query)
result_pass = cur.fetchone()[0]
if result_pass == senha:
print("Logado com sucesso")
self.error.setText("")
else:
self.error.setText("Usuário ou senha inválidos")
class TelaDeCriacao(QDialog):
def __init__(self):
super(TelaDeCriacao, self).__init__()
loadUi("criacao_conta.ui", self)
self.camposenha.setEchoMode(QtWidgets.QLineEdit.Password)
self.campoconfsenha.setEchoMode(QtWidgets.QLineEdit.Password)
self.signup.clicked.connect(self.signupfunction)
def signupfunction(self):
usuario = self.campousuario.text()
senhanova = self.camposenha.text()
confsenha = self.campoconfsenha.text()
if len(usuario) == 0 or len(senhanova) == 0 or len(confsenha) == 0:
self.error.setText("Por favor preencha todos os campos")
elif senhanova != confsenha:
self.error.setText("Senhas não combinam")
else:
self.error.setText("Ainda não programei isso")
app = QApplication(sys.argv)
bem_vindo = TelaBemVindo()
widget = QStackedWidget()
widget.addWidget(bem_vindo)
widget.setFixedHeight(800)
widget.setFixedWidth(1200)
widget.show()
try:
sys.exit(app.exec())
except:
print("Saindo")
Would appreciate some help.
thanks

Switch widget in QStackWidget from a button in another file

I got two py files. One has the main window with a QStackedWidget inside, the setCurrentWidget is set according to a condition. The other file has a widget which is dynamically added into the stacked widget and set as current widget when a button in the main window is clicked.
The widget in the second file has a dialog with a button in it. What I'm trying to do is, on clicking the button inside the dialog, the dialog should be closed and the setCurrentWidget is set according to the condition and the widget is removed from the stacked widget.
Here is what I've tried:
mainwindow.py
import sys
import os
import pathlib
from PySide2.QtWidgets import *
from PySide2 import *
from PySide2.QtCore import *
from PySide2.QtGui import *
list1 = ["item1", "item2", "item3"]
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 300)
self.toolbar = QWidget()
self.toolbar.setFixedHeight(30)
self.toolbar.setStyleSheet("background: grey;")
self.button = QPushButton("Click here!")
t_layout = QHBoxLayout()
t_layout.setMargin(0)
t_layout.addWidget(self.button)
self.toolbar.setLayout(t_layout)
self.p1_label = QLabel("Such empty!")
self.p1_label.setStyleSheet("font-size: 30px;")
self.p1_label.setAlignment(Qt.AlignCenter)
self.p2_widget = QWidget()
self.p2_widget.setStyleSheet("background: orange;")
self.sw = QStackedWidget()
self.sw.addWidget(self.p1_label)
self.sw.addWidget(self.p2_widget)
if not list1:
self.sw.setCurrentWidget(self.p1_label)
else:
self.sw.setCurrentWidget(self.p2_widget)
self.mw_layout = QVBoxLayout()
self.mw_layout.addWidget(self.toolbar)
self.mw_layout.addWidget(self.sw)
self.setLayout(self.mw_layout)
def switch_widget():
import widget_test
p3 = widget_test.widget()
self.sw.addWidget(p3)
self.sw.setCurrentWidget(p3)
self.button.clicked.connect(switch_widget)
def switch_back(self):
import widget_test
p3 = widget_test.widget()
mwin = MainWindow()
sw_ = mwin.sw
sw_.removeWidget(p3)
p1 = mwin.p1_label
p2 = mwin.p2_widget
if not list1:
sw_.setCurrentWidget(p1)
else:
sw_.setCurrentWidget(p2)
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
widget.py
import sys
import os
import pathlib
import datetime
from PySide2.QtWidgets import *
from PySide2 import *
from PySide2.QtCore import *
from PySide2.QtGui import *
class widget(QWidget):
def __init__(self):
super(widget, self).__init__()
self.setStyleSheet("background: teal;")
widget_label = QLabel("fluid dynamics is cool")
show_pop_up = QPushButton("show pop up")
pop_up = QDialog(self)
pop_up_label = QLabel("click below to, hopefully, get outta here")
get_outta_here = QPushButton("get outta here")
pop_up_layout = QVBoxLayout()
pop_up_layout.addWidget(pop_up_label)
pop_up_layout.addWidget(get_outta_here)
pop_up.setLayout(pop_up_layout)
def show_popup():
pop_up.show()
def get_out():
from main_test import MainWindow
MainWindow.switch_back(self)
pop_up.reject()
get_outta_here.clicked.connect(get_out)
show_pop_up.clicked.connect(show_popup)
widget_layout = QVBoxLayout()
widget_layout.addWidget(widget_label)
widget_layout.addWidget(show_pop_up)
self.setLayout(widget_layout)
I could merge the code together and make it work but I'm trying to keep the directory clean.
There is a lot going on here, but let's break it down.
The main problem seems to be juggling between modules. Eventhough it might seem appealing to import the modules back and forth, it doesn't really work. What you need to look for, is the built-in Signals module that you can utilize.
Another bigger problem is that you are re-assigning some attributes eventhough you really shouldn't. You also should revisit the condition you are using to assign the .setCurrentWidget. Currently the condition reads as if list1 doesn't exist, do this. Else, do the other. Also, switch_widget should be outside of the def __init__(self):.
I rewrote some parts of the code to make it work with signals as an example for you.
mainwindow.py
import sys
import os
import pathlib
from PySide2.QtWidgets import *
from PySide2 import *
from PySide2.QtCore import *
from PySide2.QtGui import *
from widget_test import widget
list1 = ["item1", "item2", "item3"]
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 300)
self.toolbar = QWidget()
self.toolbar.setFixedHeight(30)
self.toolbar.setStyleSheet("background: grey;")
self.button = QPushButton("Click here!")
t_layout = QHBoxLayout()
t_layout.setMargin(0)
t_layout.addWidget(self.button)
self.toolbar.setLayout(t_layout)
self.p1_label = QLabel("Such empty!")
self.p1_label.setStyleSheet("font-size: 30px;")
self.p1_label.setAlignment(Qt.AlignCenter)
self.p2_widget = QWidget()
self.p2_widget.setStyleSheet("background: orange;")
self.p3 = None
self.sw = QStackedWidget()
self.sw.addWidget(self.p1_label)
self.sw.addWidget(self.p2_widget)
if not list1:
self.sw.setCurrentWidget(self.p1_label)
else:
self.sw.setCurrentWidget(self.p2_widget)
self.mw_layout = QVBoxLayout()
self.mw_layout.addWidget(self.toolbar)
self.mw_layout.addWidget(self.sw)
self.setLayout(self.mw_layout)
self.button.clicked.connect(self.switch_widget)
def switch_widget(self):
self.p3 = widget()
self.p3.update_signal.connect(self.switch_back)
self.sw.addWidget(self.p3)
self.sw.setCurrentWidget(self.p3)
def switch_back(self):
self.sw.removeWidget(self.p3)
if list1:
self.sw.setCurrentWidget(self.p1_label)
else:
self.sw.setCurrentWidget(self.p2_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
widget.py
import sys
import os
import pathlib
import datetime
from PySide2.QtWidgets import *
from PySide2 import *
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtCore import Signal
class widget(QWidget):
update_signal = Signal()
def __init__(self):
super(widget, self).__init__()
self.setStyleSheet("background: teal;")
widget_label = QLabel("fluid dynamics is cool")
show_pop_up = QPushButton("show pop up")
pop_up = QDialog(self)
pop_up_label = QLabel("click below to, hopefully, get outta here")
get_outta_here = QPushButton("get outta here")
pop_up_layout = QVBoxLayout()
pop_up_layout.addWidget(pop_up_label)
pop_up_layout.addWidget(get_outta_here)
pop_up.setLayout(pop_up_layout)
def show_popup():
pop_up.show()
def get_out():
self.update_signal.emit()
pop_up.reject()
get_outta_here.clicked.connect(get_out)
show_pop_up.clicked.connect(show_popup)
widget_layout = QVBoxLayout()
widget_layout.addWidget(widget_label)
widget_layout.addWidget(show_pop_up)
self.setLayout(widget_layout)
Finally, check Python coding conventions for naming and other "minor" details.

Strange difference. Why different result in FileChooserDialog?

I've battled with the following for a couple of days, and distilled a very compact version of the problem which still shows the issue. The following program shows a basic window, and first opens a FileChooserDialog.
Here's the version which fails - it does not show the Cancel and Accept buttons in the dialog:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gtk
class Script():
def __init__(self, parent, width = 800):
self.parent = parent
def script_file_dialog(self):
fc = Gtk.FileChooserDialog(
parent = self.parent,
title = "title",
action = Gtk.FileChooserAction.OPEN,
do_overwrite_confirmation = True)
fc.add_buttons = ("Cancel", Gtk.ResponseType.CANCEL,
"Open", Gtk.ResponseType.ACCEPT)
return fc
class MainWindow(Gtk.Window):
def __init__(self):
super(MainWindow, self).__init__()
self.connect("destroy", lambda x: Gtk.main_quit())
self.set_default_size(1000, 580)
self.script = Script(self)
fc = self.script.script_file_dialog()
if fc.run() == 1:
print("one")
fc.destroy()
self.show_all()
def on_test_clicked(self, btn):
#~ self.script.on_open_script(btn)
self.script = Script(self)
fc = self.script.script_file_dialog()
if fc.run() == 1:
print("one")
fc.destroy()
def run(self):
Gtk.main()
def main(args):
mainwdw = MainWindow()
mainwdw.run()
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
And the following, almost identical version does work as intended.
Note the only difference is when instancing the FileChooserDialog,
the buttons are passed as keyword parameters. This is deprecated, and
produces a warning.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gtk
class Script():
def __init__(self, parent, width = 800):
self.parent = parent
def script_file_dialog(self):
fc = Gtk.FileChooserDialog(
parent = self.parent,
title = "title",
action = Gtk.FileChooserAction.OPEN,
do_overwrite_confirmation = True,
buttons = ("Cancel", Gtk.ResponseType.CANCEL,
"Open", Gtk.ResponseType.ACCEPT))
return fc
class MainWindow(Gtk.Window):
def __init__(self):
super(MainWindow, self).__init__()
self.connect("destroy", lambda x: Gtk.main_quit())
self.set_default_size(1000, 580)
self.script = Script(self)
fc = self.script.script_file_dialog()
if fc.run() == 1:
print("one")
fc.destroy()
self.show_all()
def on_test_clicked(self, btn):
#~ self.script.on_open_script(btn)
self.script = Script(self)
fc = self.script.script_file_dialog()
if fc.run() == 1:
print("one")
fc.destroy()
def run(self):
Gtk.main()
def main(args):
mainwdw = MainWindow()
mainwdw.run()
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
I tried delaying the showing of the dialog by triggering it by a button
after the main dialog was shown. What's more, I've used the first pattern
in other programs, and it's working there.
It's probably the stay-at-home rules which are slowly getting me crazy...
Anyone sees the problem?
The first version has a typo:
fc.add_buttons = ("Cancel", Gtk.ResponseType.CANCEL,
"Open", Gtk.ResponseType.ACCEPT)
should be:
fc.add_buttons("Cancel", Gtk.ResponseType.CANCEL,
"Open", Gtk.ResponseType.ACCEPT)

Ctreate two or more panel indicator using python gtk3 appindicator

I want to create two or more panel indicator using a single indicator class. This is the code:
#!/usr/bin/env python
import os
from gi.repository import Gtk
from gi.repository import AppIndicator3
class IndicatorObject:
def create_indicator(self, indicator_id):
indicator = AppIndicator3.Indicator.new(indicator_id, os.path.abspath('sample_icon.svg'), AppIndicator3.IndicatorCategory.SYSTEM_SERVICES)
indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
menu = Gtk.Menu()
item_quit = Gtk.MenuItem('Quit')
menu.append(item_quit)
indicator.set_menu(menu)
menu.show_all()
print indicator_id
indicator1 = IndicatorObject()
indicator1.create_indicator("first_indicator")
indicator2 = IndicatorObject()
indicator2.create_indicator("second_indicator")
Gtk.main()
But nothing happens when i run the script. How can i create two or more indicator using single class?
#!/usr/bin/env python
import os
from gi.repository import Gtk
from gi.repository import AppIndicator3
class AppIndicatorExample:
def __init__(self, indicator_id):
self.ind = AppIndicator3.Indicator.new(str(indicator_id), os.path.abspath('sample_icon.svg'), AppIndicator3.IndicatorCategory.SYSTEM_SERVICES)
self.ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
# create a menu
self.menu = Gtk.Menu()
item = Gtk.MenuItem(str(indicator_id))
item.show()
self.menu.append(item)
image = Gtk.ImageMenuItem(Gtk.STOCK_QUIT)
image.connect("activate", self.quit)
image.show()
self.menu.append(image)
self.menu.show()
self.ind.set_menu(self.menu)
def quit(self, widget, data=None):
Gtk.main_quit()
if __name__ == "__main__":
indicator = AppIndicatorExample(1)
indicator2 = AppIndicatorExample(2)
indicator3 = AppIndicatorExample(3)
indicator4 = AppIndicatorExample(4)
indicator5 = AppIndicatorExample(5)
indicator6 = AppIndicatorExample(6)
indicator7 = AppIndicatorExample(7)
indicator8 = AppIndicatorExample(8)
Gtk.main()
This is the right way to using single class to create one or more appindicator.

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