我正在尝试在 GTK4 应用程序中创建一个下拉菜单,但我一直在思考如何使下拉菜单工作。
我发现并一直在使用这个 python GTK4 Dropdown 示例,它最初可以工作,但是当列表存储使用的
dict
更新时,下拉列表不会改变。
由于我不确定自己是否正确地解决了问题,因此我将呈现实际问题的简化版本。
@dataclass
class Coffee:
"""
Represents a coffee entry in the db
"""
coffee_id:int
name:str
description:str
# date/time added (as unix time)
date_added:datetime
我的应用程序窗口使用
self.coffees:list[Coffees]
存储“咖啡列表”,并使用 self.curr_coffee:Coffee
跟踪“当前活动”咖啡。该列表由应用程序的其他部分更新。
我的目标是由此列表中的
Gtk.Dropdown
中的 name
组成的 Coffee
,并且当用户进行选择时 self.curr_coffee
会更新。
我正在使用 简化的下拉示例,并进行了少量修改。
然后我创建一个
dict
, self.coffees_dict:dict
传递到下拉列表。该字典会在 self.coffees
的每次更新时更新。 (是的,我知道这不太好;我只是想让它发挥作用)
for c in self.coffees:
self.coffees_dict[c.coffee_id] = c.name
然后使用示例类创建一个下拉列表,如下所示:
self.coffee_setting_dropdown = DropDown(data=self.appstate.localDB.coffees_dict,
itemSelectNotifyMethod=self._on_coffee_changed_notify)
self.dropDownBox = self.coffee_setting_dropdown.getDropDownElementWithBoxAndLabel("Coffee:")
但是,尽管更新了
coffee_dict
,数据库的新添加内容并未反映在下拉列表中。必须有一种更简单的方法来做到这一点 - 如何从咖啡名称创建 GTK4 下拉列表并在添加新名称时更新它?
下面是基本情况的一个工作单文件示例,改编自上面链接的示例。
import gi
from dataclasses import dataclass
gi.require_version("Adw", "1")
gi.require_version("Gtk", "4.0")
from gi.repository import Adw, Gio, GObject, Gtk
@dataclass
class Coffee:
"""
Represents a coffee entry in the db
"""
coffee_id: int
name: str
description: str
# used to generate example list[Coffee]
EXAMPLE_COFFEES_LIST = [
"Espresso",
"Americano",
"Latte",
"Cappuccino",
"Mocha",
"Macchiato",
"Flat White",
"Affogato",
"Cold Brew",
"Nitro Coffee",
"Turkish Coffee",
"Irish Coffee",
"Ristretto",
"Doppio",
"Café au Lait",
"Vienna Coffee",
"Café con Leche",
"Iced Coffee",
"Breve",
"Coffee Milk",
]
# DropDown classes are from
# https://github.com/ksaadDE/GTK4PythonExamples/blob/main/DropDownSimplified.md
class DataDropDownChild(GObject.Object):
__gtype_name__ = "DataDropDownChild"
def __init__(self, key, value):
super().__init__()
self.k = key
self.v = value
@GObject.Property
def key(self):
return self.k
@GObject.Property
def value(self):
return self.v
def getKey(
self,
):
return self.k
def getValue(
self,
):
return self.v
class DropDown:
def __init__(
self,
data=[],
factoryBindSetupMethod=None,
factoryBindMethod=None,
itemSelectNotifyMethod=None,
):
self.box = None
self.data = data
self.model = Gio.ListStore(item_type=DataDropDownChild)
for k in data.keys():
self.model.append(DataDropDownChild(key=k, value=data[k]))
# Set up the factory
self.factory = Gtk.SignalListItemFactory()
if factoryBindSetupMethod is None:
self.factory.connect("setup", self._on_factory_setup)
else:
self.factory.connect("setup", factoryBindSetupMethod)
if factoryBindMethod is None:
self.factory.connect("bind", self._on_factory_bind)
else:
self.factory.connect("bind", factoryBindMethod)
self.dd = Gtk.DropDown(model=self.model, factory=self.factory, hexpand=True)
if itemSelectNotifyMethod is None:
self.dd.connect("notify::selected-item", self._on_selected_item_notify)
else:
self.dd.connect("notify::selected-item", itemSelectNotifyMethod)
def getDropDownElement(
self,
):
return self.dd
def getDropDownElementWithBox(self, labelText="Select Object:"):
self.box = Gtk.Box(spacing=12, hexpand=True, vexpand=True)
self.box.props.margin_start = 12
self.box.props.margin_end = 12
# self.box.props.margin_top = 6
# self.box.props.margin_bottom = 6
# self.box.append(Gtk.Label(label=labelText))
self.box.append(self.getDropDownElement())
self.box.set_vexpand(False)
return self.box
# Set up the child of the list item; this can be an arbitrarily
# complex widget but we use a simple label now
def _on_factory_setup(self, factory, list_item):
label = Gtk.Label()
list_item.set_child(label)
# Bind the item in the model to the row widget; again, since
# the object in the model can contain multiple properties, and
# the list item can contain any arbitrarily complex widget, we
# can have very complex rows instead of the simple cell renderer
# layouts in GtkComboBox
def _on_factory_bind(self, factory, list_item):
label = list_item.get_child()
item = list_item.get_item()
label.set_text(str(item.v))
# The notify signal is fired when the selection changes
def _on_selected_item_notify(self, dropdown, _):
item = dropdown.get_selected_item()
print(item.getKey(), item.getValue())
def update_liststore(self, data):
# naive (and rather stupid) way of updating items in dropdown. will not scale
self.data = data
self.model = Gio.ListStore(item_type=DataDropDownChild)
for k in data.keys():
self.model.append(DataDropDownChild(key=k, value=data[k]))
class ExampleWindow(Gtk.ApplicationWindow):
def __init__(self, app):
super().__init__(application=app, title="DropDown")
# generate example data
self.coffees: list[Coffee] = []
for idx, item in enumerate(EXAMPLE_COFFEES_LIST):
self.coffees.append(Coffee(idx, item, ""))
self.coffees_dict: dict = {}
for c in self.coffees:
self.coffees_dict[c.coffee_id] = c.name
# not used in this example, here for reference
self.curr_coffee:Coffee = None
self.dd = DropDown(
data=self.coffees_dict, itemSelectNotifyMethod=self.onItemChangeNotify
)
self.dropDownBox = self.dd.getDropDownElementWithBox(labelText="Coffees:")
self.set_child(self.dropDownBox)
def onItemChangeNotify(self, dropdown, _):
item = dropdown.get_selected_item()
print("22:", item.getKey(), item.getValue())
class ExampleApp(Adw.Application):
def __init__(self):
super().__init__()
self.window = None
def do_activate(self):
if self.window is None:
self.window = ExampleWindow(self)
self.window.present()
app = ExampleApp()
app.run([])
所以你有一个下拉框来设置咖啡,并且你希望它在新咖啡添加到字典中时更新?