Added Jupyter console widget and Example#2353
Conversation
| self.addTab(self.rich_jupyter_widget, "console") | ||
|
|
||
| def execute_command(self,command_string:str=None)->bool: | ||
| return self.rich_jupyter_widget.execute(command_string) |
There was a problem hiding this comment.
you may have mixed tabs and spaces here?
There was a problem hiding this comment.
That's part of the code I got from yaqc so i guess I can blame them 😅
I formatted the files with black, so it should be good now
|
Hi @jonmatthis Thanks so much for this PR. I always thought pyqtgraph's console example probably wasn't that great and we would better serve our users by showing how to better integrate jupyter consoles or the qtconsole. A couple of small issues I see, seems like the formatting isn't quite right, I think you may have a tab instead of spaces in one place and such; you may want to run Also in EDIT: while I have no problem adding type-hints, to the code-base (eventually want to do that anyway) |
|
Nifty! I imagine updating the kernel's namespace would be a fairly common operation; would you consider adding a method similar to |
|
Whoops, I forgot to add the I'll go through your other comments now, thanks! |
… method Also added method to input 'namespace' on init (like old Console)
I agree that's very useful! I added that method and showed and sample usage in the
I did this too, I'm not 100% clear on the concept of a 'namespace' so it's possible I missed something important :) |
|
Hi @jonmatthis Thanks for updating the PR. I'm reluctant to add code here to pyqtgraph/widgets, I think this is small enough, the entirety can live as an example. In the discord chat, @pijyoi was kind enough to make a suggestion for what an example could look like: import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
from qtconsole import inprocess
class ConsoleWidget(inprocess.QtInProcessRichJupyterWidget):
def __init__(self):
super().__init__()
self.kernel_manager = inprocess.QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
def shutdown_kernel(self):
self.kernel_client.stop_channels()
self.kernel_manager.shutdown_kernel()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
cwidget = QtWidgets.QWidget()
vbox = QtWidgets.QVBoxLayout()
cwidget.setLayout(vbox)
self.plot = pg.PlotWidget()
self.console = ConsoleWidget()
vbox.addWidget(self.plot)
vbox.addWidget(self.console)
self.setCentralWidget(cwidget)
app = QtWidgets.QApplication.instance()
app.aboutToQuit.connect(self.console.shutdown_kernel)
kernel = self.console.kernel_manager.kernel
kernel.shell.push(dict(np=np, pw=self.plot))
self.console.execute("whos")
if __name__ == '__main__':
pg.mkQApp()
main = MainWindow()
main.show()
pg.exec()There is some president here for demonstrating functionality in the examples that isn't really a part of the library directly, specifically the GLPainter example comes to mind. |
|
I agree with this! It demonstrates the method to get a jupyter console widget, while keeping responsibility for maintaining that core functionality of that widget in the hands of the |
|
A few suggested tweaks to that example to make it a bit more transparent to low-XP folks (like me😅). It's mostly just changing the variable names to make it (even) more obvious which variable is what, and an example of how to execute a command after creating the Widget. Other potential cool additions would be -
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
from qtconsole import inprocess
class JupyterConsoleWidget(inprocess.QtInProcessRichJupyterWidget):
def __init__(self):
super().__init__()
self.kernel_manager = inprocess.QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
def shutdown_kernel(self):
self.kernel_client.stop_channels()
self.kernel_manager.shutdown_kernel()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
central_widget = QtWidgets.QWidget()
vbox_widget = QtWidgets.QVBoxLayout()
central_widget.setLayout(vbox_widget)
self.plot_widget = pg.PlotWidget()
self.console = JupyterConsoleWidget()
vbox_widget.addWidget(self.plot_widget)
vbox_widget.addWidget(self.console)
self.setCentralWidget(central_widget)
app = QtWidgets.QApplication.instance()
app.aboutToQuit.connect(self.console.shutdown_kernel)
kernel = self.console.kernel_manager.kernel
kernel.shell.push(dict(np=np, pw=self.plot_widget))
self.console.execute("whos")
if __name__ == '__main__':
pg.mkQApp()
main = MainWindow()
main.show()
main.console.execute("print(\"hello world :D \")")
pg.exec() |
|
sorry for the delay! I'll test this on my end later today, but I suspect that I'll be totally happy with this option :D |
…yter-console-widget
…upyter_console_example.py`
…py` example as `recommended=True`?
|
Ok, I think I removed my previously committed changes (but please double check I didn't miss anything) To summarize changes: Per the conversation above, I removed the standalong "JupyterConsoleWidget" in favor of a more fleshed out The new example creates a The widgets are implements as Docks so that Users can re-size the windows and close the PlotWidget if they want to. The example was added to the list of examples in I tagged it as a 'recommended' example, but I'm totally happy to remove that if that doesn't feel appropriate. Please let me know if there is anything else I can do to help move this forward :D |
.gitignore
Outdated
|
|
||
| # jupyter notebooks | ||
| .ipynb_checkpoints | ||
| .venv No newline at end of file |
There was a problem hiding this comment.
haha no, a newline after the .venv :)
There was a problem hiding this comment.
ooooh, that makes more sense 😅
|
Hi @jonmatthis Thanks for updating the PR, sorry it's taken me a bit to get to this. I added an inline comment about inserting a newline in Also the dark mode is 🔥 |
|
Ok, done! I knew I was forgetting something 😅 |
|
|
||
| try: | ||
| from qtconsole import inprocess | ||
| except: |
There was a problem hiding this comment.
specify the exception to catch as being ImportError here, not just a generic exception.
|
Ok, did the things 👍 |
| try: | ||
| from qtconsole import inprocess | ||
| except: | ||
| except ImportError or NameError: |
There was a problem hiding this comment.
I don't think the or operator does what you want it to.
class A(Exception): pass
class B(Exception): pass
try:
raise B()
except A or B:
print('caught')
except:
print('oops')There was a problem hiding this comment.
Ahh, correct, what is needed here is `except (ImportError, NameError):
https://docs.python.org/3/tutorial/errors.html#handling-exceptions
|
Do we want to add this example into |
Completely forgot about |
|
blerg, need to indicate to skip this example when qtconsole is not installed; sorry should have caught this earlier sorry about that! Anyway, you need to add an entry here: https://github.com/pyqtgraph/pyqtgraph/blob/master/pyqtgraph/examples/test_examples.py#L68-L77 You'll want to add something like "jupyter_console_example.py": exceptionCondition(
importlib.util.find_spec("qtconsole") is not None,
reason="No need to test with qtconsole not being installed"
), |
|
Hi @jonmatthis I hope you don't mind, I added that last bit to make the examples pass; I should have given you that feedback weeks ago, sorry about that. Anyway, this looks good to merge. Thanks for the PR! |
|
Hey cool!! This is my first PR into an open source project :D Thank you for all your great work maintaining this incredible package, and for making this PR experience easy, fun, and educational <3 |
|
Congratulations on your first PR! Appreciate the sentiment, we certainly could have done better by giving you reviews/suggestions in a more timely manner, sorry about that! |

I added a JupyterConsole widget and made an
Exampleshowing basic usethis was based on the implementation from
yaqc-qtpywhich I learned about in a conversation on the#pyqtgraphchannel in the Python discord: https://discord.com/channels/267624335836053506/898139460821192724/991360299158470676I did my best to follow the
CONTRIBUTING.mdguidelines, but I'm relatively new at this so I might have missed something