is it able to use CustomJS callback when using bokeh periodic_callback - bokeh

I am able to update GeoJSONDataSource using bokeh CustomJS, however, when using periodic_callback, I can't use CustomJS as the callback function. Below is my code with the error message "TypeError: 'CustomJS' object is not callable"
current_year = 1800
n_slider = Slider(start=1, end=50, value=1, step=1, title="N")
toggle = Toggle(label="Play", button_type="success")
callback = CustomJS(args=dict(source=source, year=current_year, n_slider=n_slider, toggle=toggle), code="""
data = source.data;
console.log(toggle.active)
console.log(n_slider.value)
console.log(year)
year = year +1
// edit GeoJSONDataSource
source.change.emit();
""")
curdoc().add_root(column(p, n_slider, toggle))
curdoc().add_periodic_callback(callback, 1000)
My question is,
Is it possible to use a CustomJS as the callback function of periodic_callback, if not, how can I edit the GeoJSONDataSource entry using the normal python function?

It is not possible, add_periodic_callback can only run actual Python callbacks (in a Bokeh server application). There is no built-in facility to execute CustomJS callbacks on a set interval. It would be "hacky" but it's possible the AjaxDataSource with its polling interval could be slightlky abused to achieve a similar effect, though.

Related

Reset the function wrongly assigned as string in python using Jupyter

I was practicing plotting when by mistake I assigned ylabel and title as below:
plt.ylabel = "No. of Hospitals"
plt.title = 'Hospitals by State'
This changed the two functions to string, as confirmed in the image below. (First blue circle)
Then I changed the statement to set these correctly to:
plt.ylabel("No. of Hospitals")
plt.title('Hospitals by State')
Now, I get the error
TypeError: 'str' object is not callable
In one of the stackoverflow article, I learned once the function is wrongly assigned then only way to fix this is restart the kernel. I don't want to restart the kernel and run all 500+ jobs above this mistake. I also tried importing matplotlib and sns again by calling pyplot as plt2 that didn't work either. (Second Blue circle in the image).
I was wondering is there a way to reset the function back to normal from the string status now?
I do understand that I can write the dataframe of interest to a file and then read it back in the new notebook. However, I'm sure many will agree knowing the process and ability to reset the function back to normal will help many in future without costly work around(s).
You may redefine them, like:
plt.title = lambda *args, **kwargs: plt.gca().set_title(*args, **kwargs)
plt.ylabel = lambda *args, **kwargs: plt.gca().set_ylabel(*args, **kwargs)
See also the original code of matplotlib.pyplot.title and matplotlib.pyplot.ylabel.

OpenMDAO Optional Error on Unconnected Input

Is there any way to force OpenMDAO to raise an error if a given input is unconnected? I know for many inputs, defaults can be provided such that the input doesn't need to be connected, however is there a way to tell OpenMDAO to automatically raise an error if certain key inputs are unconnected?
This is not built into OpenMDAO, as of V3.17. However, it is possible to do it. The only caveat is that i had to use some non public APIs to make it work (notice the use of the p.model._conn_global_abs_in2out). So those APIs are subject to changer later.
This code should give you the behavior your want. You could augment things with the use of variable tagging if you wanted a solution that didn't require you to give a list of variable names to the validate function. The list_inputs method can accept tags to filter by instead if you prefer that.
import openmdao.api as om
def validate_connections(prob, force_connected):
# make sure its a set and not a generic iterator (i.e. array)
force_connected_set = set(force_connected)
model_inputs = prob.model.list_inputs(out_stream=None, prom_name=True)
#gets the promoted names from the list of inputs
input_set = set([inp[1]['prom_name'] for inp in model_inputs])
# filter the inputs into connected and unconnected sets
connect_dict = p.model._conn_global_abs_in2out
unconnected_inputs = set()
connected_inputs = set()
for abs_name, in_data in model_inputs:
if abs_name in connect_dict and (not 'auto_ivc' in connect_dict[abs_name]):
connected_inputs.add(in_data['prom_name'])
else:
unconnected_inputs.add(in_data['prom_name'])
# now we need to check if there are any unconnected inputs
# in the model that aren't in the spec
illegal_unconnected = force_connected_set.intersection(unconnected_inputs)
if len(illegal_unconnected) > 0:
raise ValueError(f'unconnected inputs {illegal_unconnected} are are not allowed')
p = om.Problem()
###############################################################################################
# comment and uncomment these three lines to change the error you get from the validate method
###############################################################################################
# p.model.add_subsystem('c0', om.ExecComp('x=3*a'), promotes_outputs=['x'])
# p.model.add_subsystem('c1', om.ExecComp('b=a+17'))
# p.model.connect('c1.b', 'c2.b')
p.model.add_subsystem('c2', om.ExecComp('y=2*x+b'), promotes_inputs=['x'])
p.model.add_subsystem('c3', om.ExecComp('z=x**2+y'))
p.setup()
p.final_setup()
If this is a feature you think should be added to OpenMDAO proper, then feel free to submit a POEM proposing how a formal feature and its API might look.

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...

How to close a window when you click a button to open another window

I am working on a program that will allow someone to enter details in order to write a CV. I am using the Tkinter module (as extra practice) but am already stuck on the menu!
At the moment I have three different options the user can choose: Write CV, Review CV and Exit. I have created a button for each option and when the user presses the button it'll open, however the menu window remains open (there is a different subroutine for each option).
I understand that you need to do something like window.destroy(), however I'm not sure how to give a button two commands without doing something too fiddly like create more subroutines etc.?
The other option I think I'd prefer is is I could clear the menu screen?
Here is the programming I have at the moment:
def Main_Menu():
import tkinter
main_menu = tkinter.Tk()
main_menu.title("CV Writer")
main_menu.geometry("300x300")
main_menu.wm_iconbitmap('cv_icon.ico')
title = tkinter.Label(main_menu, text = "Main Menu", font=("Helvetica",25))
title.pack()
gap = tkinter.Label(main_menu, text = "")
gap.pack()
write_cv = tkinter.Button(main_menu, text = "1) Write CV", font=("Helvetica"), command=Write_CV)
write_cv.pack()
review_cv = tkinter.Button(main_menu, text = "2) Review CV", font=("Helvetica"), command=Review_CV)
review_cv.pack()
leave = tkinter.Button(main_menu, text = "3) Exit", font=("Helvetica"), command=Exit)
leave.pack()
main_menu.mainloop()
def Write_CV():
import tkinter
write_cv = tkinter.Tk()
write_cv.geometry("300x300")
write_cv.title("Write CV")
def Review_CV():
import tkinter
review_cv = tkinter.Tk()
review_cv.geometry("300x300")
review_cv.title("Review CV")
def Exit():
import tkinter
leave = tkinter.Tk()
leave.geometry("300x300")
leave.title("Exit")
Main_Menu()
Running the program should help make this question make more sense!
I am so sorry for the wordy question, but any kind of help would be appreciated! Please bear in mind I am only a GCSE student so simple language would also be so nice! Thank you!
I don't know why are you importing tkinter under each method, it's completely useless. Simply import it once at the beginning of your file with a syntax like this:
import tkinter as tk
So that you can refer to the widgets simply with the duo tk:
btn = tk.Button(None, text='I can simply refer to a widget with tk')
Apart from this, the structure of your program is really bad. In my opinion, you should not instantiate Tk inside your function Main_Menu, because it will only be visible inside it. If you want to refer to the master or root or whatever you want to call the instance of Tk, you can't, because it's a local instance, as I said above.
I usually instantiate Tk in the main function of my program, or in the following if __name__ == '__main__': construct:
if __name__ == '__main__':
master = tk.Tk() # note I am using "tk"
# create your objects or call your functions here
master.mainloop()
Your are creating an instance of Tkin each of your function, that is really a bad practice, never do that. You should only create one instance of Tk for each Tkinter application.
You should use the object-oriented paradigm or make all your widgets global to structure your application.
Except these details, you can simply call master.destroy() when you want to destroy your main window and all its children widgets, where master is the Tk instance.
In general, you have a lot of errors and bad practices. My advice is:
Read a tutorial on Python first and then on Tkinter, before
proceeding.

QTest: It's possible to test drag&drop?

I try to test drag&drop with simple sequince: mousePress + mouseMove + mouseRelease. But it's not work.
I investigate qtest source and found, that move event tested through main dispatcher processEvent(). Also I found some bugs in qt bug-tracker: 1, 2
So, I think, that it's not possible to test drag&drop under latest stable Qt4. Have anybody success story with this?
I have had no luck simulating drag and drop via the QTest mouse functions, and digia says
they're not adding that functionality to QT4. I implemented drag/drop testing via a method similar to the one suggested in the above link:
create your mime_data, something like:
mime_data = widget_model.mimeData(indexes)
or
mime_data = QMimeData()
mime_data.setText(widget.text())
then use a function like this to drop the data:
def dropOnto(self, widget, mime_data):
action = Qt.CopyAction|Qt.MoveAction
pt = widget.rect().center()
drag_drop = QDropEvent(pt, action, mime_data, Qt.LeftButton, Qt.NoModifier)
drag_drop.acceptProposedAction()
widget.dropEvent(drag_drop)
mouseMoveEvent() handler starts and blocks at QDrag.exec(). In order to simulate the drop, you have to schedule it e.g. with QTimer:
def complete_qdrag_exec():
QTest.mouseMove(drop_target)
QTest.qWait(50)
QTest.mouseClick(drop_target, Qt.MouseButton.LeftButton)
QTimer.singleShot(1000, complete_qdrag_exec)
QTest.mousePress(drag_source, Qt.MouseButton.LeftButton, Qt.KeyboardModifier.NoModifier, QPoint(10, 10))
QTest.mouseMove(drag_source, QPoint(50, 50)) # Ensure distance sufficient for DND start threshol

Resources