ComboBox.new_with_model_and_entry 相当于什么空地?
what is the glade equivalent of ComboBox.new_with_model_and_entry?
我想移植 Ghini,一个 Python 桌面程序,从 GTK2 到 GTK3,即从静态 import gtk
到动态 from gi import Gtk
。
Ghini 基于 glade 文件,我在处理 ComboBox
元素时遇到了问题 — 关联 Entry
。我已经搜索了有关 Gtk2 和 Gtk3 之间差异的文档和教程,并且有很多,但是我发现的 none 详细描述了这种特殊情况。移植脚本处理 python 源,我没有找到任何寻址空地文件的方法。
在将问题减少到最小和明确的过程中,我选择了example 13 in this Gtk tutorial。
所以我把原来的程序精简成这样:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class ComboBoxWindow:
def on_name_combo_changed(self, combo):
tree_iter = combo.get_active_iter()
if tree_iter is not None:
model = combo.get_model()
row_id, name = model[tree_iter][:2]
print("Selected: ID=%d, name=%s" % (row_id, name))
else:
entry = combo.get_child()
print("Entered: %s" % entry.get_text())
def on_country_combo_changed(self, combo):
tree_iter = combo.get_active_iter()
if tree_iter is not None:
model = combo.get_model()
country = model[tree_iter][0]
print("Selected: country=%s" % country)
def on_currency_combo_changed(self, combo):
text = combo.get_active_text()
if text is not None:
print("Selected: currency=%s" % text)
def __init__(self, builder):
builder.add_from_file("/tmp/ex13.glade")
builder.connect_signals(self)
self.window = builder.get_object("window1")
self.window.connect("destroy", Gtk.main_quit)
def show_all(self):
self.window.show_all()
builder = Gtk.Builder()
win = ComboBoxWindow(builder)
win.show_all()
Gtk.main()
我使用 Glade 将整个接口定义放在这个 ex13.glade
文件中:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="country_store">
<columns>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row><col id="0" translatable="yes">Austria</col></row>
<row><col id="0" translatable="yes">Brazil</col></row>
<row><col id="0" translatable="yes">Belgium</col></row>
<row><col id="0" translatable="yes">France</col></row>
<row><col id="0" translatable="yes">Germany</col></row>
<row><col id="0" translatable="yes">Switzerland</col></row>
<row><col id="0" translatable="yes">United Kingdom</col></row>
<row><col id="0" translatable="yes">United States</col></row>
<row><col id="0" translatable="yes">Uruguay</col></row>
</data>
</object>
<object class="GtkListStore" id="name_store">
<columns>
<!-- column-name gint1 -->
<column type="gint"/>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0">1</col>
<col id="1" translatable="yes">Billy Bobo</col>
</row>
<row>
<col id="0">2</col>
<col id="1" translatable="yes">Joey Jojo</col>
</row>
<row>
<col id="0">3</col>
<col id="1" translatable="yes">Rob McRoberts</col>
</row>
<row>
<col id="0">11</col>
<col id="1" translatable="yes">Billy Bob Junior</col>
</row>
<row>
<col id="0">12</col>
<col id="1" translatable="yes">Sue Bob</col>
</row>
<row>
<col id="0">31</col>
<col id="1" translatable="yes">Xavier McRoberts</col>
</row>
</data>
</object>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Combobox Example</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkComboBox" id="name_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">name_store</property>
<property name="has_entry">True</property>
<property name="entry_text_column">1</property>
<signal name="changed" handler="on_name_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="name_renderer"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="placeholder_text" translatable="yes">type, or choose</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="country_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">country_store</property>
<property name="id_column">0</property>
<signal name="changed" handler="on_country_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="country_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="currencies_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item translatable="yes">Euro</item>
<item translatable="yes">US Dollars</item>
<item translatable="yes">British Pound</item>
<item translatable="yes">Japanese Yen</item>
<item translatable="yes">Russian Ruble</item>
<item translatable="yes">Mexican peso</item>
<item translatable="yes">Swiss franc</item>
</items>
<signal name="changed" handler="on_currency_combo_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</interface>
然而,这并不等同于原始示例,我看不出我遗漏了什么。特别是带有 Entry 的 ComboBox 正在这样做:
- 下拉列表显示两次内容
非常欢迎指正和批评。
一小时后:
当我设置 <property name="has_entry">True</property>
时,下拉列表显示两列,这没有链接到具有两列的列表存储:我已经在 country_combo
(关联到单列列表存储),我得到一个空列和一个包含列表存储中的值的列。
稍后编辑:似乎双重表示是由 GtkCellRendererText
引起的,我可以安全地删除它。
我找到了答案,我在这里分享:
首先,我通过通用回调进一步减少了代码。
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class ComboBoxWindow:
def on_combo_changed(self, combo):
tree_iter = combo.get_active_iter()
if tree_iter is not None:
model = combo.get_model()
print("row: [%s]" % ', '.join("%s(%s)" % (type(i), str(i)) for i in model[tree_iter]))
else:
entry = combo.get_child()
print("Entered: %s" % entry.get_text())
def __init__(self, builder):
import os.path
path, name = os.path.split(__file__)
builder.add_from_file(os.path.join(path, "ex13.glade"))
builder.connect_signals(self)
self.window = builder.get_object("window1")
self.window.connect("destroy", Gtk.main_quit)
def show_all(self):
self.window.show_all()
builder = Gtk.Builder()
win = ComboBoxWindow(builder)
win.show_all()
Gtk.main()
然后是接口文件,我也将它剥离为一个 ComboBox
,关联到一个多列 ListStore
模型,它有一个内部(id-less)Entry
。
下拉有两种不同的方式,一种是当您输入 Entry
时,这种情况发生在 Entry
的关联 GtkEntryCompletion
中,后者又具有它自己的 GtkCellRendererText
.
另一种是常规下拉菜单,它至少显示一列,也可以选择显示其他列。在这个例子中,我使用了一个额外的渲染器来展示它是如何工作的。我无法关闭第一个默认列。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="epithet_store">
<columns>
<column type="gint"/> <!-- column-name id -->
<column type="gchararray"/> <!-- column-name epithet -->
<column type="gchararray"/> <!-- column-name family -->
<column type="gchararray"/> <!-- column-name phonetic -->
</columns>
<data>
<row><col id="0">0</col><col id="1">Cocos</col><col id="2">Arecaceae</col><col id="3">kukus</col></row>
<row><col id="0">1</col><col id="1">Cheilopsis</col><col id="2">Acanthaceae</col><col id="3">kilupsis</col></row>
<row><col id="0">2</col><col id="1">Haplanthoides</col><col id="2">Acanthaceae</col><col id="3">aplantidis</col></row>
<row><col id="0">3</col><col id="1">Haplanthus</col><col id="2">Acanthaceae</col><col id="3">aplantus</col></row>
<row><col id="0">4</col><col id="1">Indoneesiella</col><col id="2">Acanthaceae</col><col id="3">indunisila</col></row>
<row><col id="0">5</col><col id="1">Ancalanthus</col><col id="2">Acanthaceae</col><col id="3">ankalantus</col></row>
</data>
</object>
<object class="GtkEntryCompletion" id="epithet_entrycompletion">
<property name="model">epithet_store</property>
<property name="text_column">1</property>
<property name="inline_selection">True</property>
<property name="popup_completion">True</property>
<child>
<object class="GtkCellRendererText" id="epithet_completion_renderer"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Combobox Example</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkComboBox" id="epithet_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">epithet_store</property>
<property name="has_entry">True</property>
<property name="entry_text_column">1</property>
<property name="id_column">0</property>
<signal name="changed" handler="on_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="epithet_renderer"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="completion">epithet_entrycompletion</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
如果我对将信息保存在 ListStore 模型中不感兴趣,并且只需要处理文本和有一个条目,那么以下内容也可以工作,在某种意义上让我可以键入并区分大小写 "it was chosen"/"it was typed":
<object class="GtkComboBoxText">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_entry">True</property>
<items>
<item id="1" translatable="yes">Aitano</item>
<item id="2" translatable="yes">Aniello</item>
<item id="3" translatable="yes">Antonio</item>
<item id="4" translatable="yes">Fiorentino</item>
<item id="5" translatable="yes">Peppino</item>
<item id="6" translatable="yes">Strato</item>
</items>
<signal name="changed" handler="on_combo_changed" swapped="no"/>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="placeholder_text" translatable="yes">type, or choose</property>
</object>
</child>
</object>
关联回调与上述完整方案相同
到目前为止,我了解到您在做什么:为了测试从 gtk2 迁移到 gtk3 的新应用程序,您按原样超越了 http://python-gtk-3-tutorial.readthedocs.io/en/latest/combobox.html and try to make it differently (not with a script only but with a reduced script and with a separate GLADE file). Have you tested http://python-gtk-3-tutorial.readthedocs.io/en/latest/combobox.html? (就个人而言,我不会 100% 依赖 WWW 中的代码;但在制作修改后的脚本之前对其进行测试)。到目前为止,我个人仅将组合用作被动 window,从未连接过信号。
我想移植 Ghini,一个 Python 桌面程序,从 GTK2 到 GTK3,即从静态 import gtk
到动态 from gi import Gtk
。
Ghini 基于 glade 文件,我在处理 ComboBox
元素时遇到了问题 — 关联 Entry
。我已经搜索了有关 Gtk2 和 Gtk3 之间差异的文档和教程,并且有很多,但是我发现的 none 详细描述了这种特殊情况。移植脚本处理 python 源,我没有找到任何寻址空地文件的方法。
在将问题减少到最小和明确的过程中,我选择了example 13 in this Gtk tutorial。
所以我把原来的程序精简成这样:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class ComboBoxWindow:
def on_name_combo_changed(self, combo):
tree_iter = combo.get_active_iter()
if tree_iter is not None:
model = combo.get_model()
row_id, name = model[tree_iter][:2]
print("Selected: ID=%d, name=%s" % (row_id, name))
else:
entry = combo.get_child()
print("Entered: %s" % entry.get_text())
def on_country_combo_changed(self, combo):
tree_iter = combo.get_active_iter()
if tree_iter is not None:
model = combo.get_model()
country = model[tree_iter][0]
print("Selected: country=%s" % country)
def on_currency_combo_changed(self, combo):
text = combo.get_active_text()
if text is not None:
print("Selected: currency=%s" % text)
def __init__(self, builder):
builder.add_from_file("/tmp/ex13.glade")
builder.connect_signals(self)
self.window = builder.get_object("window1")
self.window.connect("destroy", Gtk.main_quit)
def show_all(self):
self.window.show_all()
builder = Gtk.Builder()
win = ComboBoxWindow(builder)
win.show_all()
Gtk.main()
我使用 Glade 将整个接口定义放在这个 ex13.glade
文件中:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="country_store">
<columns>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row><col id="0" translatable="yes">Austria</col></row>
<row><col id="0" translatable="yes">Brazil</col></row>
<row><col id="0" translatable="yes">Belgium</col></row>
<row><col id="0" translatable="yes">France</col></row>
<row><col id="0" translatable="yes">Germany</col></row>
<row><col id="0" translatable="yes">Switzerland</col></row>
<row><col id="0" translatable="yes">United Kingdom</col></row>
<row><col id="0" translatable="yes">United States</col></row>
<row><col id="0" translatable="yes">Uruguay</col></row>
</data>
</object>
<object class="GtkListStore" id="name_store">
<columns>
<!-- column-name gint1 -->
<column type="gint"/>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0">1</col>
<col id="1" translatable="yes">Billy Bobo</col>
</row>
<row>
<col id="0">2</col>
<col id="1" translatable="yes">Joey Jojo</col>
</row>
<row>
<col id="0">3</col>
<col id="1" translatable="yes">Rob McRoberts</col>
</row>
<row>
<col id="0">11</col>
<col id="1" translatable="yes">Billy Bob Junior</col>
</row>
<row>
<col id="0">12</col>
<col id="1" translatable="yes">Sue Bob</col>
</row>
<row>
<col id="0">31</col>
<col id="1" translatable="yes">Xavier McRoberts</col>
</row>
</data>
</object>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Combobox Example</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkComboBox" id="name_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">name_store</property>
<property name="has_entry">True</property>
<property name="entry_text_column">1</property>
<signal name="changed" handler="on_name_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="name_renderer"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="placeholder_text" translatable="yes">type, or choose</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="country_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">country_store</property>
<property name="id_column">0</property>
<signal name="changed" handler="on_country_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="country_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="currencies_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item translatable="yes">Euro</item>
<item translatable="yes">US Dollars</item>
<item translatable="yes">British Pound</item>
<item translatable="yes">Japanese Yen</item>
<item translatable="yes">Russian Ruble</item>
<item translatable="yes">Mexican peso</item>
<item translatable="yes">Swiss franc</item>
</items>
<signal name="changed" handler="on_currency_combo_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</interface>
然而,这并不等同于原始示例,我看不出我遗漏了什么。特别是带有 Entry 的 ComboBox 正在这样做:
- 下拉列表显示两次内容
非常欢迎指正和批评。
一小时后:
当我设置 <property name="has_entry">True</property>
时,下拉列表显示两列,这没有链接到具有两列的列表存储:我已经在 country_combo
(关联到单列列表存储),我得到一个空列和一个包含列表存储中的值的列。
稍后编辑:似乎双重表示是由 GtkCellRendererText
引起的,我可以安全地删除它。
我找到了答案,我在这里分享:
首先,我通过通用回调进一步减少了代码。
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class ComboBoxWindow:
def on_combo_changed(self, combo):
tree_iter = combo.get_active_iter()
if tree_iter is not None:
model = combo.get_model()
print("row: [%s]" % ', '.join("%s(%s)" % (type(i), str(i)) for i in model[tree_iter]))
else:
entry = combo.get_child()
print("Entered: %s" % entry.get_text())
def __init__(self, builder):
import os.path
path, name = os.path.split(__file__)
builder.add_from_file(os.path.join(path, "ex13.glade"))
builder.connect_signals(self)
self.window = builder.get_object("window1")
self.window.connect("destroy", Gtk.main_quit)
def show_all(self):
self.window.show_all()
builder = Gtk.Builder()
win = ComboBoxWindow(builder)
win.show_all()
Gtk.main()
然后是接口文件,我也将它剥离为一个 ComboBox
,关联到一个多列 ListStore
模型,它有一个内部(id-less)Entry
。
下拉有两种不同的方式,一种是当您输入 Entry
时,这种情况发生在 Entry
的关联 GtkEntryCompletion
中,后者又具有它自己的 GtkCellRendererText
.
另一种是常规下拉菜单,它至少显示一列,也可以选择显示其他列。在这个例子中,我使用了一个额外的渲染器来展示它是如何工作的。我无法关闭第一个默认列。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="epithet_store">
<columns>
<column type="gint"/> <!-- column-name id -->
<column type="gchararray"/> <!-- column-name epithet -->
<column type="gchararray"/> <!-- column-name family -->
<column type="gchararray"/> <!-- column-name phonetic -->
</columns>
<data>
<row><col id="0">0</col><col id="1">Cocos</col><col id="2">Arecaceae</col><col id="3">kukus</col></row>
<row><col id="0">1</col><col id="1">Cheilopsis</col><col id="2">Acanthaceae</col><col id="3">kilupsis</col></row>
<row><col id="0">2</col><col id="1">Haplanthoides</col><col id="2">Acanthaceae</col><col id="3">aplantidis</col></row>
<row><col id="0">3</col><col id="1">Haplanthus</col><col id="2">Acanthaceae</col><col id="3">aplantus</col></row>
<row><col id="0">4</col><col id="1">Indoneesiella</col><col id="2">Acanthaceae</col><col id="3">indunisila</col></row>
<row><col id="0">5</col><col id="1">Ancalanthus</col><col id="2">Acanthaceae</col><col id="3">ankalantus</col></row>
</data>
</object>
<object class="GtkEntryCompletion" id="epithet_entrycompletion">
<property name="model">epithet_store</property>
<property name="text_column">1</property>
<property name="inline_selection">True</property>
<property name="popup_completion">True</property>
<child>
<object class="GtkCellRendererText" id="epithet_completion_renderer"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Combobox Example</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkComboBox" id="epithet_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">epithet_store</property>
<property name="has_entry">True</property>
<property name="entry_text_column">1</property>
<property name="id_column">0</property>
<signal name="changed" handler="on_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="epithet_renderer"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="completion">epithet_entrycompletion</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
如果我对将信息保存在 ListStore 模型中不感兴趣,并且只需要处理文本和有一个条目,那么以下内容也可以工作,在某种意义上让我可以键入并区分大小写 "it was chosen"/"it was typed":
<object class="GtkComboBoxText">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_entry">True</property>
<items>
<item id="1" translatable="yes">Aitano</item>
<item id="2" translatable="yes">Aniello</item>
<item id="3" translatable="yes">Antonio</item>
<item id="4" translatable="yes">Fiorentino</item>
<item id="5" translatable="yes">Peppino</item>
<item id="6" translatable="yes">Strato</item>
</items>
<signal name="changed" handler="on_combo_changed" swapped="no"/>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="placeholder_text" translatable="yes">type, or choose</property>
</object>
</child>
</object>
关联回调与上述完整方案相同
到目前为止,我了解到您在做什么:为了测试从 gtk2 迁移到 gtk3 的新应用程序,您按原样超越了 http://python-gtk-3-tutorial.readthedocs.io/en/latest/combobox.html and try to make it differently (not with a script only but with a reduced script and with a separate GLADE file). Have you tested http://python-gtk-3-tutorial.readthedocs.io/en/latest/combobox.html? (就个人而言,我不会 100% 依赖 WWW 中的代码;但在制作修改后的脚本之前对其进行测试)。到目前为止,我个人仅将组合用作被动 window,从未连接过信号。