I have a row with text data in some cells and other cells have widgets in it, Can I transfer these cells in the row, so I mean the whole row to another qtablewidget without having to break the row into small data then rebuild it again into the other qtablewidget?
Note: please don't ask me for minimal pro-- because I'm only asking before doing it so I be aware what's waiting for me.
There is no function like that for QTableWidget (the only similar function only exists for QStandardItemModel: takeRow()).
In order to remove items you need to use insertRow() on the target table, takeItem() for each column in the source row, and setItem() on the target, and finally removeRow().
def moveRow(self, row):
targetRow = self.target.rowCount()
self.target.insertRow(targetRow)
for column in range(self.source.columnCount()):
item = self.source.takeItem(row, column)
if item:
self.target.setItem(targetRow, column, item)
self.source.removeRow(row)
Unfortunately, this won't let you do anything for cell widgets. When a widget is set on a cell in an item view, the view will take complete and definitive ownership on the widget: even if you try to use removeWidget or setCellWidget with another widget, the previous one will be deleted internally by Qt (see the sources for QAbstractItemView.setIndexWidget(), which is called for both removeWidget and setCellWidget).
The only solution would be to check if the cell has a widget, create a new instance of the same class and copy its properties.
A possible workaround is to add cell widgets using a container widget with a layout, add the actual widgets to that layout, and then create a new container and set the layout for it:
# add a container for the actual widget
container = QtWidgets.QWidget()
contLayout = QtWidgets.QVBoxLayout(container)
# layout usually add some margins to their widgets, let's remove them
contLayout.setContentsMargins(0, 0, 0, 0)
testButton = QtWidgets.QPushButton('test')
contLayout.addWidget(testButton)
self.source.setCellWidget(0, 0, container)
def moveRow(self, row):
# ...
widget = self.source.cellWidget(row, column)
if widget and widget.layout() is not None:
newContainer = QtWidgets.QWidget()
newContainer.setLayout(widget.layout())
self.target.setCellWidget(targetRow, column, newContainer)
self.source.removeRow(row)
Related
How to find the number of rows and columns in QGridlayout ?, In my code, I have Buttons arranged in QGridLayout. Now I need to find out the total number of columns and the total number of rows.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Widget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QGridlayout")
self.btn1 = QPushButton("Button_1")
self.btn2 = QPushButton("Button_2")
self.btn3 = QPushButton("Button_3")
self.btn4 = QPushButton("Button_4")
self.btn4.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.MinimumExpanding)
self.btn5 = QPushButton("Button_5")
self.btn6 = QPushButton("Button_6")
self.btn7 = QPushButton("Button_7")
self.btn8 = QPushButton("Button_8")
self.btn9 = QPushButton("Button_9")
self.gl = QGridLayout()
self.gl.addWidget(self.btn1,1,0,1,1,Qt.AlignCenter)
self.gl.addWidget(self.btn2,0,1,1,1)
self.gl.addWidget(self.btn3,0,2,1,1)
self.gl.addWidget(self.btn4,0,3,2,1)
self.gl.addWidget(self.btn5,1,0,1,2)
self.gl.addWidget(self.btn6,2,0,1,3)
self.gl.addWidget(self.btn7,3,0,1,4)
self.gl.addWidget(self.btn8,1,2,1,1)
self.gl.setRowStretch(4,1)
self.gl.setColumnStretch(2,1)
self.gl.setSpacing(1)
self.setLayout(self.gl)
print(self.gl.count())
# print(self.gl.rowcount())
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
For basic usage, rowCount() and columnCount() will suffice, as already said in other answers.
Be aware, though, that the above will only be reliable for the following conditions:
layout items (widgets, nested layouts, spacers) are always added continuously: for instance, grid layout allows adding an item at row 0 and another at row 10, even if they only occupy one row and no other item occupies the inner rows and even if no row spanning occurs in the middle;
removal of items will never clear the row or column count, even if there is no item at or after the "last" row or column;
rowCount() and columnCount() will always return a number equal or greater than 1, even if the layout is empty;
considering the above, dynamic insertion of new items between existing ones can become quite messy;
To explain the above, consider the following example:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
app = QApplication([])
window = QWidget()
layout = QGridLayout(window)
def addRow():
row = layout.rowCount()
label = QLabel(str(row))
label.setStyleSheet('border: 1px solid black;')
label.setMinimumSize(100, 30)
label.setAlignment(Qt.AlignCenter)
layout.addWidget(label)
def removeRow():
item = layout.itemAt(layout.count() - 1)
widget = item.widget()
if widget:
widget.deleteLater()
layout.removeItem(item)
for r in range(4):
addRow()
window.show()
QTimer.singleShot(1000, removeRow)
QTimer.singleShot(2000, addRow)
QTimer.singleShot(3000, removeRow)
QTimer.singleShot(4000, addRow)
app.exec()
The steps are the following:
create the first 4 labels; note that the label text shows the rowCount() before adding it to the layout: the text of the first label is "1", even if, at that moment, the layout is empty; the second label shows again "1", since the layout now does have an item at row 1;
remove the last label ("3") and the layout item corresponding to it; the layout now theoretically has only 3 rows; the layout item removal is actually pointless for this, since it won't change the result (I only added it for explanation purposes);
add another label; the new label displays "4", not "3";
remove the "4" label; the layout theoretically has only 3 rows again;
add a further label, which displays "5";
While counter-intuitive, this behavior is done just for performance, simplicity and consistency reasons: the row/column count is static, and always based on the greatest row/column index (plus possible spanning if greater than 1). While adding/removing items within the same function call, the grid size must be consistent (and as fast as possible). Also, the computation of layout managers is possibly quite important for performance, as each layout has to query its own child items, ask about their size hints and policies and then make complex computations to set their geometries, and all this becomes recursive for nested layouts.
If you want a more dynamic approach you can still implement it, for example with this simple (but costly) function:
def gridLayoutRealRowCount(layout):
rows = 0
for i in range(layout.count()):
row, _, span, _ = layout.getItemPosition(i)
rows = max(1, row + span)
return rows
Now you can replace the row = layout.rowCount() used before with the above function:
def addRow():
row = gridLayoutRealRowCount(layout)
# ...
And you'll get a more consistent result: the labels will always show "0", "1", "2" (and eventually "3"), even after removing the last item.
You can also make a "shim" (or "pseudo-polyfill") if you create a monkey patch after the very first Qt import; assuming all your Qt imports are consistent, you can use it from anywhere:
def gridLayoutRealRowCount(layout):
rows = 0
for i in range(layout.count()):
row, _, span, _ = layout.getItemPosition(i)
rows = max(1, row + span)
return rows
def gridLayoutRealColumnCount(layout):
columns = 0
for i in range(layout.count()):
_, column, _, span = layout.getItemPosition(i)
columns = max(1, columns + span)
return columns
QGridLayout.getRowCount = gridLayoutRealRowCount
QGridLayout.getColumnCount = gridLayoutRealColumnCount
Which allows the following:
def addRow():
row = layout.getRowCount()
# ...
If you require high performance and you're not interested on a "rigid" grid layout, you should consider other options, like a QFormLayout or a nested vertical+horizontal layout.
For the number of columns: QGridLayout.columnCount()
For the number of rows: QGridLayout.rowCount()
num_rows = self.gl.rowCount()
num_columns = self.gl.columnCount()
QGridLayout has methods rowCount() and columnCount() that you can use to get the rows and columns respectively. I see you commented out a call to rowcount, I am guessing you may just need to change that to rowCount
I cannot prevent the rightest column of a Gtk.TreeView to expand.
As the real Gtk.TreeView may display a greater number of rows, making it usually somewhat greater than the screen's height, it is embedded in a Gtk.ScrolledWindow. This is required. Without it, attaching an empty grid at the right of the treeview, expanding itself horizontally, would fix the problem. Based on this idea, I've tried a workaround that introduces another difficulty (see below).
I have built a minimal working example from the example from https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html#filtering, without filtering nor buttons; and the columns are 80 px wide at least (this works) and their content is horizontally centered. This last detail makes the horizontal expansion of the rightest column visible. In the original example, it does expand too, but as everything is left aligned, this is not really visible. I'd liked to keep the columns' content centered, without seeing the rightest expanded.
This example is minimal, but contains some helping features: you'll find clickable column titles, that will display some information about the clicked column in the console; a remove button (works fine, remove the selected rows) and a paste button that allows to paste new rows from a selection (e.g. from selected lines from a spreadsheet, but there's nothing to check the data are correct, if you paste something that does not convert to int, it will simply crash).
Workaround
A workaround I've tried consist of gathering both the treeview and a horizontally expanding empty right grid at its right inside a grid that would be put inside the Gtk.ScrolledWindow. It works, but causes other subtle problems: in some situations, the treeview does not get refreshed (it happens after a while), yet nothing prevents the main loop to refresh the view (there's no other processing in the background, for instance). To experiment this workaround: comment and uncomment the lines as described in the code below; run the program via python script.py (if you need to install pygobject in a venv, see here), notice the rightest column does not expand to the right any longer, select the 3 first rows and press "remove", then from a spread sheet, select 3 lines of dummy integers as shown below and then press "paste". Scroll down to the last rows: you'll see most of the time that the 3 pasted lines do not show up, even if it is possible to scroll over the last row. Maybe one of them will show up after some time, then another... (or simply select a row, and they'll show up). Strangely, it happens if one has just removed as many lines as one wants to paste after the removal (3 removed, 3 pasted; or 4 removed, 4 pasted etc.).
Example spreadsheet selection:
Question
So, I'd prefer to avoid the workaround (I'm afraid I may find other situations triggering a bad refreshing of the treeview), that I could not fix itself (for instance, setting self.scrollable_treelist.set_propagate_natural_height(True) proved useless, maybe I'm not using it correctly though?) and only attach the treeview itself directly in the Gtk.ScrolledWindow. How to prevent the rightest column to expand, then?
(I've tried to use a fair amount of setters and properties of the cell renderers, the treeview, the treeview columns, the scrolled window, to no avail. Some of them are still in the code below.)
Any solution using and fixing the workaround above would be accepted though.
In any case, the treeview may be scrolled, and lines may be added and removed from it without any refreshing problem.
Source Code
import gi
try:
gi.require_version('Gtk', '3.0')
except ValueError:
raise
else:
from gi.repository import Gtk, Gdk
# ints to feed the store
data_list = [(i, 2 * i, 3 * i, 4 * i, 5 * i) for i in range(40)]
class AppWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Treeview Columns Size Demo")
self.set_border_width(10)
# Setting up the self.grid in which the elements are to be positioned
self.grid = Gtk.Grid()
self.grid.set_column_homogeneous(True)
self.grid.set_row_homogeneous(True)
self.add(self.grid)
# Creating the ListStore model
self.store = Gtk.ListStore(int, int, int, int, int)
for data_ref in data_list:
self.store.append(list(data_ref))
# creating the treeview and adding the columns
self.treeview = Gtk.TreeView(model=self.store)
rend = Gtk.CellRendererText()
rend.set_alignment(0.5, 0.5)
for i, column_title in enumerate([f'nĂ—{p}' for p in [1, 2, 3, 4, 5]]):
column = Gtk.TreeViewColumn(column_title, rend, text=i)
column.set_min_width(80)
# column.set_max_width(80)
# column.set_fixed_width(80)
# column.set_sizing(Gtk.TreeViewColumnSizing(1))
column.set_alignment(0.5)
column.set_clickable(True)
column.connect('clicked', self.on_column_clicked)
self.treeview.append_column(column)
self.treeview.set_hexpand(False)
self.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
# Put the treeview in a scrolled window
self.scrollable_treelist = Gtk.ScrolledWindow()
self.scrollable_treelist.set_vexpand(True)
self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)
self.scrollable_treelist.add(self.treeview)
# WORKAROUND
# Alternatively, embed the treeview inside a grid containing an
# empty grid to the right of the treeview
# To try it: comment out the previous line; uncomment next lines
# scrolled_grid = Gtk.Grid()
# empty_grid = Gtk.Grid()
# empty_grid.set_hexpand(True)
# scrolled_grid.attach(self.treeview, 0, 0, 8, 10)
# scrolled_grid.attach_next_to(empty_grid, self.treeview,
# Gtk.PositionType.RIGHT, 1, 1)
# self.scrollable_treelist.add(scrolled_grid)
# self.scrollable_treelist.set_propagate_natural_height(True)
# Buttons
self.remove_button = Gtk.Button(label='Remove')
self.remove_button.connect('clicked', self.on_remove_clicked)
self.paste_button = Gtk.Button(label='Paste')
self.paste_button.connect('clicked', self.on_paste_clicked)
self.grid.attach_next_to(self.remove_button, self.scrollable_treelist,
Gtk.PositionType.TOP, 1, 1)
self.grid.attach_next_to(self.paste_button, self.remove_button,
Gtk.PositionType.RIGHT, 1, 1)
self.set_default_size(800, 500)
self.show_all()
# Clipboard (to insert several rows)
self.clip = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
self.clip2 = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
def on_column_clicked(self, col):
print(f'col.get_sizing()={col.get_sizing()}')
print(f'col.get_expand()={col.get_expand()}')
print(f'col.get_width()={col.get_width()}')
print(f'col.get_min_width()={col.get_min_width()}')
print(f'col.get_max_width()={col.get_max_width()}')
print(f'col.get_fixed_width()={col.get_fixed_width()}')
def on_remove_clicked(self, widget):
model, paths = self.treeview.get_selection().get_selected_rows()
refs = []
for path in paths:
refs.append(Gtk.TreeRowReference.new(model, path))
for ref in refs:
path = ref.get_path()
treeiter = model.get_iter(path)
model.remove(treeiter)
# print(f'AFTER REMOVAL, REMAINING ROWS={[str(r[0]) for r in model]}')
def on_paste_clicked(self, widget):
text = self.clip.wait_for_text()
if text is None:
text = self.clip2.wait_for_text()
if text is not None:
lines = text.split('\n') # separate the lines
lines = [tuple(L.split('\t')) for L in lines] # convert to tuples
print(f'PASTE LINES={lines}')
for line in lines:
if len(line) == 5:
line = tuple(int(value) for value in line)
self.store.append(line)
win = AppWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
I have a QTableWidget with some rows.
In each row, one of the cells has another widget set via setCellWidget.
I would like to style this cellWidget based on whether or not the row is selected. For reference, the cellWidget is another QTableWidget but it is disabled/not editable and essentially read-only.
I have found the syntax for accessing sub-controls (in particular, accessing the item of the parent QTableWidget) -- namely MainTable::item https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-sub-controls
I have also found the (more standard) css-syntax for accessing the pseudo-state of the control -- namely MainTable::item:selected. https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-pseudo-states
If I naively use this to style the selected item (tablerow) as yellow as below
def add_file(self, row, element):
"""populate a new row in the table"""
# self is the parent QTableWidget
self.setRowHeight(row, self.ICON_SIZE.height())
img = self.create_thumbnail(element['filepath'])
# add an image to the first column
item = QTableWidgetItem("",0)
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
item.setData(Qt.DecorationRole, img)
item.setData(Qt.TextAlignmentRole, Qt.AlignHCenter|Qt.AlignCenter)
item.setData(Qt.SizeHintRole, self.ICON_SIZE)
self.setItem(row, self.THUMBCOL, item)
# StatsTable is another nested QTableWidget
stats = StatsTable(element)
# add the new nested table to the outer main tables second column
self.setCellWidget(row, self.STATSCOL, stats)
self.setStyleSheet("""
MainTable::item:selected
{
background: yellow;
color: purple;
}
""")
The entire row except for the cellWidget will have a yellow background.
Now if I modify the QSS-selector in an attempt to access the child widget, I get unexpected results:
MainTable::item:selected QTableWidget
{
background: yellow;
color: purple;
}
this results in every row having its cellWidget-table given a yellow background independent of the selection-status of the row (unlike before where only the selected row sans the nested table had a yellow background).
Is there something simple I am overlooking here, or do I have to create some callbacks to manually apply and unapply the style when a row is selected?
this is a selected row with the first QSS applied
this is a selected row with the second QSS applied
neither of these two has the cellWidget styled if the row is selected.
As an alternative to using item delegates, I added a callback to the itemSelectionChanged-signal, and iterate through the rows in the main table. I set a property value on the child-tableWidget depending on whether the row is selected or not. This property is accessed in the stylesheet.
Unfortunately it seems I have to force a recalculation of the stylesheet by setting it in its entirety, so the seemingly clever workaround is not all that clever after all.
Since my nested widget is very restricted (read only, disabled so it can not be navigated to, ...) I do not think I need the flexibility of a custom item delegate, even though it probably is a better solution. I also expect far less than 100 rows, so performance may not be an issue.
def __init__(self, ...):
...
# called whenever the main table has its selected row(s) change.
self.itemSelectionChanged.connect(self.update_selection)
def update_selection(self):
for row in range(self.rowCount()):
item = self.item(row, 0)
widg = self.cellWidget(row, 1)
if item.isSelected():
widg.setProperty("row_is_selected", "true")
else:
widg.setProperty("row_is_selected", "false")
# it is apparently necessary to force a recalculation anyway so the
# above property-change is a roundabout way to adjust the style
# compared to just setting or removing it below.
# this forces a recalculation nonetheless.
widg.setStyleSheet(widg.styleSheet())
def add_file(self, row, element):
...
stats.setProperty("row_is_selected", "false")
self.setStyleSheet("""
StatsTable[row_is_selected="true"]
{
background: yellow;
color: purple;
}
""")
The subcontrol and pseudo elements cannot be used for parenthood selectors, and it's also impossible to set the background of a specific item based on the selection if the whole row is selected.
The background of an item view is painted using the Base palette color role, which is normally white and opaque. What you could do is to override it and make it transparent:
def add_file(self, row, element):
# ...
palette = stats.palette()
palette.setColor(palette.Base, QtCore.Qt.transparent)
stats.setPalette(palette)
Unfortunately, this will only fix the background part, and won't change the color of the displayed text. In order to achieve that, you need to know the state of the selection and update the stylesheets depending on the item selection.
You could connect to the selectionChanged of the main table's selectionModel() (or itemSelectionChanged for a QTableWidget), and then style items accordingly:
# somewhere in the __init__
self.TableQSS = '''
QTableWidget
{
background: yellow;
color: purple;
}
'''
self.itemSelectionChanged.connect(self.updateTables)
def updateTables(self):
selected = self.selectedIndexes()
for row in range(self.rowCount()):
table = self.cellWidget(row, self.STATSCOL)
if not isinstance(table, StatsTable):
continue
if self.model().index(row, self.STATSCOL) in selected:
table.setStyleSheet(self.TableQSS)
else:
table.setStyleSheet('')
Consider that stylesheets and palette don't always play well together, and setting palette colors is normally the preferred solution as it's (theoretically) safer with the current style which will use the palette to define other colors, such gradients, shades, etc.
So, keep setting the palette as explained at the beginning, still connect the itemSelectionChanged signal as above, and then:
def updateTables(self):
# get the default colors for the text from the palette of the main
# table (we cannot rely on the child tables as they've been changed)
basePalette = self.palette()
colors = [basePalette.color(cg, basePalette.Text) for cg in range(3)]
selected = self.selectedIndexes()
for row in range(self.rowCount()):
table = self.cellWidget(row, self.STATSCOL)
if not isinstance(table, StatsTable):
continue
palette = table.palette()
if self.model().index(row, self.STATSCOL) in selected:
palette.setColor(palette.Text, QtGui.QColor('purple'))
else:
# restore default colors
for cg, color in enumerate(colors):
palette.setColor(cg, palette.Text, color)
table.setPalette(palette)
Note that using a nested item view is normally not a good idea, as it makes things much more complex (especially with selections and keyboard/mouse interaction) and could potentially create issues in certain situations.
Since it seems that you only need to display data, you should consider implementing your own item delegate (see QStyledItemDelegate) and eventually draw formatted text using a basic HTML table (see this answer).
Alternatively, use a QPlainTextEdit with disabled scroll bars and set in read only mode (in this case, you still need to do what explained above).
I have the following code to place a CheckBox in the first column of a list of items in a QTableWidget.
checkboxWidget = QWidget()
checkBox = QCheckBox(checkboxWidget)
checkBox.clicked.connect(self._check_changed)
#
# If the variable is in the monitored list
# check the checkbox
#
isMonitored = False
if (self._monitored_variables != None):
if (self._monitored_variables[name]):
isMonitored = True
if (isMonitored):
checkBox.setCheckState(Qt.CheckState.Checked)
else:
checkBox.setCheckState(Qt.CheckState.Unchecked)
layoutCheckbox = QHBoxLayout(checkboxWidget)
layoutCheckbox.addWidget(checkBox)
layoutCheckbox.setAlignment(Qt.AlignCenter)
layoutCheckbox.setContentsMargins(0, 0, 0, 0)
self._variables_view.setCellWidget(row,0, checkboxWidget)
I started with the answer to this question:
How should I connect CheckBox clicked signals in Table Widgets in PyQt5?
The difference that I have is that I want the CheckBox centered in the table cell, hence the extra controls.
The click handler looks like this:
def _check_changed(self):
cb = self.sender()
print(cb.parent())
ix = self._variables_view.indexAt(cb.pos())
print(ix.row(), ix.column(), cb.isChecked())
The problem I am facing is that the row/column is not correct.
How can I recover the row/column of the CheckBox that was clicked?
It seems that my previous answer was not explicit but I am going to take this answer to take it in a generic way that will work for all the classes that inherit from QAbstractItemView and any type of widget placed through the setCellWidget or setIndexWidget methods.
General case:
The key to the solution is to obtain the position of the widget that emits the signal with respect to the viewport of the QAbstractItemView, and then use indexAt to get the QModelIndex. This can be obtained by following the following steps:
Map any relative internal position of the widget to global coordinates.
Map global coordinates to local coordinate relative to the viewport.
use indexAt() with local coordinate.
gl = widget.mapToGlobal(QtCore.QPoint())
lp = view.viewport().mapFromGlobal(gp)
ix = view.indexAt(lp)
Specific case:
In this case, just do the following:
def _check_changed(self):
widget = self.sender()
gl = widget.mapToGlobal(QtCore.QPoint())
lp = self._variables_view.viewport().mapFromGlobal(gp)
ix = self._variables_view.indexAt(lp)
It seems the answer to my issues was rather simple. In the "indexAt" method the "cb.pos()" needs to be changed to "cb.parent().pos()".
I'm trying to figure out if it is possible to have different dropdowns/other widgets appear based on the selection in one dropdown. It seems that with 'interact' there are ways to make the values in a second dropdown change based on the values in the first, but I haven't had any luck finding a way to completely change the widgets being displayed based on the first selection. Below is the code that exports my dashboard with ipywidgets - have changed some names in there to make it a bit easier to read without the entire script.
top = widgets.Accordion([
AppLayout(header=header,
center=VBox([Label('First Selection'),
select_1]),
left_sidebar=VBox([Label('Second Selection'),
dropdown_2,
Label('Third Selection'),
dropdown_3,
Label('Fourth Selection'),
dropdown_4,
Label('Fifth Selection'),
dropdown_5,
group_checkbox]),
right_sidebar=VBox([Label('Sixth Selection'),
select_6t,
Label('Seventh Selection'),
select_7]),
footer=VBox([HBox([text_box,button_export]),HBox([dropdown_eight,nine_slider,ten_text,stage_export,run_export])]),
pane_widths=['350px', 1, 1],
pane_heights=['80px', 4, 1],
height='auto',
grid_gap="20px")])
pd.options.display.max_columns = None
pd.options.display.max_rows = None
bottom = tab
app = widgets.VBox([top,bottom])
app
Basically the list populating the HBox in footer contains other widgets which may or may not have relevance based on the selection in dropdown_eight. Rather than have several rows of widgets, I'd like to be able to have a function that determines which additional widgets are needed based on the selection in dropdown_eight. Initially, I was trying to define a function that would return the appropriate list of widgets but could not get it to update in the dashboard.
Any help would be much appreciated!
I find when you have two interlinked widgets, it's easier for me to bind the two together into a class. It's also easier to write a method to access the values you need.
Below is a similar example where you can alter the number of boolean widgets, and then get the chosen values by calling wm.checks. Hopefully you can see how it works and adapt to your situation?
import ipywidgets as ipyw
import string
class WidgetMaker(ipyw.VBox):
def __init__(self):
self.widget_count = ipyw.IntText(description='Number:')
self.bool_widget_holder = ipyw.HBox()
children = [
self.widget_count,
self.bool_widget_holder,
]
self.widget_count.observe(self._add_bool_widgets, names=['value'])
super().__init__(children=children)
def _add_bool_widgets(self, widg):
num_bools = widg['new']
new_widgets = []
for _, label in zip(range(num_bools), string.ascii_lowercase):
new_widget = ipyw.Checkbox(description=label)
new_widgets.append(new_widget)
self.bool_widget_holder.children = tuple(new_widgets)
#property
def checks(self):
return {
w.description: w.value
for w in self.bool_widget_holder.children
}