How to embed a dropdown ipywidget in an ipysheet spreadsheet? - python-3.x

I tried following the code to merge a dropdown menu ipywidget into the ipysheet. it seems like my code works the way I imagined. However, I am unable to select items from the menus listed in the dropdown widget.
import ipysheet
import ipywidgets as widgets
Solvent = widgets.Dropdown(
options=['DMC', '2-Butanol', 'Chloroform', 'Ethanol'],
value='DMC',)
sheet2 = ipysheet.sheet()
ipysheet.column(0, [Solvent])
ipysheet.column(1, [1,2,3,4, 5])
widgets.VBox([sheet2, Solvent ])

After some review, I believe that this is a genuine bug/shortcoming with ipysheet. Though there are alternative approaches, this inability to use a particular widget within ipysheet cells is not an expected result of this API... Someone please correct me if I'm missing something.
There is a "hack" that is working for me through Jupyter Notebook in the browser, which is to right-click the dropdown before left-clicking it to change the context of the dropdown menu, allowing you to select it via mouseover.
The other alternative is passing in your options as the "choice" argument to the cell you wish to contain the dropdown.
from ipywidgets import link
import ipysheet
import ipywidgets as widgets
Solvent = widgets.Dropdown(
options=['DMC', '2-Butanol', 'Chloroform', 'Ethanol'],
value='DMC',)
sheet2 = ipysheet.sheet()
# dropdown_cell = ipysheet.cell(0,0,choice = Solvent.options,value='WORLD')
interactive_cell = ipysheet.cell(1,3, value='HELLO')
dropdown_cell = ipysheet.cell(0,0,choice = Solvent.options,value='WORLD')
# link((interactive_cell,'value'),(dropdown_cell,'value'))
link((dropdown_cell,'value'),(interactive_cell,'value'))
widgets.VBox([sheet2, Solvent ])
From my current understanding (and attempts), only cells are interactive in ipysheets. (The alternative method of using the ipysheet "calculation" decorator also depends on the items being Cells, not Columns.)

Related

IPython.display not showing TextBox in ipywidgets

I'm trying to build such a functionality such that whenever the user clicks in the Add button in my code, it generates a new text box, just under the old one. For example, like this:
Now, if the user were to click on add once again, a 5th text box should appear.
I've tried to achieve the same using this piece of code:
add_button = widgets.Button(description='Add',
disabled=False,
button_style='',
style={'description_width': 'initial', 'button_width': 'auto'},
icon='plus'
)
display(add_button)
add_button.on_click(add_new)
And my add_new function is simply defined as follows:
def add_new(*args):
display(widgets.Text(placeholder='Type something',description='String:'))
But this does not seem to be working nothing happens on clicking the button, any help would be appreciated. Also if there is a better way to do this, please help, I'm new to ipywidgets.
Try like this:
output = widgets.Output()
def add_new(*args):
with output:
display(widgets.Text(placeholder='Type something',description='String:'))
add_button = widgets.Button(description='Add',
disabled=False,
button_style='',
style={'description_width': 'initial', 'button_width': 'auto'},
icon='plus'
)
display(add_button)
add_button.on_click(add_new)
output

How to sort values based on selection in an Altair chart?

Given an interactive area chart like this:
import altair as alt
from vega_datasets import data
source = data.iowa_electricity()
selection = alt.selection(type='multi', fields=['source'], bind='legend')
alt.Chart(source).mark_area().encode(
x="year:T",
y="net_generation:Q",
color="source:N",
opacity=alt.condition(selection, alt.value(1), alt.value(0.1))
).add_selection(selection)
I would like to sort the selected values first so they stack up from the bottom and don't "hang in thin air" like in the example below:
However, I can't see how I would express this in a transformation. The only thing that works is transform_filter(selection) but that completely removes the values that are not selected.
Is this not possible or am I missing something?
One way you can do this is to access the contents of the selection within a calculate transform, using a vega expression to find whether the current column is in the selection. At this point, you can set the order to this encoding:
import altair as alt
from vega_datasets import data
source = data.iowa_electricity()
selection = alt.selection(type='multi', fields=['source'], bind='legend')
alt.Chart(source).add_selection(
selection
).transform_calculate(
order=f"indexof({selection.name}.source || [], datum.source)",
).mark_area().encode(
x="year:T",
y="net_generation:Q",
color="source:N",
opacity=alt.condition(selection, alt.value(1), alt.value(0.1)),
order=alt.Order("order:N", sort='descending'),
).add_selection(selection)

Is there a way to set title value to the containers in ipywidgets?

All,
I am trying to build a small UI as a wrapper for a tool I have. I am trying to use ipywidgets in Jupyter notebooks to do this and I am running into a small issue. It is not sufficiently clear by looking at the Read The Docs page as to how it can be addressed, so any guidance would be greatly appreciated.
Here is what I am looking at, this is straight from the documentation:
```
import ipywidgets as widgets
tab_contents = ['P0', 'P1', 'P2', 'P3', 'P4']
children = [widgets.Text(description=name) for name in tab_contents]
tab = widgets.Tab()
tab.children = children
tab.titles = [str(i) for i in range(len(children))]
tab
```
This looks great, except that the tabs do not have a title. The documentation basically says, titles can be set the same way as Accordion container, but even that container doesn't show how to set a title. Would appreciate any help in this matter.
Best
Uday
Yep it's not the most obvious way to do it. Here is an alternative that avoids a list comprehension.
for title, (index, _) in zip(tab_contents, enumerate(tab.children)):
tab.set_title(index, title)
I found an answer here:
from ipywidgets import *
names = ['General', 'Representation', 'Preference', 'Theme', 'Extra', 'Help']
tab = Tab([IntSlider(description='hi') for _ in names])
[tab.set_title(i, title) for i, title in enumerate(names)]
tab

Interactive labeling of images in jupyter notebook

I have a list of pictures:
pictures = {im1,im2,im3,im4,im5,im6}
Where
im1:
im2:
im3:
im4:
im5:
im6:
I want to assign the pictures to labels (1,2,3,4 etc.)
For instance, here pictures 1 to 3 belong to label 1, picture 4 belongs to label 2, picture 5 to label 3, and picture 6 to label 4.
-> label = {1,1,1,2,3,4}
Since I need to see the images when I label them, I need a method to do that while labeling them. I was thinking of creating an array of images:
And then I define the ranges by clicking on the first and last picture belonging to the same labels, so for example:
What do you think ? Is this somehow possible ?
I would like to assign different labels to different ranges of pictures.
For instance: When one has finished selecting the first label one could indicate it by a Double-click and then do the selection of the second label range, then Double-click, then do the selection of the third label range, then Double-click, then do the selection of the fourth label range, etc.
It does not have to be Double-clicking to change the selection of the labels, it could also just be a buttom or any other idea that you might have.
In the end one should have the list of labels.
Essentially, most of the interaction you are looking for boils down to being able to display images, and detect clicks on them in real time. As that is the case, you can use the jupyter widgets (aka ipywidgets) module to achieve most (if not all) of what you are looking for.
Take a look at the button widget which is described here with explanation on how to register to its click event. The problem - we can't display an image on a button, and I didn't find any way to do this within the ipywidgets documentation. There is an image widget, but it does not provide an on_click event. So construct a custom layout, with a button underneath each image:
COLS = 4
ROWS = 2
IMAGES = ...
IMG_WIDTH = 200
IMG_HEIGHT = 200
def on_click(index):
print('Image %d clicked' % index)
import ipywidgets as widgets
import functools
rows = []
for row in range(ROWS):
cols = []
for col in range(COLS):
index = row * COLS + col
image = widgets.Image(
value=IMAGES[index], width=IMG_WIDTH, height=IMG_HEIGHT
)
button = widgets.Button(description='Image %d' % index)
# Bind the click event to the on_click function, with our index as argument
button.on_click(functools.partial(on_click, index))
# Create a vertical layout box, image above the button
box = widgets.VBox([image, button])
cols.append(box)
# Create a horizontal layout box, grouping all the columns together
rows.append(widgets.HBox(cols))
# Create a vertical layout box, grouping all the rows together
result = widgets.VBox(rows)
You can technically also write a custom widget to display an image and listen for a click, but I simply don't believe it's worth your time and effort.
Good luck!
The qsl package provides widgets that do this. For your case, the following code would allow you to label images in batches. Full disclosure, qsl is a project I started because I, like you, wanted to label images from inside Jupyter notebooks.
import qsl
from IPython.display import display
labeler = qsl.MediaLabeler(
items=[
{"target": "https://i.stack.imgur.com/cML6z.jpg"},
{"target": "https://i.stack.imgur.com/6EVAP.jpg"},
{"target": "https://i.stack.imgur.com/CAxUw.jpg"},
{"target": "https://i.stack.imgur.com/8fhan.jpg"},
{"target": "https://i.stack.imgur.com/eMXn5.jpg"},
{"target": "https://i.stack.imgur.com/YFBfM.jpg"}
],
# Optional, you can also configure the labeler from
# the UI.
config={
"image": [
{
"name": "Type",
"options": [
{"name": "Foo"},
{"name": "Bar"}
]
}
]
},
# Optional, set to 1 if you want to label
# one image at a time.
batch_size=4,
# Optionally, save labels to JSON. You
# can also get the labels using `labeler.items`.
jsonpath="labels.json"
)
display(labeler)
This generates a UI that looks like this.
Here is a Google Colab notebook that shows how to do this in Google Colab.

PyQt QComboBox setting number of visible items in dropdown

I'm working on an application in PyQt that takes an object dictionary and allows you to plot the variables streaming from a robot in real time. One of the things I'm working on to enable this is a drop down menu. Unfortunately, we have a couple hundred variables, so my PyQt Combobox pops up from the top of the screen to the bottom with items when clicked. I'd like to limit the number of items displayed at a time to 20, with the ability to scroll to see the rest. I've tried using the documented setMaxVisibleItems method, but it doesn't affect the drop down at all. Any recommendations?
Code here:
#!/usr/bin/env python
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QComboBox, QApplication
from cli.parc2_od import cli_od
import sys
app = QApplication(sys.argv)
items = cli_od.OD.keys() #Items to populate dropdown.
combo = QComboBox()
#The solution:
combo.setStyleSheet("QComboBox { combobox-popup: 0; }")
combo.setMaxVisibleItems(10)
combo.addItems(items)
combo.resize(300, 30)
combo.show()
sys.exit(app.exec_())
According to the documentation:
The property maxVisibleItems is ignored for non-editable comboboxes in styles that returns true for `QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style.
And you can override that SH_ComboBox_Popup style hint with a style sheet:
combo.setStyleSheet("QComboBox { combobox-popup: 0; }");

Resources