001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas/ 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see https://www.gnu.org/licenses/. 027 */ 028package edu.wisc.ssec.mcidasv; 029 030import static edu.wisc.ssec.mcidasv.McIdasPreferenceManager.PROP_HIQ_FONT_RENDERING; 031import static ucar.unidata.util.GuiUtils.makeMenu; 032import static ucar.unidata.util.MenuUtil.MENU_SEPARATOR; 033 034import java.io.BufferedReader; 035import java.io.FileReader; 036import java.io.IOException; 037import java.util.ArrayList; 038import java.util.List; 039import java.util.Map; 040import java.util.logging.Logger; 041 042import edu.wisc.ssec.mcidasv.startupmanager.options.BooleanOption; 043import org.python.util.PythonInterpreter; 044 045import edu.wisc.ssec.mcidasv.startupmanager.options.OptionMaster; 046import edu.wisc.ssec.mcidasv.util.CollectionHelpers; 047 048import ucar.unidata.data.DataSource; 049import ucar.unidata.data.DescriptorDataSource; 050import ucar.unidata.idv.IntegratedDataViewer; 051import ucar.unidata.idv.ui.ImageGenerator; 052import ucar.unidata.idv.ui.JythonShell; 053import ucar.unidata.util.FileManager; 054import ucar.unidata.util.Misc; 055 056import javax.swing.*; 057import javax.swing.filechooser.FileNameExtensionFilter; 058 059/** 060 * Overrides the IDV's {@link ucar.unidata.idv.JythonManager JythonManager} to 061 * associate a {@link JythonShell} with a given {@code JythonManager}. 062 */ 063public class JythonManager extends ucar.unidata.idv.JythonManager { 064 065// /** Trusty logging object. */ 066// private static final Logger logger = LoggerFactory.getLogger(JythonManager.class); 067 068 /** Associated Jython Shell. May be {@code null}. */ 069 private JythonShell jythonShell; 070 071 private static final Logger logger = 072 Logger.getLogger(JythonManager.class.getName()); 073 074 /** 075 * Create the manager and call initPython. 076 * 077 * @param idv The IDV. 078 */ 079 public JythonManager(IntegratedDataViewer idv) { 080 super(idv); 081 } 082 083 /** 084 * Create a Jython shell, if one doesn't already exist. This will also 085 * bring the window {@literal "to the front"} of the rest of the McIDAS-V 086 * session. 087 * 088 * @return JythonShell object for interactive Jython usage. 089 */ 090 public JythonShell createShell() { 091 if (jythonShell == null) { 092 jythonShell = new JythonShell(getIdv()); 093 094 } 095 jythonShell.toFront(); 096 return jythonShell; 097 } 098 099 public JythonShell createShellWithScript(String file) { 100 if (jythonShell == null) { 101 jythonShell = new JythonShell(getIdv()); 102 103 } 104 jythonShell.toFront(); 105 String cmd = ""; 106 try (BufferedReader reader = new BufferedReader(new FileReader(file))) { 107 String line; 108 while ((line = reader.readLine()) != null) { 109 cmd += line + "\n"; 110 } 111 } catch (IOException e) { 112 logger.info("Error reading scripting file: " + e.getMessage()); 113 } 114 115 jythonShell.eval(cmd); 116 return jythonShell; 117 } 118 119 /** 120 * Returns the Jython Shell associated with this {@code JythonManager}. 121 * 122 * @return Jython Shell being used by this manager. May be {@code null}. 123 */ 124 public JythonShell getShell() { 125 return jythonShell; 126 } 127 128 /** 129 * Create and initialize a Jython interpreter. 130 * 131 * @return Newly created Jython interpreter. 132 */ 133 @Override public PythonInterpreter createInterpreter() { 134 PythonInterpreter interpreter = super.createInterpreter(); 135 return interpreter; 136 } 137 138 /** 139 * Removes the given interpreter from the list of active interpreters. 140 * 141 * <p>Also attempts to close any Jython Shell associated with the 142 * interpreter.</p> 143 * 144 * @param interpreter Interpreter to remove. Should not be {@code null}. 145 */ 146 @Override public void removeInterpreter(PythonInterpreter interpreter) { 147 super.removeInterpreter(interpreter); 148 if ((jythonShell != null) && !jythonShell.isShellResetting() && jythonShell.getInterpreter().equals(interpreter)) { 149 jythonShell.close(); 150 jythonShell = null; 151 } 152 } 153 154 /** 155 * Overridden so that McIDAS-V can add an {@code islInterpreter} object 156 * to the interpreter's locals (before executing the contents of {@code}. 157 * 158 * @param code Jython code to evaluate. {@code null} is probably a bad idea. 159 * @param properties {@code String->Object} pairs to insert into the 160 * locals. Parameter may be {@code null}. 161 */ 162 @SuppressWarnings("unchecked") // dealing with idv code that predates generics. 163 @Override public void evaluateTrusted(String code, Map<String, Object> properties) { 164 if (properties == null) { 165 properties = CollectionHelpers.newMap(); 166 } 167 properties.putIfAbsent("islInterpreter", new ImageGenerator(getIdv())); 168 properties.putIfAbsent("_idv", getIdv()); 169 properties.putIfAbsent("idv", getIdv()); 170 super.evaluateTrusted(code, properties); 171 } 172 173 /** 174 * Return the list of menu items to use when the user has clicked on a 175 * formula {@link DataSource}. 176 * 177 * @param dataSource The data source clicked on. 178 * 179 * @return {@link List} of menu items. 180 */ 181 @SuppressWarnings("unchecked") // dealing with idv code that predates generics. 182 @Override public List doMakeFormulaDataSourceMenuItems(DataSource dataSource) { 183 List menuItems = new ArrayList(100); 184 JMenuItem menuItem; 185 186 menuItem = new JMenuItem("Create Formula"); 187 menuItem.addActionListener(e -> showFormulaDialog()); 188 menuItems.add(menuItem); 189 190 List editItems; 191 if (dataSource instanceof DescriptorDataSource) { 192 editItems = doMakeEditMenuItems((DescriptorDataSource)dataSource); 193 } else { 194 editItems = doMakeEditMenuItems(); 195 } 196 menuItems.add(makeMenu("Edit Formulas", editItems)); 197 198 menuItems.add(MENU_SEPARATOR); 199 200 menuItem = new JMenuItem("Jython Library"); 201 menuItem.addActionListener(e -> showJythonEditor()); 202 menuItems.add(menuItem); 203 204 menuItem = new JMenuItem("Jython Shell"); 205 menuItem.addActionListener(e -> createShell()); 206 menuItems.add(menuItem); 207 208 menuItem = new JMenuItem("Load Jython Script"); 209 menuItem.addActionListener(e -> { 210 FileNameExtensionFilter filter = new FileNameExtensionFilter("Python Files (*.py)", "py"); 211 String file = FileManager.getReadFile("Load Script", filter); 212 createShellWithScript(file); 213 }); 214 215 menuItems.add(menuItem); 216 217 menuItems.add(MENU_SEPARATOR); 218 219 menuItem = new JMenuItem("Import"); 220 menuItem.addActionListener(e -> importFormulas()); 221 menuItems.add(menuItem); 222 223 menuItem = new JMenuItem("Export"); 224 menuItem.addActionListener(e -> exportFormulas()); 225 menuItems.add(menuItem); 226 227 return menuItems; 228 } 229 230 /** 231 * Determine if the user should be warned about a potential bug that we've been unable to resolve. 232 * 233 * <p>The conditions for the bug to appear are: 234 * <ul> 235 * <li>In background mode (i.e. running a script).</li> 236 * <li>Geometry by reference is <b>disabled</b>.</li> 237 * <li>New font rendering is <b>enabled</b>.</li> 238 * </ul> 239 * 240 * @return {@code true} if the user's configuration has made it possible for bug to manifest. 241 */ 242 public static boolean shouldWarnImageCapturing() { 243 boolean backgroundMode = McIDASV.getStaticMcv().getArgsManager().isScriptingMode(); 244 boolean shouldWarn = false; 245 OptionMaster optMaster = OptionMaster.getInstance(); 246 BooleanOption useGeometryByRef = optMaster.getBooleanOption("USE_GEOBYREF"); 247 if (useGeometryByRef != null) { 248 shouldWarn = backgroundMode 249 && !Boolean.parseBoolean(System.getProperty("visad.java3d.geometryByRef")) 250 && Boolean.parseBoolean(System.getProperty(PROP_HIQ_FONT_RENDERING, "false")); 251 } 252// System.out.println("background mode: "+backgroundMode); 253// System.out.println("geo by ref: "+Boolean.parseBoolean(System.getProperty("visad.java3d.geometryByRef"))); 254// System.out.println("new fonts: "+Boolean.parseBoolean(System.getProperty(PROP_HIQ_FONT_RENDERING, "false"))); 255// System.out.println("shouldWarn: "+shouldWarn); 256// System.out.println("props:\n"+System.getProperties()); 257// return shouldWarn; 258 return false; 259 } 260 261}