如何使用Python和Glade在GTK3中刷新matplotlib图表



我已经使用GTK3、Python和Glade将两个matplotlib图插入到一个容器中。他们使用来自第三方API的数据绘制区域设置的天气。我希望在输入新的区域设置并按下刷新按钮时更新图形。

下面的每一个解决方案都以不同的问题结束。太多了,无法描绘。通常,他们已经设法用新图表显示主窗口的另一个实例,但用旧图表显示的旧实例仍然打开。我试过:

  • 销毁父容器,然后重新加载更新的容器。以下代码产生分段故障。代码试图将对象从生成器的新实例传递到旧实例。我认为这是我的主要问题。我不知道如何将正在使用的构建器实例传递到on_refresh_button_click()中,而不必将所有内容重写到一个类中。

    def on_refresh_button_click(self, widget):
    parent = self.get_parent()
    grandparent = parent.get_parent()
    parent.destroy()
    city_name = "yerevan"
    db = "test.db"
    get_updated_data(city_name)
    builder = builder_with_signals()
    read_weather_from_db(db, builder, city_name)
    grandparent.add(parent)
    parent.show_all()
    
  • 使用self.get_parent()获取按钮的父容器作为要处理的对象。我认为这几乎奏效了。我可以将remove()destroy()作为包含该图的容器。我想我也成功地添加了更新的一个。但我没能把它展示出来。代码:

    def on_refresh_button_click(self, widget):
    parent = self.get_parent()
    city_name = "yerevan"
    db = "test.db"
    get_updated_data(city_name)
    fig_list = read_weather_from_db(db, city_name)
    for child in parent:
    try:
    for grandchild in child:
    if Gtk.Buildable.get_name(grandchild) == "chart_past":
    parent = child # Confusing, yes.
    old = grandchild
    new = FigureCanvas(fig_list[0])
    props = {}
    for key in parent.list_child_properties():
    props[key.name] = parent.child_get_property(old, key.name)
    parent.remove(old)
    parent.add(new)
    for name, value in props.items():
    parent.child_set_property(new, name, value)
    parent.show_all()
    child.show_all()
    grandchild.show_all()
    # Try to find the newly added object
    for item in parent:
    print("trying to find another:", Gtk.Buildable.get_name(item))
    except:
    print(Gtk.Buildable.get_name(child))
    
  • 删除旧的容器/小部件,然后添加一个新的,使用以下代码:

    def replace_widget(old, new):
    parent = old.get_parent()
    props = {}
    for key in Gtk.ContainerClass.list_child_properties(type(parent)):
    props[key.name] = parent.child_get_property(old, key.name)
    parent.remove(old)
    parent.add(new)
    for name, value in props.iteritems():
    parent.child_set_property(new, name, value)
    
  • 在使用不同的区域设置从头开始运行脚本之前销毁主窗口:

    def on_refresh_button_click(self, widget):
    builder = setup_builder()
    add_signals(builder)
    window = builder.get_object("window1")
    window.destroy()
    display_data("yerevan")
    
  • 关闭程序并重新启动它,这甚至对我来说都没有意义,但:

    def on_refresh_button_click(self, widget):
    Gtk.main_quit()
    display_data("yerevan")
    
  • 从这里、这里和这里使用canvas.draw()

  • add()替换add_with_viewport(),因为这说明它有问题。

还阅读了本文档的不同部分,并尝试了其他一些事情,但这两天太长了,所以我忘记了。

大多数示例似乎使用GTK3和Python构建应用程序,但没有Glade。他们也使用类。我(暂时(不想使用类。在我把整件事重写成一个类之前,我想看看是否有人知道解决方案。我可能只是误解或错过了什么。

我是GTK和Glade的新手,这是我的第一次尝试,所以请原谅这场混乱。我省略了SQLCRUD代码和向API发送请求的代码。这些效果很好。相关代码:

# SETUP THE BUILDER
def setup_builder():
return Gtk.Builder()
def add_signals(builder):
builder.add_objects_from_file('weather.xml', ('window1', 'refresh_button', 'box_charts'))
return builder.connect_signals({'on_window1_destroy': (on_window1_destroy,'window1'),
'on_refresh_button_click': (on_refresh_button_click,),
})
def builder_with_signals():
builder = setup_builder()
add_signals(builder)
return builder

# READ DATA FROM DATABASE
def read_weather_from_db(db, builder, city_name):
chart_future_values = read_db(db, "chart_future", city_name)
chart_past_values = read_db(db, "chart_past", city_name)
fig_future = embed_chart("day and time", "temp", chart_future_values["xticks"], chart_future_values["datetimes_x_axis"], chart_future_values["temps"])
fig_past = embed_chart("day and time", "temp", chart_past_values["xticks"], chart_past_values["datetimes_x_axis"], chart_past_values["temps"])
add_canvas(builder, "chart_future", fig_future)
add_canvas(builder, "chart_past", fig_past)
return builder

# EMBED THE CHARTS INTO CONTAINERS
def embed_chart(xlabel, ylabel, xticks, xticklabels, yticks):
fig = Figure(figsize=(5, 5), dpi=100)
chart = fig.add_subplot(111)
chart.set_xlabel(xlabel)
chart.set_ylabel(ylabel)
chart.set_xticks(xticks)
chart.set_xticklabels(xticklabels, rotation=90)
chart.plot(xticks, yticks)
return fig
def add_canvas(builder, chart_container, fig):
canvas = FigureCanvas(fig)
subbox_chart = builder.get_object(chart_container)
subbox_chart.add(canvas)

# THIS RUNS THE SCRIPT
def display_data(city_name="isfahan"):
get_updated_data(city_name)
builder = builder_with_signals()
read_weather_from_db("test.db", builder, city_name)
show_gtk(builder)
def on_window1_destroy(self, widget):
Gtk.main_quit()
# HERE IS THE IMPORTANT BIT
def on_refresh_button_click(self, widget):
# I DON'T KNOW WHAT TO PUT HERE
def show_gtk(builder):
window_main = builder.get_object('window1')
window_main.show_all()
Gtk.main()

我不认为你需要Glade xml文件,但我不确定,因为我对这个太陌生了:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<signal name="destroy" handler="on_window1_destroy" swapped="no"/>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox" id="box_main">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox" id="box_charts">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="chart_past">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="min_content_width">500</property>
<property name="min_content_height">500</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="chart_future">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="min_content_width">500</property>
<property name="min_content_height">500</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="refresh_button">
<property name="label" translatable="yes">refresh</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="button-press-event" handler="on_refresh_button_click" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

事实证明,使用self.parent()作为起点可能是最理想的选择,因为我不想将整个事情重写为一个类。但是:

  • 销毁/移除包含图形的子容器意味着现在没有更多的容器可以放入新图形
  • 我认为我无法删除子容器中的图形。这种假设是错误的

删除图形需要花费一些精力。假设child是包含容器对象的变量。打印child.get_children()返回None。这在一定程度上导致了我的错误假设。

但我注意到,当我尝试add()一个更新的图时,它给了我这个错误:gtk_scrolled_window_add: assertion 'child_widget == NULL' failed。里面有什么东西。

我看不到它,但我可以删除这个child_widget吗?

# This successfully removes any descendent in `child`.
# It leaves the container empty, so to speak.
for grandkid in child.get_children():
child.remove(grandkid)
# This adds and displays the updated figure.
new = FigureCanvas(fig)
child.add(new)
child.show_all()

工作。

我不确定我能给你一个使用现有代码的例子,但我是如何做到的:

Figure = None
def invoice_chart_clicked (self, button):
global Figure
if Figure == None:
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
from matplotlib.pyplot import pie
self.figure = Figure(figsize=(4, 4), dpi=100)
canvas = FigureCanvas(self.figure)  # a Gtk.DrawingArea
canvas.set_size_request(800, 500)
overlay = self.builder.get_object('overlay1')
overlay.add (canvas)
a = self.figure.add_subplot(111)
labels = list()
fractions = list()
unpaid = 0
self.cursor.execute("SELECT SUM(amount_due), c.name FROM invoices "
"JOIN contacts AS c ON c.id = invoices.customer_id "
"WHERE (canceled, paid, posted) = "
"(False, False, True) GROUP BY customer_id, c.name "
"ORDER BY SUM(amount_due)")
for row in self.cursor.fetchall():
customer_total = row[0]
customer_name = row[1]
fractions.append(customer_total)
labels.append(customer_name)
unpaid += 1
if unpaid == 0:
labels.append("None")
fractions.append(1.00)
a.pie(fractions, labels=labels, autopct='%1.f%%', radius=0.7)
window = self.builder.get_object('window1')
window.show_all()

每次重新加载此函数时,都会重新生成绘图。你可以在这里找到完整的代码。我从来没有运行过任何测试来查看所有内存是否都被正确释放,等等。也许它会提供足够的内存。

像这里一样创建一个单独的绘图窗口,GUI中的按钮将触发刷新?https://github.com/f4iteightiz/UWR_simulator一个连续运行的函数动画应该为您提供所需的"刷新"功能。

相关内容

  • 没有找到相关文章

最新更新