Source code for fact.plotting.viewer

import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.figure import Figure
import numpy as np
import os
from .utils import calc_linewidth
from ..instrument import get_pixel_coords
from . import camera

import tkinter as tk
from tkinter import filedialog


[docs]class Viewer(): """ A tkinter based GUI to look at fact events in the camera view. Attributes ---------- dataset : array like with shape (num_events, 1440) the data you want to plot into the pixels label : str the label for the colormap clickedcolour: a matplotlib conform colour represantation the coulour for clicked pixel [default: red] mapfile : str path/to/fact/pixelmap.csv [default pixel-map.csv] cmap : str or matplotlib colormap instance the colormap to use for plotting the 'dataset' [default: gray] vmin : float the minimum for the colorbar, if None min(dataset[event]) is used [default: None] vmax : float the maximum for the colorbar, if None max(dataset[event]) is used [default: None] """ def __init__(self, dataset, label, clickedcolour="r", mapfile="pixel-map.csv", cmap="gray", vmin=None, vmax=None, ): matplotlib.use('TkAgg', warn=False, force=True) matplotlib.rcdefaults() self.event = 0 if dataset.shape == (1440, ): self.dataset = np.reshape(dataset, (1, 1440)) elif dataset.shape[1] == 1440: self.dataset = dataset else: raise ValueError('Viewer expects dataset with shape (1440, )\n' 'or (n_events, 1440)' ) self.numEvents = dataset.shape[0] self.clickedcolour = clickedcolour self.label = label self.cmap = cmap self.vmin = vmin self.vmax = vmax self.pixel_x, self.pixel_y = get_pixel_coords() self.fig = Figure(figsize=(7, 6), dpi=100) self.init_plot() # ---- GUI Stuff ---- self.root = tk.Tk() self.root.geometry(("1024x768")) self.root.wm_title("PyFactViewer") buttonFrame = tk.Frame(self.root) plotFrame = tk.Frame(self.root) infoFrame = tk.Frame(plotFrame) buttonFrame.pack(side=tk.TOP) plotFrame.pack(side=tk.BOTTOM, expand=True, fill=tk.BOTH) infoFrame.pack(side=tk.BOTTOM) self.canvas = FigureCanvasTkAgg(self.fig, master=plotFrame) self.canvas.mpl_connect("pick_event", self.onpick) self.canvas.mpl_connect("resize_event", self.redraw) self.canvas.show() self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) self.canvas._tkcanvas.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1) self.root.bind('<Key>', self.on_key_event) self.quit_button = tk.Button(master=buttonFrame, text='Quit', command=self.quit, expand=None, ) self.next_button = tk.Button(master=buttonFrame, text='Next', command=self.next, expand=None, ) self.previous_button = tk.Button(master=buttonFrame, text='Previous', command=self.previous, expand=None, ) self.save_button = tk.Button(master=buttonFrame, text='Save Image', command=self.save, expand=None, ) self.eventstring = tk.StringVar() self.eventstring.set("EventNum: {:05d}".format(self.event)) self.eventbox = tk.Label(master=buttonFrame, textvariable=self.eventstring, ) self.eventbox.pack(side=tk.LEFT) self.previous_button.pack(side=tk.LEFT) self.next_button.pack(side=tk.LEFT) self.quit_button.pack(side=tk.RIGHT) self.save_button.pack(side=tk.RIGHT) self.infotext = tk.StringVar() self.infotext.set("Click on a Pixel") self.infobox = tk.Label(master=infoFrame, textvariable=self.infotext) self.infobox.pack(side=tk.LEFT) self.update() tk.mainloop()
[docs] def init_plot(self): self.width, self.height = self.fig.get_figwidth(), self.fig.get_figheight() self.fig.clf() self.ax = self.fig.add_subplot(1, 1, 1, aspect=1) divider = make_axes_locatable(self.ax) self.cax = divider.append_axes("right", size="5%", pad=0.1) self.ax.set_axis_off() if self.vmin is None: vmin = np.min(self.dataset[self.event]) else: vmin = self.vmin if self.vmax is None: vmax = np.max(self.dataset[self.event]) else: vmax = self.vmax self.plot = camera( ax=self.ax, data=self.dataset[self.event], pixelcoords=None, cmap=self.cmap, vmin=vmin, vmax=vmax, linewidth=None, picker=False, ) self.plot.set_picker(0) self.clicked_pixel = None self.cb = self.fig.colorbar(self.plot, cax=self.cax, label=self.label) self.cb.set_clim(vmin=vmin, vmax=vmax) self.cb.draw_all()
[docs] def save(self): filename = filedialog.asksaveasfilename( initialdir=os.getcwd(), parent=self.root, title="Choose a filename for the saved image", defaultextension=".pdf", ) if filename: fig = self.fig fig.savefig(filename, dpi=300, bbox_inches="tight", transparent=True) print("Image sucessfully saved to", filename)
[docs] def redraw(self, event): self.linewidth = calc_linewidth(self.ax) self.fig.tight_layout(pad=0) self.canvas.draw()
[docs] def quit(self): self.root.quit() # stops mainloop self.root.destroy() # this is necessary on Windows to prevent
[docs] def next(self): self.event = (self.event + 1) % len(self.dataset) self.update()
[docs] def previous(self): if self.event == 0: self.event = self.numEvents - 1 else: self.event -= 1 self.update()
[docs] def on_key_event(self, event): if event.keysym == "Right": self.next() elif event.keysym == "Left": self.previous() elif event.keysym == "q": self.quit()
[docs] def update(self): self.plot.set_array(self.dataset[self.event]) self.plot.changed() if self.vmin is None: vmin = np.min(self.dataset[self.event]) else: vmin = self.vmin if self.vmax is None: vmax = np.max(self.dataset[self.event]) else: vmax = self.vmax self.linewidth = calc_linewidth(self.ax) self.plot.set_linewidths(self.linewidth) self.cb.set_clim(vmin=vmin, vmax=vmax) self.cb.draw_all() self.plot.set_picker(0) self.canvas.draw() self.eventstring.set("EventNum: {:05d}".format(self.event))
[docs] def onpick(self, event): hitpixel = event.ind[0] if hitpixel != self.clicked_pixel: self.clicked_pixel = hitpixel self.update() self.infotext.set( "chid: {:04d}, {} = {:4.2f}".format( hitpixel, self.label, self.plot.get_array()[hitpixel] ) )