I've encapsulated a matplotlib plot in a Gtk+ window and I'm trying to update that plot when a button is clicked (it's Gauss' circle problem). Trouble is, I'm not exactly sure how to get the plot to update with an event. So far I have the following.
#! /usr/bin/env python3.4
# -*- coding: utf-8 -*-
""" Main application--embed Matplotlib figure in window with UI """
import gi
gi.require_version('Gtk', '3.0')
import numpy as np
from gi.repository import Gtk, GObject
from matplotlib.figure import Figure
# make sure cairocffi is installed, pycairo doesn't support FigureCanvasGTK3Agg
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg \
as FigureCanvas
from matplotlib.patches import Ellipse
from typing import List, Tuple
from math import sqrt
class Main(Gtk.Window):
""" Main window UI """
SIGMA = 10
def __init__(self):
Gtk.Window.__init__(self, title='Gauss\' Circle Problem')
self.connect('destroy', lambda _: Gtk.main_quit())
self.set_border_width(10)
self.set_default_size(600, 450)
# Set up the l/r box layout
self.box = Gtk.Box(spacing=10)
self.add(self.box)
# Set up the right column
self.rcolumn = Gtk.Grid()
self.box.pack_end(self.rcolumn, False, False, 1)
# Set up spin button
adjustment = Gtk.Adjustment(10, 3, 100, 1, 0, 0)
self.spinbutton = Gtk.SpinButton()
self.spinbutton.set_adjustment(adjustment)
self.rcolumn.attach(self.spinbutton, 0, 0, 1, 1)
# Set up update button
self.update_plot_button = Gtk.Button(label='Update')
self.update_plot_button.connect('clicked', self.update_sigma_event)
self.rcolumn.attach_next_to(self.update_plot_button,
self.spinbutton, Gtk.PackDirection.BTT, 1, 1)
self._add_plot()
def update_sigma_event(self, button) -> None:
""" Update sigma and replot """
self.SIGMA = self.spinbutton.get_value()
self._add_plot()
def _add_plot(self) -> None:
""" Add the plot to the window """
fig = Figure(figsize=(5, 4))
ax = fig.add_subplot(111, aspect='equal')
arr = np.zeros([self.SIGMA * 2 + 1] * 2)
points = self.collect(int(self.SIGMA), int(self.SIGMA), self.SIGMA)
# flip pixel value if it lies inside (or on) the circle
for p in points:
arr[p] = 1
# plot ellipse on top of boxes to show their centroids lie inside
circ = Ellipse(\
xy=(int(self.SIGMA), int(self.SIGMA)),
width=2 * self.SIGMA,
height=2 * self.SIGMA,
angle=0.0
)
ax.add_artist(circ)
circ.set_clip_box(ax.bbox)
circ.set_alpha(0.2)
circ.set_facecolor((1, 1, 1))
ax.set_xlim(-0.5, 2 * self.SIGMA + 0.5)
ax.set_ylim(-0.5, 2 * self.SIGMA + 0.5)
# Plot the pixel centers
ax.scatter(*zip(*points), marker='.', color='white')
# now plot the array that's been created
ax.imshow(-arr, interpolation='none', cmap='gray')
# add it to the window
canvas = FigureCanvas(fig)
self.box.pack_start(canvas, True, True, 0)
@staticmethod
def collect(x: int, y: int, sigma: float =3.0) -> List[Tuple[int, int]]:
""" create a small collection of points in a neighborhood of some
point
"""
neighborhood = []
X = int(sigma)
for i in range(-X, X + 1):
Y = int(pow(sigma * sigma - i * i, 1/2))
for j in range(-Y, Y + 1):
neighborhood.append((x + i, y + j))
return neighborhood
if __name__ == '__main__':
window = Main()
window.show_all()
Gtk.main()
I'm not exactly sure where to go from here, I just know that updating the SpinButton
indeed adjusts self.SIGMA
, but I don't know how to tell matplotlib to update the plot in the window.
Also, this is what it looks like currently if you aren't able to run it (I'm also trying to vertically center the two button widgets in the right column :P):