How can I run Jupyter Dash app in offline mode - jupyter-notebook

I haven’t got internet connection on my work machine, but I’d like to use dash. When I try to run my code with
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
I also get only white screen without any graphics or errors.
Here some simple code for example:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
app = JupyterDash('offline example')
app.layout = html.Div([
dcc.Graph(id='my-graph', figure={'data': [{'x': [1, 2, 3], 'y': [4, 1, 2]}]})
])
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
if __name__ == '__main__':
app.run_server(mode=“Inline”)

You can always run JupyterDash without an internet connection. So that's not the problem here. I rather think that you haven't installed all components you need to run plotly and JupyterDash properly. If you're able to run the latest versions, take a closer look on how to run all necessary installations here:
You should also take a look at what this youtube channel has to offer on tips & tricks
It may also be the case that you for some reason are not able to launch your app in JupyterLab. You can check this by changing
if __name__ == '__main__':
app.run_server(mode="Inline")
To:
if __name__ == '__main__':
app.run_server(mode="external")
Which will fire up the Dash app in your default browser.

Related

problem with event loops, gui qt5, and ipywidgets in a jupyter notebook

I am trying to integrate interactive ipywidgets with a loop in my code that also performs other tasks (in this case, acquiring data from some hardware attached from the computer and updating live plots).
In the past, I could do this by using IPython.kernel.do_one_iteration() in my while loop: this would trigger a sync of the ipywidget changes and I would be able to retrieve them from the python widget objects. A minimal example is here:
import ipywidgets as widgets
from time import sleep
import IPython
do_one_iteration = IPython.get_ipython().kernel.do_one_iteration
w = widgets.ToggleButton()
display(w)
i=0
while True:
do_one_iteration()
print(i, w.value, end="\r")
w.decription = str(i)
sleep(0.5)
i+=1
Here, the for loop prints out the ticker integer along with the state of the widget. (In the real code, I would also acquire data, update plots, and change plot / acquisition settings dependent on the interaction with the user via the widgets.)
With ipykernel 5.3.2 and ipython 7.16.1, this worked fine: if the widget changed, calling do_one_iteration() synced the widget states to the kernel and I could retrieve it from my while loop.
After an upgrade (to 6.4.1 and 7.29.0), this no longer works. It seems that do_one_iteration() is now a coroutine: I get a warning coroutine 'Kernel.do_one_iteration' was never awaited if I use the above code.
With some help of a friend, we found a way to do this with threading an asyncio:
%gui asyncio
import asyncio
import ipywidgets as widgets
button = widgets.ToggleButton()
display(button)
text = widgets.Text()
display(text)
text.value= str(button.value)
stop_button = widgets.ToggleButton()
stop_button.description = "Stop"
display(stop_button)
async def f():
i=0
while True:
i += 1
text.value = str(i) + " " + str(button.value)
await asyncio.sleep(0.2)
if stop_button.value == True:
return
asyncio.create_task(f());
And this works (also adding a stop button, and changing to text output widget instead of printing). But to throw a spanner in the works, I need to use a library that itself uses a QT gui event loop. Some more puzzling suggests that this should be the code to make this work:
%gui qt5
import asyncio
import ipywidgets as widgets
import qasync
button = widgets.ToggleButton()
display(button)
text = widgets.Text()
display(text)
text.value= str(button.value)
stop_button = widgets.ToggleButton()
stop_button.description = "Stop"
display(stop_button)
async def f():
while True:
i += 1
text.value = str(i) + " " + str(button.value)
await asyncio.sleep(0.2)
if stop_button.value == True:
return
from qtpy import QtWidgets
APP = QtWidgets.QApplication.instance()
loop = qasync.QEventLoop(APP)
asyncio.set_event_loop(loop)
asyncio.create_task(f());
But with this code, the updates do not propagate, and I get the following error on the terminal running my notebook server:
[IPKernelApp] ERROR | Error in message handler
Traceback (most recent call last):
File "/Users/gsteele/anaconda3/envs/myenv2/lib/python3.9/site-packages/ipykernel/kernelbase.py", line 457, in dispatch_queue
await self.process_one()
File "/Users/gsteele/anaconda3/envs/myenv2/lib/python3.9/site-packages/ipykernel/kernelbase.py", line 440, in process_one
t, dispatch, args = await self.msg_queue.get()
RuntimeError: Task <Task pending name='Task-2'
coro=<Kernel.dispatch_queue() running at
/Users/gsteele/anaconda3/envs/myenv2/lib/python3.9/site-packages/ipykernel/kernelbase.py:457>
cb=[IOLoop.add_future.<locals>.<lambda>() at /Users/gsteele/anaconda3/envs/myenv2/lib/python3.9/site-packages/tornado/ioloop.py:688]>
got Future <Future pending> attached to a different loop
It seems that somehow my ipywidgets events are propagating to the wrong event loop.
And now my question is: does anybody know what is going on here?
It's hard for me to identify if this is a "bug", and if so, in which software package do things go wrong? ipykernel? Or tornado? Or ipywidgets? Or asyncio? Or maybe I'm missing something?
Any thoughts highly welcome, thanks!
Found at least a partial solution: using the nest_asyncio package allows me to now use do_one_iteration(), just by adding the following to the first code block:
import nest_asyncio
nest_asyncio.apply()
and then using await do_one_iteration() instead of calling it directly.
(see https://github.com/ipython/ipykernel/issues/825)
For my purposes, this solves my issue since I don't need asynchronous interaction with my GUI. The problem of the %gui qt5 interaction with the event loop in the asynchronous versions of the code is still a mystery though...

Jupyter (Lab): Browsing the remote file system inside a notebook

I have several Jupyter notebooks which perform analysis on datasets. Right now, a dataset is specified by its filename. Every time the user wants to perform analysis on a new dataset, she/he has to edit the appropriate line in the notebook and modify dataset path string. The datasets can be located in different directories. The notebooks can also be located in different directories. In each notebook I would like to provide a widget that allows the user to browse the remote file system and pick the dataset he/she wants to analyse.
Are there any open source projects that support the above functionality? I am looking for something that is still active/supported and has some basic documentation. I did quick search on Google and surprisingly I didn't find anything.
Then I realised that JupyterLab, the evolution of Jupyter, has something very similar to what I want. It already has a very capable file browser but it is a bit "isolated" from everything else.
Is it possible somehow to get the relative (to the currently opened notebook) path of the selected file in the JupyterLab file browser?
Thank you.
Here's code for a server-side file browsing widget. Only tested in regular Jypter notebook - not Jupyter Lab. Also, must use a fairly recent version. Hope this helps.
import sys
import os
import ipywidgets as ui
from IPython.display import display
class PathSelector():
def __init__(self,start_dir,select_file=True):
self.file = None
self.select_file = select_file
self.cwd = start_dir
self.select = ui.SelectMultiple(options=['init'],value=(),rows=10,description='')
self.accord = ui.Accordion(children=[self.select])
self.accord.selected_index = None # Start closed (showing path only)
self.refresh(self.cwd)
self.select.observe(self.on_update,'value')
def on_update(self,change):
if len(change['new']) > 0:
self.refresh(change['new'][0])
def refresh(self,item):
path = os.path.abspath(os.path.join(self.cwd,item))
if os.path.isfile(path):
if self.select_file:
self.accord.set_title(0,path)
self.file = path
self.accord.selected_index = None
else:
self.select.value = ()
else: # os.path.isdir(path)
self.file = None
self.cwd = path
# Build list of files and dirs
keys = ['[..]'];
for item in os.listdir(path):
if item[0] == '.':
continue
elif os.path.isdir(os.path.join(path,item)):
keys.append('['+item+']');
else:
keys.append(item);
# Sort and create list of output values
keys.sort(key=str.lower)
vals = []
for k in keys:
if k[0] == '[':
vals.append(k[1:-1]) # strip off brackets
else:
vals.append(k)
# Update widget
self.accord.set_title(0,path)
self.select.options = list(zip(keys,vals))
with self.select.hold_trait_notifications():
self.select.value = ()
f = PathSelector('/some/data')
display(f.accord)

Moving the cursor in a PyQt5 text edit doesn't work

I'm contributing to Frescboaldi, a PyQt5 application and experience problems interacting with the core text edit component.
It seems whatever I try I can't get either of setPosition or movePosition to work.
The code
cursor.insertText("Hello")
cursor.setPosition(cursor.position() - 5)
properly inserts the text Hello in the document but leaves the cursor at the end of the inserted text (instead of moving it to the left by 5 characters). The first line proves that cursor, textedit and document are set up properly. trying movePosition doesn't have any effect either.
The actual goal is to insert some text, have it selected and the cursor at the end of the selection as can be seen in https://github.com/wbsoft/frescobaldi/blob/master/frescobaldi_app/cursortools.py#L179
Am I doing anything wrong here? Could this be a bug in Qt/PyQt? Or could this be an issue in PyQt5?
[Edit:] I've now confirmed with a minimal app example that the problem can't be in the larger construction of the application. In the following mini app neither setPosition nor movePosition has any effect - while insertText works well:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication, QTextEdit
def main():
app = QApplication(sys.argv)
w = QTextEdit()
w.setWindowTitle('Manipulate cursor')
cursor = w.textCursor()
cursor.insertText("Hello World")
# neither of the following commands have any effect
cursor.setPosition(cursor.position() - 5)
cursor.movePosition(cursor.movePosition(cursor.Left, cursor.KeepAnchor, 3))
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are working on a local copy of the text cursor returned by w.textCursor. You should call w.setTextCursor(cursor) at the end to change the visible cursor.
A second problem is that you use the output of movePosition to call movePosition again, which is not allowed:
cursor.movePosition(cursor.movePosition(cursor.Left, cursor.KeepAnchor, 3))
should be
cursor.movePosition(cursor.Left, cursor.KeepAnchor, 3)
Note that I tested it in Qt (not PyQt), but that should not make any difference, which successfully selected lo of Hello world.

bokeh development workflow with interaction

I'm developing a visualization using
% bokeh serve --show myapp.py
The problem is that when I change myapp.py I have to kill the above command and restart it. Is there a better workflow for this kind of development?
Thanks!
Not yet. As of Bokeh 0.11.1 (and soon to be 0.12 too), this is a planned, but still open, feature request There are only a few folks working a huge pile of work for Bokeh. New contributors can help accelerate new features and fixes. If you're able to help, please reach out to us on the project gitter channel.
I was unable to understand enough of the bokeh internals to make this work nicer, but here is a hacky script that does what I want anyway.
# bokeh_watcher.py
#
# Watches specific files in directory and restarts bokeh server upon change.
#
# % python bokeh_watcher filename.py
#
# Note that you stil have to navigate your browser to localhost:5006/filename
# to see your Bokeh visualization and you might have to refresh the browser.
import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import RegexMatchingEventHandler
from bokeh.command.bootstrap import main
import multiprocessing
import os
JOBS = []
FILE = []
def spawn_bokeh(args):
main(args)
class BokehHandler(RegexMatchingEventHandler):
'''
kills and restarts bokeh server upon filechange.
'''
def on_modified(self, event):
super(BokehHandler, self).on_modified(event)
what = 'directory' if event.is_directory else 'file'
logging.info("Modified %s: %s"% (what, event.src_path))
p=JOBS.pop()
p.terminate()
time.sleep(1) # time to die
logging.info('terminated')
logging.info('initiating restart')
p = multiprocessing.Process(target=spawn_bokeh,
args=(self.args,))
p.start()
JOBS.append(p)
if __name__ == "__main__":
here = os.path.realpath(__file__)
fullpathname=os.path.dirname(here)+os.sep+sys.argv[1]
# local logger
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
filemod_handler = BokehHandler(['.*%s'%(sys.argv[1])])
filemod_handler.args = ['','serve',fullpathname, '--log-level','info']
# fire up bokeh server
p = multiprocessing.Process(target=spawn_bokeh,args=(filemod_handler.args,))
p.start()
# store object in global for later
JOBS.append(p)
observer = Observer()
observer.schedule(filemod_handler, '.', recursive=False)
observer.start()
try:
while True:
time.sleep(3)
except KeyboardInterrupt:
observer.stop()
observer.join()

OptionParser in ipython notebook?

I am enjoying developing inside the ipython notebook, but I am having a problem when I want to write a main() function that reads the command line args (with OptionParser, for example). I want to be able to export the code to a .py file and run it from the command line, but I haven't found a way to have a main() that runs both in the notebook with predefined arguments or from the command line with python and command line args. What is the secret?
In case that is not clear, I would like to do something like this:
if __name__ == '__main__':
# if in the notebook
vals = {'debug':True, 'tag_file': 't.tags'}
options = Object()
for k,v in vals.items():
options.setattr(k,v)
args = 'fname1.txt'
# if running as a command line python script
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-d','--debug',action='store_true',dest='debug')
parser.add_option('-t','--tags',action='store',dest='tag_file')
options,args = parser.parse_args()
You cannot determine that you are in an IPython notebook or a qtconsole, or a simple IPython shell, for the simple reason the 3 can be connected to the same kernel at the same time.
It would be like asking, what color is the current key the user is typing. You could get it by looking the plugged usb devices and look for images on the internet and guess the keyboard color, but nothing guarantees you it will be accurate, nor that it won't change, and user can have multiple keyboard plugged, or even painted keyboard.
It is really the same with the notebook, Even if you determine you are in ZMQKernel, are you speeking to qtconsole or webserver ? Again, you found that you were talking to the webserver, are you talking to JS or Emacs ? And so on and so forth.
The only thing you can do, you can ask the user.
What is reliable, is test wether you are in IPython or not.
If you really but reeaaalllyy want a way, as until now, the notebook is the only thing that can display Javascript. And javascript can execute code in pyton. So you might be able to create something that display JS that send back info to the kernel. And using thread and timer you can say that you were not in a notebook (but you will have a race condition).
Don't worry about the distinction. Just set default values, and unless they are overridden from the command line, use those.
if __name__ == '__main__':
parser = OptionParser()
parser.add_option('-d', '--debug', action='store_true', dest='debug',
default=True)
parser.add_option('-t','--tags',action='store',dest='tag_file',
default='t.tags')
options, args = parser.parse_args()
if not args:
args = ['fname1.txt']

Resources