How to align and place ipywidgets - jupyter-notebook

Is there a way to control the placement and alignment of ipywidgets (inside jupyter notebook)?
from ipywidgets import widgets
from IPython.display import Javascript, display
event_type_ui = widgets.Text(value='open', description='Test Event')
platform_ui = widgets.Text(value='Android', description='Control Event')
display(event_type_ui)
display(platform_ui)
I would like to specify an offset (in pixels?) to allow the label to fit and to have two controls aligned vertically.

After some fiddling, I was able to get this:
Before:
After:
Here is a copy-paste snippet, if interested:
from ipywidgets import widgets
from IPython.display import Javascript, display
align_kw = dict(
_css = (('.widget-label', 'min-width', '20ex'),),
margin = '0px 0px 5px 12px'
)
platform_ui = widgets.Dropdown(description = 'platform',options=['iPhone','iPad','Android'], **align_kw)
event_type_ui = widgets.Text(value='open', description='Test Event', **align_kw)
os_version_ui = widgets.Text(value='iOS9', description='Operating System', **align_kw)
refresh_ui = widgets.Checkbox(description='Force Refresh', **align_kw)
display(platform_ui,event_type_ui,os_version_ui,refresh_ui)

The layout and styling documentation for ipywidgets says:
Every Jupyter interactive widget has a layout attribute exposing a number of css properties that impact how widgets are laid out.
I was able to coax it into aligning the labels:
from ipywidgets import Text, HBox, VBox, Box
from IPython.display import display
widget1 = Text(value='Cats', description='Test Event', layout='margin-right:5px')
widget2 = Text(value='Oranges', description='Another Test Event')
widget1.width = '200px'
widget2.width = '150px'
display(VBox([widget1, widget2]))
to produced this:
But in general, I doesn't seem like we can directly target layout properties of the description label, just the entire widget itself.

The ipywidgets also have an add_class that can be used for getting into the weeds when you really need it:
import ipywidgets as widgets
from IPython.display import display, HTML
widget_list={}
for label in ("longish description","brief"):
widget_list[label]=widgets.Text(description=label)
widget_list[label].add_class("balanced-text")
display(HTML("<style>div.balanced-text .widget-label {max-width:200px; width:200px}</style>"))
display(widgets.VBox(list(widget_list.values())))

Related

Insert folium map html code inside a bokeh app

I posted this as a followup question on Include folium in bokeh tabs, and now as a new question as well.
I´m trying to render the raw HTML-code from my folium map, but it´s not working.. Any ideas? :)
div = Div(
text=map.get_root().render(),
width=x,
height=y
)
I would much rather be able to render my folium map directly into a bokeh Div object instead of running an Flask app on the side.. I have looked into the possibilities of using an iframe, but there seems to be something off with my code here as well:
div.text = """<iframe srcdoc= """ + map.get_root().render() + """ height=""" + y + """ width=""" + x +"""></iframe>"""
I managed to use a Flask app on the side for the folium map and then use the url as src to my iframe, but then I was having trouble updating the content of that map from my bokeh tool.
Feel free to comment on anything of the above, cheers! :)
Update - Testscript:
from bokeh.models.widgets import Div
from bokeh.layouts import row
from bokeh.plotting import curdoc
import folium
def run():
folium_map = folium.Map(location=(60., 20.))
div = Div(
text=folium_map._repr_html_(),
width=500,
height=500,
)
return row(div)
bokeh_layout = run()
doc = curdoc()
doc.add_root(bokeh_layout)
With map.get_root().render(), you have all the HTML page. If you just want an iframe, you can use the method _repr_html_() of the folium map :
div = Div(
text=map._repr_html_(),
width=x,
height=y
)

With Bokeh, how to hide the icon of a tool while keeping it enabled?

With Bokeh, how to hide the icon of a tool while keeping it enabled?
Some context, I have multiple p.line() plots in a single figure. Each line plot has its own hover tool as per this question.
I don't find it appealing though, to have each hover tool its own icon:
.
So I thought about keeping the multiple Hover tools but hiding them from the user.
What are my options?
Thanks in advance.
First, I would ask myself if a user would like a hidden hover that can't be disabled?
So first option I would suggest is adding a tooltips not to each renderer but to the figure which creates a single HoverTool icon that applies to all lines in the plot like this: (Bokeh v1.3.0)
from bokeh.plotting import figure, show
from bokeh.models import HoverTool, Column, Button, CustomJS
import numpy as np
p = figure(toolbar_location='above',
tooltips=[("x", "#x")]
)
lines = [p.line(np.arange(10), np.random.random(10)) for i in range(3)]
for i in range(len(lines)):
p.add_tools(HoverTool(tooltips=[("x", "#x")], renderers=[lines[i]]))
# button = Button(label='Hide Hover Icon')
# code = ''' hover_btns = document.getElementsByClassName('bk-tool-icon-hover')
# for(i=0; i<hover_btns.length; i++)
# hover_btns[i].style.display = 'none' '''
#
# button.callback = CustomJS(code=code)
show(Column(p,
# button,
))
Then if you really don't want to see any hover icon in your toolbar you could add a JS callback executed on button click that hides all hover icons like this:
from bokeh.plotting import figure, show
from bokeh.models import HoverTool, Column, Button, CustomJS
import numpy as np
p = figure(toolbar_location='above',
# tooltips=[("x", "#x")]
)
lines = [p.line(np.arange(10), np.random.random(10)) for i in range(3)]
for i in range(len(lines)):
p.add_tools(HoverTool(tooltips=[("x", "#x")], renderers=[lines[i]]))
button = Button(label='Hide Hover Icon')
code = ''' hover_btns = document.getElementsByClassName('bk-tool-icon-hover')
for(i=0; i<hover_btns.length; i++)
hover_btns[i].style.display = 'none' '''
button.callback = CustomJS(code=code)
show(Column(p,
button,
))
But doing so requires a user to click on the button first, so maybe a more elegant way to do this would be to call the JS code already at page load using an approach described here.

How to get wider text for Checkbox widget?

How can I configure the ipywidgets Checkbox for long text strings as in,
c = Checkbox(description=' this is some very long very long text',value=False)
and not get the text to be squeezed and wrapped in the encompassing Jupyter notebook?
Thanks!
Simply use this:
import ipywidgets as widgets
c = widgets.Checkbox(
description='This is some very long very long text',
value=False,
layout=widgets.Layout(width='100%'))
Another option is to bundle it with Label:
from ipywidgets import widgets, Layout
from IPython.display import display
checkbox = widgets.Checkbox(value=False, disabled=False, layout=Layout(width='30px'))
label = widgets.Label('description', layout=Layout(width='500px', margin='6px 0 0 -10px'))
box = widgets.HBox([checkbox, label])
display(box)
Use this to set the width to the left of the checkbox and also set the overall width independently
import ipywidgets as widgets
c = widgets.Checkbox(
description=' this is some very long very long text',
value=False)
c.style = {'description_width': '0px'} # sets the width to the left of the check box
c.layout.width = 'auto' # sets the overall width check box widget
c
Put this is the cell with the checkbox
from IPython.display import display, HTML
HTML('<style> .widget-hbox .widget-label { max-width:350ex; text-align:left} </style>')
and it'll change the widths and alignment.

Autodesk Maya model panel resize event

I'm writing a simple tool menu for Maya, and I'd like to stick it to the border of model panel (perspective).
from PySide import QtCore, QtGui
from maya import OpenMayaUI as omui
from shiboken import wrapInstance
class TestWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent = self.getMayaWindow())
self.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.FramelessWindowHint)
self.setFixedSize(100, 100)
panelPtr = omui.MQtUtil.findControl('modelPanel4')
panel = wrapInstance(long(panelPtr), QtGui.QWidget)
position = panel.mapToGlobal(panel.pos())
self.move(position.x(), position.y() + panel.geometry().height() / 2 - self.geometry().height() / 2)
mainLayout = QtGui.QVBoxLayout(self)
button = QtGui.QPushButton('CLOSE')
button.setFixedSize(80, 80)
button.clicked.connect(self.deleteLater)
mainLayout.addWidget(button)
def getMayaWindow(self):
omui.MQtUtil.mainWindow()
ptr = omui.MQtUtil.mainWindow()
return wrapInstance(long(ptr), QtGui.QWidget)
w = TestWidget()
w.show()
The main widget is positioned exactly where I want when it is created (horizontally on the left side of model panel, vertically - in the middle of model panel).
I need to reposition it accordingly when the model panel is resized, but model panel does not emit resized() signal. I'd appreciate any advise.
I've been trying many things to get this working yesterday. I did some additionnal researches today and came to this topic: cgsociety: Creating a floating button inside the viewport
In case of broken link, this is one of the answer:
You can use geometry but there are some issues with triggering
commands based on selection and the undo queue. If you want to go that
route, I would suggest looking into zooHud and zooTriggers (Part of
the zooToolbox)
If you are wanting actual GUI control parented to the viewport, mel
only offers hudslider, hudbutton, and headsUpMessage.
You can also use PyQt and parent in your own custom widgets/layouts or
whatever you want using something like this:
import maya.OpenMayaUI as apiUI import sip
from PyQt4 import QtGui
view = apiUI.M3dView()
apiUI.M3dView.getM3dViewFromModelPanel('modelPanel4', view)
viewWidget = sip.wrapinstance(long(view.widget()), QtCore.QObject)
global myBtn
myBtn = QtGui.QPushButton(viewWidget)
myBtn.setText('testing!')
myBtn.move(100, 100) #Relative to top-left corner of viewport myBtn.show()
You can do anything a full qt widget can do with that, so it's
extremely flexible. but it would require having PyQt installed, which
can be a barrier depending on your tools distribution.
I did a mix of this answer and your code:
from PySide import QtCore, QtGui
from maya import OpenMayaUI as omui
from shiboken import wrapInstance
class CustomQWidget(QtGui.QWidget):
def __init__(self, *args, **kwargs):
QtGui.QWidget.__init__(self, *args, **kwargs)
mainLayout = QtGui.QVBoxLayout(self)
closeButton = QtGui.QPushButton('CLOSE')
closeButton.setFixedSize(80, 40)
closeButton.clicked.connect(self.deleteLater)
helloButton = QtGui.QPushButton('HELLO')
helloButton.setFixedSize(80, 40)
helloButton.clicked.connect(self.printHello)
#Trying to fix glitchy background / Doesn't work, why?
#Is it because layouts don't have background?
p = self.palette()
p.setColor(self.backgroundRole(), QtCore.Qt.red)
self.setPalette(p)
self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
##############################
mainLayout.addWidget(closeButton)
mainLayout.addWidget(helloButton)
def printHello(self):
print "Hello"
view = omui.M3dView()
omui.M3dView.getM3dViewFromModelPanel('modelPanel4', view) #Given the name of a model panel,
#get the M3dView used by that panel. If this fails, then a panel with the given name could not be located.
viewWidget = wrapInstance(long(view.widget()), QtGui.QWidget)
position = viewWidget.mapToGlobal(viewWidget.pos())
w = CustomQWidget(viewWidget)
w.move(0, viewWidget.geometry().height() / 2 - 100 / 2) #Relative to middle-left corner of viewport
w.show()
One of the issue I have it that the background of the widget is glitched:
If anyone knows why and how to fix it, I'll edit my answer with pleasure.
Else, when running this script from Maya's script editor, the widget follows the panel when it is resized.
I did fix such a problem, but not using Python/PyQt.
The problem itself is, that your Qt Widget is there. I have not found a way to make it not paint its background.
My solution was different: I derived from a Qt Layout, pushed all my widgets into that layout and used MQtUtil to get the QWidget of that modelPanel's modelEditor to attach the "real Qt layout" to it.
Heavy caveat that may make Python not suited: Maya doesn't expect "non-Maya" Layouts to be bound to "real-Maya" Widgets like modelEditors. So you need to listen to QEvents and find out when to destroy your layout, so Maya doesn't crash trying.
set autofillbackground True to fix your background painting issue

qlistwidgetitem with custom widget doesn't look selected on the UI

I have a Qlistwidget in icon mode and I'm using setItemWidget to display my elements in my custom widgets, so far this is working.
Pretty much is like this one:
https://stackoverflow.com/questions/3639468/what-qt-widgets-to-use-for-read-only-scrollable-collapsible-icon-list
The only problem I have is that when I select the items, they don't look selected (no frame around them). They are being selected as I'm getting the right signals but you can't see the selection on the UI.
Any ideas on how to make them appear selected?
** Edit to add sample code **
(it is a modification on the code found on the previous link)
import sys
from PyQt4 import QtGui, QtCore
class displayItem(QtGui.QWidget): #A simple widget to display, just centers a digit in a 100x100 widget
def __init__(self,num):
QtGui.QWidget.__init__(self)
self.size=100
self.resize(self.size,self.size)
self.setMinimumSize(self.size,self.size)
self.text = num
def paintEvent(self,event):
p = QtGui.QPainter(self)
p.drawText(self.size//2,self.size//2,str(self.text))
app = QtGui.QApplication(sys.argv)
#Build the list widgets
list1 = QtGui.QListWidget() #This will contain your icon list
list1.setMovement(QtGui.QListView.Static) #otherwise the icons are draggable
list1.setResizeMode(QtGui.QListView.Adjust) #Redo layout every time we resize
list1.setViewMode(QtGui.QListView.IconMode) #Layout left-to-right, not top-to-bottom
listItem = QtGui.QListWidgetItem(list1)
listItem.setSizeHint(QtCore.QSize(100,100)) #Or else the widget items will overlap (irritating bug)
list1.setItemWidget(listItem,displayItem(1))
listItem = QtGui.QListWidgetItem(list1) #Add a few more items
listItem.setSizeHint(QtCore.QSize(100,100))
list1.setItemWidget(listItem,displayItem(2))
listItem = QtGui.QListWidgetItem(list1)
listItem.setSizeHint(QtCore.QSize(100,100))
list1.setItemWidget(listItem,displayItem(3))
list1.show() #kick off the app in standard PyQt4 fashion
sys.exit(app.exec_())
Thanks
/J
Yes. .
it is related to the viewMode.
When I set the viewMode for the list1 as ListMode, selected items look selected(highlighted)
list1.setViewMode(QtGui.QListView.ListMode)
still trying to figure out why it is not working with the iconMode. . .

Resources