using urwid, I'm trying to separate the highlight/walk and cursor functionality of a Pile widget. How can I use up/down to change which widget is highlighted, while keeping the cursor in a different widget?
The default focus behavior couples the cursor with attribute (highlighting) behavior. The example below shows one way to decouple these, where a list of SelectableIcons retains the highlight feature, while the cursor is moved to a separate Edit widget. It does this via:
overriding the keypress method to update the focus where the cursor is not
wrapping each SelectableIcon in AttrMap that change their attribute based on their Pile's focus_position
after changing the SelectableIcon attributes, the focus (cursor) is set back to the Edit widget via focus_part='body'
self._w = ... is called to update all widgets on screen
There may be more concise ways of doing this, but this should be rather flexible.
import urwid
def main():
my_widget = MyWidget()
palette = [('unselected', 'default', 'default'),
('selected', 'standout', 'default', 'bold')]
urwid.MainLoop(my_widget, palette=palette).run()
class MyWidget(urwid.WidgetWrap):
def __init__(self):
n = 10
labels = ['selection {}'.format(j) for j in range(n)]
self.header = urwid.Pile([urwid.AttrMap(urwid.SelectableIcon(label), 'unselected', focus_map='selected') for label in labels])
self.edit_widgets = [urwid.Edit('', label + ' edit_text') for label in labels]
self.body = urwid.Filler(self.edit_widgets[0])
super().__init__(urwid.Frame(header=self.header, body=self.body, focus_part='body'))
self.update_focus(new_focus_position=0)
def update_focus(self, new_focus_position=None):
self.header.focus_item.set_attr_map({None: 'unselected'})
try:
self.header.focus_position = new_focus_position
self.body = urwid.Filler(self.edit_widgets[new_focus_position])
except IndexError:
pass
self.header.focus_item.set_attr_map({None: 'selected'})
self._w = urwid.Frame(header=self.header, body=self.body, focus_part='body')
def keypress(self, size, key):
if key == 'up':
self.update_focus(new_focus_position=self.header.focus_position - 1)
if key == 'down':
self.update_focus(new_focus_position=self.header.focus_position + 1)
if key in {'Q', 'q'}:
raise urwid.ExitMainLoop()
super().keypress(size, key)
main()
If you really need this, it probably makes sense to write your own widgets -- maybe based on some classes extending urwid.Text and urwid.Button
There is no real "highlight" feature in the widgets that come with urwid, there is only a "focus" feature, and it doesn't seem to be easy to decouple the focus highlight from the focus behavior.
You probably want to implement your own widgets with some sort of secondary highlighting.
Related
My goal is to create a pre-written code - or function - that I can apply to multiple questions, where I only need to fill in the information shown on the lower half. The top half code must remain general, because I do not want to replace pieces of the code every time I use it, and would rather have it do so automatically, making it Reusable.
At the moment my code keeps repeating itself even when I type in the correct answers ('Fully', 'Partially', 'None')
def enter_preset_option():
global subj_desc
subj_desc = input(question)
i = 0
while i < len(options):
if str(subj_desc.lower()) == options[i]:
print("Great!")
break
else:
print("Invalid input. Please try again!")
i += 1
enter_preset_option()
question = "Is the property fully fenced or partially fenced? (Answer 'Fully', 'Partially' or 'None'): "
options = ['fully', 'partially', 'none']
attribute = "Fenced"
enter_preset_option()
fenced = subj_desc
print(fenced)
I am pretty new to Plotly Dash and have been struggling especially with multivalue dropdown callback and would really appreciate any help. Basically I've followed a tutorial and created a pie-chart if a single pillar(from my data) value is selected. I would like to achieve two things:
The default or initial chart should show all pillar and the number of projects
Multi selection of pillar values
My main issue is actually the creating the callback for these. Thank you in advance for any help!!
Here is my code
app = dash.Dash(__name__)
all = df.Pillar.unique()
app.layout=html.Div([
html.H1("PM dashboard"),
dcc.Dropdown(id='pillar-choice',
options=[{'label':x, 'value':x}
for x in all],
value='Service Provider',
multi=False),
dcc.Graph(id='my-graph',
figure={}),
])
#app.callback(
Output(component_id='my-graph', component_property='figure'),
Input(component_id='pillar-choice', component_property='value')
)
def interactive_graphs(value_pillar):
print(value_pillar)
dff = df[df.Pillar==value_pillar]
fig = px.pie(data_frame=dff, names='Pillar', values='Project No')
return fig
if __name__=='__main__':
app.run_server()
I think the problem here is that value_pillar will be a list, so you need to do something like:
dff = df[df.Pillar.isin(value_pillar)]
And if you want to show everything by default, you'll need to check the value of that argument for your default value and, if it matches the default, avoid filtering.
I'm having trouble creating widgets in a Jupyter notebook that update when other widget values are changed. This is the code I've been playing around with:
from ipywidgets import interact, interactive, fixed
import ipywidgets as widgets
from IPython.display import display
def func(arg1,arg2):
print arg1
print arg2
choice = widgets.ToggleButtons(description='Choice:',options=['A','B'])
display(choice)
metric = widgets.Dropdown(options=['mercury','venus','earth'],description='Planets:')
text = widgets.Text(description='Text:')
a = interactive(func,
arg1=metric,
arg2=text,
__manual=True)
def update(*args):
if choice.value == 'A':
metric = widgets.Dropdown(options=['mercury','venus','earth'],description='Planets:')
text = widgets.Text(description='Text:')
a.children = (metric,text)
else:
metric = widgets.Dropdown(options=['monday','tuesday','wednesday'],description='Days:')
text2 = widgets.Textarea(description='Text2:')
a.children = (metric,text2)
choice.observe(update,'value')
display(a)
The resulting widgets metric and text do change based whether A or B is selected, but the problem is that the "Run func" button goes away as soon as I change to B. I've tried adding the __manual attribute immediately before display(a), adding it within update, and several other places. How do I change the children of the widget box without overwriting the fact that I want to manually run the function?
This is about unit testing (using Python's unittest module). I'm trying to implement, programmatically, the user's pressing "F2" to start editing the cell of a JTable.
The utility method "run_in_edt" wraps the passed method in a Runnable and then runs it using invokeAndWait, rather than invokeLater.
def test_can_edit_table_date(self):
main_frame = FTCase2.app.main_frame
dates_table = main_frame.dates_table
def start_editing():
dates_table.requestFocus()
f2_key_stroke = javax.swing.KeyStroke.getKeyStroke( java.awt.event.KeyEvent.VK_F2, 0 )
im = dates_table.getInputMap( javax.swing.JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT )
action_value = im.get( f2_key_stroke )
self.assertEqual( action_value, 'startEditing' )
am = dates_table.actionMap
self.f2_action = am.get( action_value )
self.assertIsNotNone( self.f2_action )
sel_row = dates_table.selectedRow
self.assertNotEqual( sel_row, -1 )
self.assertTrue( dates_table.isCellEditable( sel_row, 0 ))
self.start_editing_action_event = java.awt.event.ActionEvent( dates_table,
java.awt.event.ActionEvent.ACTION_FIRST, 'X' )
self.f2_action.actionPerformed( self.start_editing_action_event )
# dates_table.editCellAt( sel_row, 0 )
# self.assertTrue( dates_table.editing )
_utils.run_in_edt( start_editing )
# time.sleep( 1 )
def write_string_in_cell_editor():
self.assertTrue( dates_table.editing )
cell_editor = dates_table.cellEditor
self.assertIsNotNone( cell_editor )
cell_value = cell_editor.cellEditorValue
cell_editor.component.text = "mouse"
self.f2_action.actionPerformed( self.start_editing_action_event)
_utils.run_in_edt( write_string_in_cell_editor )
The problem: "dates_table.editing" always comes out false... and getting the cell editor returns None. I have also tried putting a sleep between these two Runnables, just in case it was a question of "events having to bubble up/down"...
NB I also tried with a more sensible value as the 3rd param of ActionEvent, such as action_value (i.e. 'startEditing'). No joy.
I can of course do:
dates_table.editCellAt( sel_row, 0 )
... with this uncommented, what's interesting is that, in the second method here, I set the JTextField's ("editor delegate") text to "mouse", and then "press F2" by using the action.actionPerformed... and... it works, in the sense that in my table cell renderer I allow only dates values or None, not strings, so an AssertionError is raised. Meaning that I have managed to simulate an F2 key press (NB although the name of this action is "startEditing", it also stops an editing session, in real life as in testing).
... I could content myself with using editCellAt, and having ascertained that F2 has the right entry in the right InputMap, and that the value is pops out is an Action (could be checked) with the name "startEditing", which is proven to be capable of ending an edit, I could just content myself with that.
But I so hate it when my understanding is revealed to be less than good! I want to know WHY this doesn't work...
Found the answer and put it here for reference.
My requestFocus() action on the JTable did indeed leave selection on the right row, but without any selection of the (single) column. Even when running as normal (not testing) the JTable column did not initially respond to F2. It was puzzling to me why the cell was not initially surrounded by a heavy black border. The answer was therefore to put this line after the requestFocus line:
dates_table.setColumnSelectionInterval( 0, 0 )
How can I left align, rather than right align, the label in an iPython 3 widget, such as IntSlider? My ultimate goal is to left align a set of labeled widgets. This entails left aligning their labels, since the label is the leftmost element of each widget.
I've read the discussion in
Aligning TextBox Widgets in IPython Notebooks, but (a) it focuses on making more space for a right-aligned label, and (b) the proposed solution does not seem to affect the label width. (As an aside, I would be interested in finding cell-executable code that can reset the minimum label width.)
I've also read the discussion in Change the size of the label in an IPython notebook widget, but it doesn't seem to offer a simple solution.
Thanks for your help.
Addendum (2015-06-02):
Looks like widget.interactive() does not play nicely with the solution suggested by Jakob. Example:
from IPython.html import widgets
from IPython.display import display
def mySlider(text='', twidth=100, min=0, max=10, value=5):
c1 = widgets.HBox()
ints = widgets.IntSlider(min=min, max=max, value=value)
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
return c1
s1 = mySlider('Test')
s2 = mySlider('TestTest')
s3 = mySlider('TestTestTest')
def process(a, b, c):
print([a, b, c])
widgets.interactive(
process,
a=s1.children[1].value,
b=s2.children[1].value,
c=s3.children[1].value
)
yields slider labels a, b, c with the usual alignment.
You can simply combine an IntSlider with an Html widget to create your custom widget like
from IPython.html import widgets
from IPython.display import display
def mySlider(text='', twidth=100):
c1 = widgets.HBox()
ints = widgets.IntSlider()
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
return c1
Using this method, some widgets could look like
s1 = mySlider('Test')
s2 = mySlider('TestTest')
s3 = mySlider('TestTestTest')
display(s1,s2,s3)
Update to work with interact
To use these custom widgets with interact it is necessary to add some properties and callbacks. The interact method requires the widget.description and the widget.value arguments to setup the interactive widgets. As our container widget does not have these arguments, they are added manually. Moreover it is necessary to link the container.value with the IntSlider.value. This is once realized by a simple assignment, and more important via the on_trait_change method.
Finally, the interact methods calls the process function on widget.on_trait_change callbacks, thus the container.on_trait_change method is replaced by the IntSlider.on_trait_change call.
The updated code looks like:
def mySlider2(text='', twidth=100, min=0, max=10, value=5):
c1 = widgets.HBox()
ints = widgets.IntSlider(min=min, max=max, value=value)
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
c1.description = text
c1.value = ints.value
def update(name, value):
c1.value = value
ints.on_trait_change(update,'value')
c1.on_trait_change = ints.on_trait_change
return c1
s1a = mySlider2('Test')
s2a = mySlider2('TestTest')
s3a = mySlider2('TestTestTest')
widgets.interactive(
process,
a=s1a,
b=s2a,
c=s3a
)