Source code for pint.scripts.pintk

#!/usr/bin/env python -W ignore::FutureWarning -W ignore::UserWarning -W ignore:DeprecationWarning
"""Tkinter interactive interface for PINT pulsar timing tool"""
import argparse

import sys
import os

import tkinter as tk
import tkinter.filedialog as tkFileDialog
import tkinter.messagebox as tkMessageBox
import matplotlib as mpl
from loguru import logger as log

import pint.logging

pint.logging.setup(level=pint.logging.script_level)

import pint
from pint.pintk.paredit import ParWidget
from pint.pintk.plk import PlkWidget, helpstring
from pint.pintk.pulsar import Pulsar
from pint.pintk.timedit import TimWidget


__all__ = ["main"]


[docs]class PINTk: """Main PINTk window.""" def __init__( self, master, parfile=None, timfile=None, fitter="downhill", ephem=None, loglevel=None, **kwargs, ): self.master = master self.master.title("Tkinter interface to PINT") self.mainFrame = tk.Frame(master=self.master) self.mainFrame.grid(row=0, column=0, sticky="nesw") self.master.grid_rowconfigure(0, weight=1) self.master.grid_columnconfigure(0, weight=1) self.loglevel = loglevel self.maxcols = 2 self.createWidgets() if parfile is not None and timfile is not None: self.openPulsar( parfile=parfile, timfile=timfile, fitter=fitter, ephem=ephem ) self.initUI() self.updateLayout() def initUI(self): # Create top level menus top = self.mainFrame.winfo_toplevel() self.menuBar = tk.Menu(top) top["menu"] = self.menuBar self.fileMenu = tk.Menu(self.menuBar) self.fileMenu.add_command( label="Open par/tim", command=self.openParTim, underline=0, accelerator="Ctrl+O", ) self.fileMenu.add_command(label="Switch model", command=self.switchModel) self.fileMenu.add_command(label="Switch TOAs", command=self.switchTOAs) parfile_submenu = tk.Menu(self.fileMenu) parfile_submenu.add_command( label="Write par (pint format)", command=self.writeParPINT ) parfile_submenu.add_command( label="Write par (tempo2 format)", command=self.writeParTempo2 ) parfile_submenu.add_command( label="Write par (tempo format)", command=self.writeParTempo ) self.fileMenu.add_cascade(label="Write par...", menu=parfile_submenu) timfile_submenu = tk.Menu(self.fileMenu) timfile_submenu.add_command( label="Write tim (tempo2 format)", command=self.writeTimTempo2 ) timfile_submenu.add_command( label="Write tim (tempo format)", command=self.writeTimTempo ) self.fileMenu.add_cascade(label="Write tim...", menu=timfile_submenu) self.fileMenu.add_command(label="Exit", command=top.destroy, accelerator="q") self.menuBar.add_cascade(label="File", menu=self.fileMenu, underline=0) self.viewMenu = tk.Menu(self.menuBar) self.viewMenu.add_checkbutton( label="Plk", command=self.updateLayout, variable=self.active["plk"], accelerator="Ctrl+p", ) self.viewMenu.add_checkbutton( label="Model Editor", command=self.updateLayout, variable=self.active["par"], accelerator="Ctrl+m", ) self.viewMenu.add_checkbutton( label="TOAs Editor", command=self.updateLayout, variable=self.active["tim"], accelerator="Ctrl+t", ) self.menuBar.add_cascade(label="View", menu=self.viewMenu) self.helpMenu = tk.Menu(self.menuBar) self.helpMenu.add_command(label="About", command=self.about) self.helpMenu.add_command( label="PINTk Help", command=lambda: print(helpstring), accelerator="h" ) self.menuBar.add_cascade(label="Help", menu=self.helpMenu) # Key bindings top.bind("<Control-p>", lambda e: self.toggle("plk")) top.bind("<Control-m>", lambda e: self.toggle("par")) top.bind("<Control-t>", lambda e: self.toggle("tim")) top.bind("<Control-o>", lambda e: self.openParTim()) def createWidgets(self): self.widgets = { "plk": PlkWidget(master=self.mainFrame, loglevel=self.loglevel), "par": ParWidget(master=self.mainFrame), "tim": TimWidget(master=self.mainFrame), } self.active = {"plk": tk.IntVar(), "par": tk.IntVar(), "tim": tk.IntVar()} self.active["plk"].set(1) def updateLayout(self): for widget in self.mainFrame.winfo_children(): widget.grid_forget() visible = 0 for key in self.active.keys(): if self.active[key].get(): row = int(visible / self.maxcols) col = visible % self.maxcols self.widgets[key].grid(row=row, column=col, sticky="nesw") self.mainFrame.grid_rowconfigure(row, weight=1) self.mainFrame.grid_columnconfigure(col, weight=1) visible += 1 def openPulsar(self, parfile, timfile, fitter="downhill", ephem=None): self.psr = Pulsar(parfile, timfile, ephem, fitter=fitter) self.widgets["plk"].setPulsar( self.psr, updates=[self.widgets["par"].set_model, self.widgets["tim"].set_toas], ) self.widgets["par"].setPulsar(self.psr, updates=[self.widgets["plk"].update]) self.widgets["tim"].setPulsar(self.psr, updates=[self.widgets["plk"].update]) def switchModel(self): parfile = tkFileDialog.askopenfilename(title="Open par file") self.psr.parfile = parfile self.psr.reset_model() self.widgets["plk"].update() self.widgets["par"].set_model() def switchTOAs(self): timfile = tkFileDialog.askopenfilename() self.psr.timfile = timfile self.psr.reset_TOAs() self.widgets["plk"].update() self.widgets["tim"].set_toas() def openParTim(self): parfile = tkFileDialog.askopenfilename(title="Open par file") timfile = tkFileDialog.askopenfilename(title="Open tim file") self.openPulsar(parfile, timfile) def writeParPINT(self): self.widgets["plk"].writePar(format="pint") def writeParTempo(self): self.widgets["plk"].writePar(format="tempo") def writeParTempo2(self): self.widgets["plk"].writePar(format="tempo2") def writeTimTempo(self): self.widgets["plk"].writeTim(format="tempo") def writeTimTempo2(self): self.widgets["plk"].writeTim(format="tempo2") def toggle(self, key): self.active[key].set((self.active[key].get() + 1) % 2) self.updateLayout() def about(self): tkMessageBox.showinfo( title="About PINTk", message=f"A Tkinter based graphical interface to PINT (version={pint.__version__}), using matplotlib (version={mpl.__version__}) and the {mpl.get_backend()} backend", )
[docs]def main(argv=None): parser = argparse.ArgumentParser( description="Tkinter interface for PINT pulsar timing tool" ) parser.add_argument("parfile", help="parfile to use") parser.add_argument("timfile", help="timfile to use") parser.add_argument("--ephem", help="Ephemeris to use", default=None) parser.add_argument( "--test", help="Build UI and exit. Just for unit testing.", default=False, action="store_true", ) parser.add_argument( "-f", "--fitter", type=str, choices=( "notdownhill", "downhill", "WLSFitter", "GLSFitter", "WidebandTOAFitter", "PowellFitter", "DownhillWLSFitter", "DownhillGLSFitter", "WidebandDownhillFitter", "WidebandLMFitter", ), default="downhill", help="PINT Fitter to use [default='downhill']. 'notdownhill' will choose WLS/GLS/WidebandTOA depending on TOA/model properties. 'downhill' will do the same for Downhill versions.", ) parser.add_argument( "--version", action="version", help="Print version info and exit.", version=f"This is PINT version {pint.__version__}, using matplotlib (version={mpl.__version__})", ) parser.add_argument( "--log-level", type=str, choices=pint.logging.levels, default=pint.logging.script_level, help="Logging level", dest="loglevel", ) parser.add_argument( "-v", "--verbosity", default=0, action="count", help="Increase output verbosity" ) parser.add_argument( "-q", "--quiet", default=0, action="count", help="Decrease output verbosity" ) args = parser.parse_args(argv) pint.logging.setup( level=pint.logging.get_level(args.loglevel, args.verbosity, args.quiet) ) # see if the arguments were flipped if ( os.path.splitext(args.parfile)[1] == ".tim" and os.path.splitext(args.timfile)[1] == ".par" ): log.debug( f"Swapping inputs: parfile='{args.timfile}' and timfile='{args.parfile}'" ) args.parfile, args.timfile = args.timfile, args.parfile else: if os.path.splitext(args.timfile)[1] != ".tim": log.info( f"Input timfile '{args.timfile}' has unusual extension '{os.path.splitext(args.timfile)[1]}': is this intended?" ) if os.path.splitext(args.parfile)[1] != ".par": log.info( f"Input parfile '{args.parfile}' has unusual extension '{os.path.splitext(args.parfile)[1]}': is this intended?" ) root = tk.Tk() root.minsize(1000, 800) if not args.test: app = PINTk( root, parfile=args.parfile, timfile=args.timfile, fitter=args.fitter, ephem=args.ephem, loglevel=pint.logging.get_level(args.loglevel, args.verbosity, args.quiet), ) root.protocol("WM_DELETE_WINDOW", root.destroy) img = tk.Image("photo", file=pint.pintk.plk.icon_img) root.tk.call("wm", "iconphoto", root._w, img) tk.mainloop()
if __name__ == "__main__": main()