GTK:在 Gtk.ApplicationWindow 上重绘小部件时出现问题
GTK: Problems redrawing widgets on Gtk.ApplicationWindow
我正在 Python 中为一个已经存在的程序创建一个 GTK GUI。想象一下下面的结构:
program
|----gtk
|-----application.py
|-----mainwindow.py
|-----mainwidgets.py
当然这是简化了的。但是 GUI 的工作方式有点像这样:application.py 是一个 Gtk.Application,它在 mainwidgets 中创建对象的实例,提供作为回调传递给 mainwindow.py 的后端函数,它放置小部件在 Gtk.ApplicationWindow 中。
有几个案例我遇到了麻烦。我将讨论两个我认为相关的问题。
让我们先从简单的开始。在 mainwidget 中定义了一个简单的按钮,它在我的程序中用作状态栏。
class Statusbar(Gtk.Widget):
def __init__(self, on_button_do):
super(Gtk.Widget, self).__init__()
self.callback = on_button_do
self.button_label_int = 0
self.button = Gtk.Button.new_with_label(str(button_label_int)
def increment_button_label(self):
self.button_label_int += 1
self.button.set_label(str(button_label))
然后,当主窗口接收到信号时,指示调用
statusbar.increment_button_label()。一旦收到该信号,这几乎立即因分段错误而崩溃,没有提供有关该问题的更多信息。
class AppWindow(Gtk.ApplicationWindow):
__gsignals__ = {
"new_log": (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self, statusbar, sidebar, *args, **kwargs):
super(Gtk.ApplicationWindow, self).__init__(*args, **kwargs)
self.statusbar = statusbar
self.sidebar = sidebar
self.notificationBox = Gtk.Box()
self.notificationBox.pack_start(self.statusbar.get_button(), True, True, 0)
# MAINBOX: THE BIGGER BOX OF ALL THE LITTLE BOXES
self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.mainBox.pack_start(self.
self.mainBox.pack_start(self.notificationBox, False, False, 0)
self.add(self.mainBox)
self.show_all()
def do_new_log(self):
"""What should the window do when it gets a new_log signal"""
statusbar.increment_button_label()
另一个(相关的?)问题与更新小部件的显示有关:我的侧边栏。侧边栏实际上只是一个 Gtk.ListStore 和一个 Gtk.TreeView,没什么特别的。在 application.py 你会发现这样的代码:
def do_startup(self):
Gtk.Application.do_startup(self) # deep GTK magic
self.sidebar = Sidebar(self.workspace_manager,
self.changeWorkspace,
CONF.getLastWorkspace())
然后将侧边栏的实例传递给 mainwindow.py 以放入框中。目前一切正常。
当我尝试向侧边栏添加信息时出现问题。它的后端部分有效,因为如果我重新启动应用程序,我可以在侧边栏中看到新条目。侧边栏从这个后端获取信息:
class Sidebar(Gtk.Widget):
def __init__(self, workspace_manager, callback_to_change_workspace, conf):
super(Gtk.Widget, self).__init__()
self.callback = callback_to_change_workspace
self.ws_manager = workspace_manager
self.lastWorkspace = conf
def createTitle(self):
title = Gtk.Label()
title.set_text("Workspaces")
return title
def workspaceModel(self):
self.workspace_list_info = Gtk.ListStore(str)
for ws in self.ws_manager.getWorkspacesNames():
treeIter = workspace_list_info.append([ws])
if ws == self.lastWorkspace:
self.defaultSelection = treeIter
def workspaceView(self):
self.lst = Gtk.TreeView(self.workspace_list_info)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Workspaces", renderer, text=0)
self.lst.append_column(column)
# select by default the last active workspace
if self.defaultSelection is not None:
self.selectDefault = self.lst.get_selection()
self.selectDefault.select_iter(self.defaultSelection)
self.selection = self.lst.get_selection()
self.selection.connect("changed", self.callback)
maindindow 试图做的是将 sidebar.lst 放入一个盒子中。那很好用。问题是当我通过对话框添加工作区时,我没有出现,正如我上面所说的。
知道是什么导致了这些问题吗?由于某种原因,这种组织问题的方式对 GTK 来说不合适吗?我认为 code 本身很好:毕竟添加了工作区,并且 GTK 不会崩溃。它只是做得不好。此外,按钮一开始显示得很好,它甚至会发出信号。但是,一旦我尝试更改其标签,一切都会爆炸。
好的,所以这实际上与多线程和信号有关。它是通过覆盖发射信号解决的。
class _IdleObject(GObject.GObject):
"""
Override GObject.GObject to always emit signals in the main thread
by emmitting on an idle handler
"""
def __init__(self):
GObject.GObject.__init__(self)
def emit(self, *args):
GObject.idle_add(GObject.GObject.emit, self, *args)
我正在 Python 中为一个已经存在的程序创建一个 GTK GUI。想象一下下面的结构:
program
|----gtk
|-----application.py
|-----mainwindow.py
|-----mainwidgets.py
当然这是简化了的。但是 GUI 的工作方式有点像这样:application.py 是一个 Gtk.Application,它在 mainwidgets 中创建对象的实例,提供作为回调传递给 mainwindow.py 的后端函数,它放置小部件在 Gtk.ApplicationWindow 中。
有几个案例我遇到了麻烦。我将讨论两个我认为相关的问题。
让我们先从简单的开始。在 mainwidget 中定义了一个简单的按钮,它在我的程序中用作状态栏。
class Statusbar(Gtk.Widget):
def __init__(self, on_button_do):
super(Gtk.Widget, self).__init__()
self.callback = on_button_do
self.button_label_int = 0
self.button = Gtk.Button.new_with_label(str(button_label_int)
def increment_button_label(self):
self.button_label_int += 1
self.button.set_label(str(button_label))
然后,当主窗口接收到信号时,指示调用 statusbar.increment_button_label()。一旦收到该信号,这几乎立即因分段错误而崩溃,没有提供有关该问题的更多信息。
class AppWindow(Gtk.ApplicationWindow):
__gsignals__ = {
"new_log": (GObject.SIGNAL_RUN_FIRST, None, ())
}
def __init__(self, statusbar, sidebar, *args, **kwargs):
super(Gtk.ApplicationWindow, self).__init__(*args, **kwargs)
self.statusbar = statusbar
self.sidebar = sidebar
self.notificationBox = Gtk.Box()
self.notificationBox.pack_start(self.statusbar.get_button(), True, True, 0)
# MAINBOX: THE BIGGER BOX OF ALL THE LITTLE BOXES
self.mainBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.mainBox.pack_start(self.
self.mainBox.pack_start(self.notificationBox, False, False, 0)
self.add(self.mainBox)
self.show_all()
def do_new_log(self):
"""What should the window do when it gets a new_log signal"""
statusbar.increment_button_label()
另一个(相关的?)问题与更新小部件的显示有关:我的侧边栏。侧边栏实际上只是一个 Gtk.ListStore 和一个 Gtk.TreeView,没什么特别的。在 application.py 你会发现这样的代码:
def do_startup(self):
Gtk.Application.do_startup(self) # deep GTK magic
self.sidebar = Sidebar(self.workspace_manager,
self.changeWorkspace,
CONF.getLastWorkspace())
然后将侧边栏的实例传递给 mainwindow.py 以放入框中。目前一切正常。
当我尝试向侧边栏添加信息时出现问题。它的后端部分有效,因为如果我重新启动应用程序,我可以在侧边栏中看到新条目。侧边栏从这个后端获取信息:
class Sidebar(Gtk.Widget):
def __init__(self, workspace_manager, callback_to_change_workspace, conf):
super(Gtk.Widget, self).__init__()
self.callback = callback_to_change_workspace
self.ws_manager = workspace_manager
self.lastWorkspace = conf
def createTitle(self):
title = Gtk.Label()
title.set_text("Workspaces")
return title
def workspaceModel(self):
self.workspace_list_info = Gtk.ListStore(str)
for ws in self.ws_manager.getWorkspacesNames():
treeIter = workspace_list_info.append([ws])
if ws == self.lastWorkspace:
self.defaultSelection = treeIter
def workspaceView(self):
self.lst = Gtk.TreeView(self.workspace_list_info)
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Workspaces", renderer, text=0)
self.lst.append_column(column)
# select by default the last active workspace
if self.defaultSelection is not None:
self.selectDefault = self.lst.get_selection()
self.selectDefault.select_iter(self.defaultSelection)
self.selection = self.lst.get_selection()
self.selection.connect("changed", self.callback)
maindindow 试图做的是将 sidebar.lst 放入一个盒子中。那很好用。问题是当我通过对话框添加工作区时,我没有出现,正如我上面所说的。
知道是什么导致了这些问题吗?由于某种原因,这种组织问题的方式对 GTK 来说不合适吗?我认为 code 本身很好:毕竟添加了工作区,并且 GTK 不会崩溃。它只是做得不好。此外,按钮一开始显示得很好,它甚至会发出信号。但是,一旦我尝试更改其标签,一切都会爆炸。
好的,所以这实际上与多线程和信号有关。它是通过覆盖发射信号解决的。
class _IdleObject(GObject.GObject):
"""
Override GObject.GObject to always emit signals in the main thread
by emmitting on an idle handler
"""
def __init__(self):
GObject.GObject.__init__(self)
def emit(self, *args):
GObject.idle_add(GObject.GObject.emit, self, *args)