GTK/Python: 如何获取按键事件来编辑和导航 TreeView 单元格?
GTK/Python: How to get key-press-event to edit and navigate a TreeView cell?
我已连接到 "key-press-event" 以浏览 Gtk.TreeView。我成功地让 Tab 导航到右边(跨行)。我在使用 Return 向下导航时遇到问题。选择一个单元格后,我可以编辑其内容,但我必须按一次 Return 来提交值,然后再次导航到下面的单元格。我希望行为像 Tab 一样,我在其中按 Return 一次并提交更改并且所选单元格向下移动一。我正在寻找像电子表格那样的行为。
我猜这里与键绑定有冲突,即第一个 Return 按下事件提交更改,第二个 Return 按下事件向下导航。此外,我尝试连接 Shift_L 键(而不是 Return)以向下导航,同时它使用一个键导航按下它也无法将更改提交到单元格。
如果需要,我会启动一个 MWE,但我认为外面的人可能知道这里的问题,并且可以为我指明正确的方向或教育我。
编辑: 好吧,我花时间把所有东西都剥离成一个 MWE,任何人都可以提供帮助 tested/eyeballed。相关代码部分是名为 onTreeNavigateKeyPress 的回调函数。其中,麻烦的条件是 elif keyname == 'Return'。如果你在你的机器上 运行 这个你会看到你可以更改单元格值和 Tab 到右边和 Tab两者都向右导航并将更改的值提交给单元格。使用 Return 键执行相同操作将提交更改,但您需要再次按 Return 以导航向下。说我迂腐,但我讨厌那样。正如您从代码中看到的那样,我尝试直接使用新位置调用 Gtk.TreeView.set_cursor 方法以及从 [=12= 调用它].
#!/usr/bin/env python3`
from gi.repository import Gtk, Gdk, GLib
class linFitApp(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='Testing Keypress Events on Treeview')
self.set_position(Gtk.WindowPosition.CENTER)
self.set_default_size(400, 300)
self.set_border_width(5)
self.mainBox = Gtk.Box()
self.scrollTableWindow = Gtk.ScrolledWindow()
self.scrollTableWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.mainBox.pack_start(self.scrollTableWindow, False, False, 0)
self.add(self.mainBox)
############################################################################
#Now to set up the data table
self.dataTableListStore = Gtk.ListStore(float, float, float, float)
self.dataTableTreeView = Gtk.TreeView(model=self.dataTableListStore)
self.dataTableTreeView.props.activate_on_single_click = True
self.dataTableTreeView.connect("key-press-event", self.onTreeNavigateKeyPress)
#set up the x column
self.xColumnTextRenderer = Gtk.CellRendererText()
self.xColumnTextRenderer.set_property("editable", True)
self.xColumnTextRenderer.connect("edited", self.onXChanged)
self.xColumnTreeView = Gtk.TreeViewColumn("x", self.xColumnTextRenderer, text=0)
#set up the y column
self.yColumnTextRenderer = Gtk.CellRendererText()
self.yColumnTextRenderer.set_property("editable", True)
self.yColumnTextRenderer.connect("edited",self.onYChanged)
self.yColumnTreeView = Gtk.TreeViewColumn("y", self.yColumnTextRenderer, text=1)
#set up the dx column
self.dxColumnTextRenderer = Gtk.CellRendererText()
self.dxColumnTextRenderer.set_property("editable", True)
self.dxColumnTextRenderer.connect("edited",self.onDxChanged)
self.dxColumnTreeView = Gtk.TreeViewColumn("dx", self.dxColumnTextRenderer, text=2)
#set up the dy column
self.dyColumnTextRenderer = Gtk.CellRendererText()
self.dyColumnTextRenderer.set_property("editable", True)
self.dyColumnTextRenderer.connect("edited",self.onDyChanged)
self.dyColumnTreeView = Gtk.TreeViewColumn("dy", self.dyColumnTextRenderer, text=3)
#pack treeview into the scrolled window
self.scrollTableWindow.add(self.dataTableTreeView)
#add treeview columns to treeview
self.dataTableTreeView.append_column(self.xColumnTreeView)
self.dataTableTreeView.append_column(self.yColumnTreeView)
self.dataTableTreeView.append_column(self.dxColumnTreeView)
self.dataTableTreeView.append_column(self.dyColumnTreeView)
#fill in treeview with some sample data
self.dataTableListStore.append([0, 4, 0, 0])
self.dataTableListStore.append([5, 8.2, 0, 0])
self.dataTableListStore.append([10, 11.7, 0, 0])
self.dataTableListStore.append([15, 16.5, 0, 0])
self.dataTableListStore.append([20, 19, 0, 0])
self.dataTableListStore.append([25, 24.5, 0, 0])
self.dataTableListStore.append([30, 26.2, 0, 0])
#define the callbacks for cell editing
def onXChanged(self, widget, path, number):
self.dataTableListStore[path][0]=float(number.replace(',', '.'))
def onYChanged(self, widget, path, number):
self.dataTableListStore[path][1]=float(number.replace(',', '.'))
def onDxChanged(self, widget, path, number):
self.dataTableListStore[path][2]=float(number.replace(',', '.'))
def onDyChanged(self, widget, path, number):
self.dataTableListStore[path][3]=float(number.replace(',', '.'))
#define the callback for keypress events
def onTreeNavigateKeyPress(self, treeview, event):
keyname = Gdk.keyval_name(event.keyval)
path, col = treeview.get_cursor()
columns = [c for c in treeview.get_columns()]
colnum = columns.index(col)
if keyname == 'Tab':
if colnum + 1 < len(columns):
next_column = columns[colnum + 1]
else:
next_column = columns[0]
GLib.timeout_add(50,
treeview.set_cursor,
path, next_column, True)
elif keyname == 'Return':
model = treeview.get_model()
#Check if currently in last row of Treeview
if path.get_indices()[0] + 1 == len(model):
path = treeview.get_path_at_pos(0,0)[0]
#treeview.set_cursor(path, columns[colnum], True)
GLib.timeout_add(50,
treeview.set_cursor,
path, columns[colnum], True)
else:
path.next()
#treeview.set_cursor(path, columns[colnum], True)
GLib.timeout_add(50,
treeview.set_cursor,
path, columns[colnum], True)
else:
pass
#create main application window and start Gtk loop
mainWindow = linFitApp()
mainWindow.connect("delete-event", Gtk.main_quit)
mainWindow.show_all()
Gtk.main()
答案(可能只是一个功能性的 hack)如下:
在 Gtk.Treeview 单元格处于编辑模式时按 Return 不会释放按键事件信号。我没有将回调函数连接到 key-press-event 信号,而是将它连接到 key-release-event 信号 is 在编辑单元格时按下 Return 后发出。所以按下 Return 键将激活它提交新值的任何信号(我仍然不知道那是哪个信号)并且释放 Return 键将激活导航到下一个单元格。瞧,我们有两个所需的操作,只需按一下 Return 键即可。
简答:
改变这个
self.dataTableTreeView.connect("key-press-event", self.onTreeNavigateKeyPress)
至此
self.dataTableTreeView.connect("key-release-event", self.onTreeNavigateKeyPress)
我已连接到 "key-press-event" 以浏览 Gtk.TreeView。我成功地让 Tab 导航到右边(跨行)。我在使用 Return 向下导航时遇到问题。选择一个单元格后,我可以编辑其内容,但我必须按一次 Return 来提交值,然后再次导航到下面的单元格。我希望行为像 Tab 一样,我在其中按 Return 一次并提交更改并且所选单元格向下移动一。我正在寻找像电子表格那样的行为。
我猜这里与键绑定有冲突,即第一个 Return 按下事件提交更改,第二个 Return 按下事件向下导航。此外,我尝试连接 Shift_L 键(而不是 Return)以向下导航,同时它使用一个键导航按下它也无法将更改提交到单元格。
如果需要,我会启动一个 MWE,但我认为外面的人可能知道这里的问题,并且可以为我指明正确的方向或教育我。
编辑: 好吧,我花时间把所有东西都剥离成一个 MWE,任何人都可以提供帮助 tested/eyeballed。相关代码部分是名为 onTreeNavigateKeyPress 的回调函数。其中,麻烦的条件是 elif keyname == 'Return'。如果你在你的机器上 运行 这个你会看到你可以更改单元格值和 Tab 到右边和 Tab两者都向右导航并将更改的值提交给单元格。使用 Return 键执行相同操作将提交更改,但您需要再次按 Return 以导航向下。说我迂腐,但我讨厌那样。正如您从代码中看到的那样,我尝试直接使用新位置调用 Gtk.TreeView.set_cursor 方法以及从 [=12= 调用它].
#!/usr/bin/env python3`
from gi.repository import Gtk, Gdk, GLib
class linFitApp(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='Testing Keypress Events on Treeview')
self.set_position(Gtk.WindowPosition.CENTER)
self.set_default_size(400, 300)
self.set_border_width(5)
self.mainBox = Gtk.Box()
self.scrollTableWindow = Gtk.ScrolledWindow()
self.scrollTableWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.mainBox.pack_start(self.scrollTableWindow, False, False, 0)
self.add(self.mainBox)
############################################################################
#Now to set up the data table
self.dataTableListStore = Gtk.ListStore(float, float, float, float)
self.dataTableTreeView = Gtk.TreeView(model=self.dataTableListStore)
self.dataTableTreeView.props.activate_on_single_click = True
self.dataTableTreeView.connect("key-press-event", self.onTreeNavigateKeyPress)
#set up the x column
self.xColumnTextRenderer = Gtk.CellRendererText()
self.xColumnTextRenderer.set_property("editable", True)
self.xColumnTextRenderer.connect("edited", self.onXChanged)
self.xColumnTreeView = Gtk.TreeViewColumn("x", self.xColumnTextRenderer, text=0)
#set up the y column
self.yColumnTextRenderer = Gtk.CellRendererText()
self.yColumnTextRenderer.set_property("editable", True)
self.yColumnTextRenderer.connect("edited",self.onYChanged)
self.yColumnTreeView = Gtk.TreeViewColumn("y", self.yColumnTextRenderer, text=1)
#set up the dx column
self.dxColumnTextRenderer = Gtk.CellRendererText()
self.dxColumnTextRenderer.set_property("editable", True)
self.dxColumnTextRenderer.connect("edited",self.onDxChanged)
self.dxColumnTreeView = Gtk.TreeViewColumn("dx", self.dxColumnTextRenderer, text=2)
#set up the dy column
self.dyColumnTextRenderer = Gtk.CellRendererText()
self.dyColumnTextRenderer.set_property("editable", True)
self.dyColumnTextRenderer.connect("edited",self.onDyChanged)
self.dyColumnTreeView = Gtk.TreeViewColumn("dy", self.dyColumnTextRenderer, text=3)
#pack treeview into the scrolled window
self.scrollTableWindow.add(self.dataTableTreeView)
#add treeview columns to treeview
self.dataTableTreeView.append_column(self.xColumnTreeView)
self.dataTableTreeView.append_column(self.yColumnTreeView)
self.dataTableTreeView.append_column(self.dxColumnTreeView)
self.dataTableTreeView.append_column(self.dyColumnTreeView)
#fill in treeview with some sample data
self.dataTableListStore.append([0, 4, 0, 0])
self.dataTableListStore.append([5, 8.2, 0, 0])
self.dataTableListStore.append([10, 11.7, 0, 0])
self.dataTableListStore.append([15, 16.5, 0, 0])
self.dataTableListStore.append([20, 19, 0, 0])
self.dataTableListStore.append([25, 24.5, 0, 0])
self.dataTableListStore.append([30, 26.2, 0, 0])
#define the callbacks for cell editing
def onXChanged(self, widget, path, number):
self.dataTableListStore[path][0]=float(number.replace(',', '.'))
def onYChanged(self, widget, path, number):
self.dataTableListStore[path][1]=float(number.replace(',', '.'))
def onDxChanged(self, widget, path, number):
self.dataTableListStore[path][2]=float(number.replace(',', '.'))
def onDyChanged(self, widget, path, number):
self.dataTableListStore[path][3]=float(number.replace(',', '.'))
#define the callback for keypress events
def onTreeNavigateKeyPress(self, treeview, event):
keyname = Gdk.keyval_name(event.keyval)
path, col = treeview.get_cursor()
columns = [c for c in treeview.get_columns()]
colnum = columns.index(col)
if keyname == 'Tab':
if colnum + 1 < len(columns):
next_column = columns[colnum + 1]
else:
next_column = columns[0]
GLib.timeout_add(50,
treeview.set_cursor,
path, next_column, True)
elif keyname == 'Return':
model = treeview.get_model()
#Check if currently in last row of Treeview
if path.get_indices()[0] + 1 == len(model):
path = treeview.get_path_at_pos(0,0)[0]
#treeview.set_cursor(path, columns[colnum], True)
GLib.timeout_add(50,
treeview.set_cursor,
path, columns[colnum], True)
else:
path.next()
#treeview.set_cursor(path, columns[colnum], True)
GLib.timeout_add(50,
treeview.set_cursor,
path, columns[colnum], True)
else:
pass
#create main application window and start Gtk loop
mainWindow = linFitApp()
mainWindow.connect("delete-event", Gtk.main_quit)
mainWindow.show_all()
Gtk.main()
答案(可能只是一个功能性的 hack)如下:
在 Gtk.Treeview 单元格处于编辑模式时按 Return 不会释放按键事件信号。我没有将回调函数连接到 key-press-event 信号,而是将它连接到 key-release-event 信号 is 在编辑单元格时按下 Return 后发出。所以按下 Return 键将激活它提交新值的任何信号(我仍然不知道那是哪个信号)并且释放 Return 键将激活导航到下一个单元格。瞧,我们有两个所需的操作,只需按一下 Return 键即可。
简答:
改变这个
self.dataTableTreeView.connect("key-press-event", self.onTreeNavigateKeyPress)
至此
self.dataTableTreeView.connect("key-release-event", self.onTreeNavigateKeyPress)