$darkmode
DENOPTIM
MoleculeViewPanel.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2020 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.gui;
20
21
22import java.awt.Color;
23import java.awt.Cursor;
24import java.awt.Dimension;
25import java.awt.event.ActionEvent;
26import java.awt.event.ActionListener;
27import java.awt.event.MouseEvent;
28import java.awt.event.MouseListener;
29import java.io.File;
30import java.util.HashSet;
31import java.util.Map;
32import java.util.Map.Entry;
33import java.util.Set;
34import java.util.TreeMap;
35import java.util.TreeSet;
36
37import javax.swing.JCheckBoxMenuItem;
38import javax.swing.JOptionPane;
39import javax.swing.JPopupMenu;
40import javax.swing.JScrollPane;
41import javax.swing.JSplitPane;
42import javax.swing.JTable;
43import javax.swing.UIManager;
44import javax.swing.table.DefaultTableModel;
45import javax.swing.table.JTableHeader;
46
47import org.openscience.cdk.CDKConstants;
48import org.openscience.cdk.interfaces.IAtomContainer;
49
50import denoptim.constants.DENOPTIMConstants;
51import denoptim.exception.DENOPTIMException;
52import denoptim.graph.Candidate;
53import denoptim.io.DenoptimIO;
54
55
62public class MoleculeViewPanel extends JSplitPane
63{
67 private static final long serialVersionUID = 912850110991449553L;
68
72 private Candidate item;
73
78 private boolean toleratePartialData = false;
79
81 private JScrollPane tabPanel;
82 protected DefaultTableModel dataTabModel;
83 protected JTable dataTable;
84 private JPopupMenu dataTabPopMenu;
85
86 private String tmpSDFFile;
87
88 private final String NL = System.getProperty("line.separator");
89
90//-----------------------------------------------------------------------------
91
96 {
97 initialize(340);
98 }
99
100//-----------------------------------------------------------------------------
101
107 public MoleculeViewPanel(int dividerPosition)
108 {
109 initialize(dividerPosition);
110 }
111
112//-----------------------------------------------------------------------------
113
114 @SuppressWarnings("serial")
115 private void initialize(int dividerPosition)
116 {
117 this.setOrientation(JSplitPane.VERTICAL_SPLIT);
118 this.setOneTouchExpandable(true);
119 this.setDividerLocation(dividerPosition);
120
121 // Jmol viewer panel
122 jmolPanel = new JmolPanel();
123 this.setTopComponent(jmolPanel);
124
125 // Data table
126 dataTabModel = new DefaultTableModel() {
127 @Override
128 public boolean isCellEditable(int row, int column) {
129 return true;
130 }
131 };
132 dataTabModel.setColumnCount(2);
133 String column_names[]= {"<html><b>Property</b></html>",
134 "<html><b>Value</b></html>"};
135 dataTabModel.setColumnIdentifiers(column_names);
136 dataTable = new JTable(dataTabModel);
137 dataTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
138 dataTable.getColumnModel().getColumn(0).setMaxWidth(75);
139 dataTable.getColumnModel().getColumn(1).setMinWidth(750);
140 dataTable.setGridColor(Color.LIGHT_GRAY);
141 dataTable.addMouseListener(new MouseListener() {
142 @Override
143 public void mouseReleased(MouseEvent e) {
144 showPopup(e);
145 }
146 @Override
147 public void mousePressed(MouseEvent e) {
148 showPopup(e);
149 }
150 public void mouseExited(MouseEvent e) {}
151 public void mouseEntered(MouseEvent e) {}
152 @Override
153 public void mouseClicked(MouseEvent e) {
154 showPopup(e);
155 }
156 });
157 JTableHeader dataTabHeader = dataTable.getTableHeader();
158 dataTabHeader.setPreferredSize(new Dimension(100, 20));
159 dataTabHeader.addMouseListener(new MouseListener() {
160 @Override
161 public void mouseReleased(MouseEvent e) {
162 showPopup(e);
163 }
164 @Override
165 public void mousePressed(MouseEvent e) {
166 showPopup(e);
167 }
168 public void mouseExited(MouseEvent e) {}
169 public void mouseEntered(MouseEvent e) {}
170 @Override
171 public void mouseClicked(MouseEvent e) {
172 showPopup(e);
173 }
174 });
175 tabPanel = new JScrollPane(dataTable,
176 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
177 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
178 this.setBottomComponent(tabPanel);
179
180 //Find a proper tmp disk space
181 tmpSDFFile = Utils.getTempFile("Denoptim_MolViewer_loadedMol.sdf");
182
183 }
184
185//-----------------------------------------------------------------------------
186
187 private void showPopup(MouseEvent e)
188 {
189 if (!e.isPopupTrigger() || item == null)
190 {
191 return;
192 }
193 // We take the list of potentially available properties
194 // from the SDF of the item.
195 IAtomContainer mol;
196 TreeMap<String,String> availableProps = new TreeMap<String,String>();
197 try {
199 new File(item.getSDFFile())).get(0);
200 } catch (Exception e1) {
201 return;
202 }
203 for (Object propRef : mol.getProperties().keySet())
204 {
205 String key = propRef.toString();
206 String val = key;
207 if (GUIPreferences.defualtSDFTags.keySet().contains(key))
208 {
209 val = GUIPreferences.defualtSDFTags.get(key);
210 }
211 availableProps.put(key,val);
212 }
213
214 dataTabPopMenu = new JPopupMenu("Add/Remove Rows");
215 for (Entry<String, String> entry : availableProps.entrySet())
216 {
217 JCheckBoxMenuItem mi = new JCheckBoxMenuItem(entry.getValue());
218 if (GUIPreferences.chosenSDFTags.contains(entry.getKey()))
219 {
220 mi.setSelected(true);
221 } else {
222 mi.setSelected(false);
223 }
224 mi.addActionListener(new ActionListener() {
225 public void actionPerformed(ActionEvent e) {
226 //At this point the menuItem is already selected
227 // You can use this to verify:
228 /*
229 for (Component c : dataTabPopMenu.getComponents())
230 {
231 if (c instanceof JCheckBoxMenuItem)
232 {
233 JCheckBoxMenuItem i = (JCheckBoxMenuItem) c;
234 System.out.println(" "+i.isSelected()+" "+i);
235 }
236 }
237 */
238 if (!mi.isSelected())
239 {
240 mi.setSelected(false);
241 GUIPreferences.chosenSDFTags.remove(entry.getKey());
242 } else {
243 mi.setSelected(true);
244 GUIPreferences.chosenSDFTags.add(entry.getKey());
245 }
246 fillDataTable(mol);
247 }
248 });
249 dataTabPopMenu.add(mi);
250 }
251 dataTabPopMenu.show(e.getComponent(), e.getX(), e.getY());
252 }
253
254//-----------------------------------------------------------------------------
255
260 public void enablePartialData(boolean enable)
261 {
262 toleratePartialData = enable;
263 }
264
265//-----------------------------------------------------------------------------
266
271 public void loadChemicalStructure(IAtomContainer mol)
272 {
273 try {
275 } catch (DENOPTIMException e) {
276 System.out.println("Could not write molecular representation to "
277 + "tmp file. Thus, could not load it into Jmol viewer.");
278 return;
279 }
280 File file = new File(tmpSDFFile);
282 }
283
284//-----------------------------------------------------------------------------
285
290 public void loadChemicalStructureFromFile(String pathName)
291 {
292 File file = new File(pathName);
294 }
295
296//-----------------------------------------------------------------------------
297
302 public void loadChemicalStructureFromFile(File file)
303 {
304 // WARNING! You might be tempted to use 'false' to really clear ('zap')
305 // the viewer from outdated content. However, that operation is extremely
306 // slow (2-3 seconds) and degrades user experience substantially.
307 // Instead, we keep outdated data and let the incoming data overwrite
308 // the old ones.
309 // In case of timing, note that the 'zap' script is executed in another
310 // thread, so the timing of the clear method does not reflect the actual
311 // time it takes to do the operation. The other thread dealing with the
312 // 'zap' command will, however, stop the execution of this thread when
313 // the latter asks viewer.isScriptExecuting() (see below in this method)
314 clearAll(true);
315
316 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
317 try {
318 item = DenoptimIO.readCandidates(file, false).get(0);
319 } catch (DENOPTIMException e) {
321 {
322 e.printStackTrace();
323 JOptionPane.showMessageDialog(this,
324 "<html>Could not load data from file <br>'" + file +"'.",
325 "Error",
326 JOptionPane.PLAIN_MESSAGE,
327 UIManager.getIcon("OptionPane.errorIcon"));
328 this.setCursor(Cursor.getPredefinedCursor(
329 Cursor.DEFAULT_CURSOR));
330 return;
331 }
332 else
333 {
334 try {
335 //WARNING: here we always ignore the fragment space because
336 // so far, there is no case where the fully defined graph
337 // is needed. We only need its string representation.
338
340 file).get(0),false,true);
341 } catch (Exception e1) {
342 try {
345 file).get(0), true);
346 } catch (Exception e2) {
347 e1.printStackTrace();
348 this.setCursor(Cursor.getPredefinedCursor(
349 Cursor.DEFAULT_CURSOR));
350 return;
351 }
352 }
353 }
354 }
355
356 fillDataTable(file);
357
358 // This is often needed to wait for the 'zap' script to finish.
359 while (jmolPanel.viewer.isScriptExecuting())
360 {
361 try
362 {
363 Thread.sleep(100);
364 } catch (InterruptedException e)
365 {
366 // should never happen
367 e.printStackTrace();
368 }
369 }
370 jmolPanel.viewer.openFile(file.getAbsolutePath());
371
373 this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
374 }
375
376//-----------------------------------------------------------------------------
377
382 private void fillDataTable(File molFile)
383 {
384 IAtomContainer mol = null;
385 if (molFile != null)
386 {
387 try {
388 mol = DenoptimIO.readAllAtomContainers(molFile).get(0);
389 } catch (Exception e) {
390 System.out.println("Could not read descriptors from '"
391 + molFile + "': "+e.getLocalizedMessage());
392 }
393 }
394 fillDataTable(mol);
395 }
396
397//-----------------------------------------------------------------------------
398
403 private void fillDataTable(IAtomContainer mol)
404 {
406
407 Set<String> fromDnMol = new HashSet<String>();
408 fromDnMol.add(CDKConstants.TITLE);
409 fromDnMol.add(DENOPTIMConstants.UNIQUEIDTAG);
410 fromDnMol.add(DENOPTIMConstants.FITNESSTAG);
411 fromDnMol.add(DENOPTIMConstants.MOLERRORTAG);
412 fromDnMol.add("Generation");
413 fromDnMol.add(DENOPTIMConstants.PROVENANCE);
414
415 TreeSet<String> chosen = GUIPreferences.chosenSDFTags;
416 Map<String,String> defPropMap = GUIPreferences.defualtSDFTags;
417
418 if (item.getName() != null && chosen.contains(CDKConstants.TITLE))
419 {
420 dataTabModel.addRow(new Object[] {
421 defPropMap.get(CDKConstants.TITLE),
422 item.getName() });
423 }
424 if (item.getUID() != null && chosen.contains(
426 {
427 dataTabModel.addRow(new Object[] {
428 defPropMap.get(DENOPTIMConstants.UNIQUEIDTAG),
429 item.getUID() });
430 }
431 if (item.hasFitness() && chosen.contains(
433 {
434 dataTabModel.addRow(new Object[]{
435 defPropMap.get(DENOPTIMConstants.FITNESSTAG),
436 item.getFitness()});
437 }
438 else
439 {
440 if (item.getError() != null && chosen.contains(
442 {
443 dataTabModel.addRow(new Object[] {
444 defPropMap.get(DENOPTIMConstants.MOLERRORTAG),
445 item.getError() });
446 }
447 }
448 if (item.getGeneration() > -1 && chosen.contains("Generation"))
449 {
450 dataTabModel.addRow(new Object[] {
451 defPropMap.get("Generation"),
452 item.getGeneration() });
453 }
454 if (item.getComments() != null && chosen.contains(
456 {
457 dataTabModel.addRow(new Object[] {
458 defPropMap.get(DENOPTIMConstants.PROVENANCE),
459 item.getComments() });
460 }
461
462 if (mol != null)
463 {
464 for (String key : GUIPreferences.chosenSDFTags)
465 {
466 Object p = mol.getProperty(key);
467 if (p == null || fromDnMol.contains(key))
468 {
469 continue;
470 }
471 dataTabModel.addRow(new Object[] {key, p.toString()});
472 }
473 }
474 }
475
476//-----------------------------------------------------------------------------
477
485 public void clearAll(boolean dataIsComing)
486 {
488 clearMolecularViewer(dataIsComing);
489 }
490
491//-----------------------------------------------------------------------------
492
496 public void cleardataTable()
497 {
498 int initRowCount = dataTabModel.getRowCount();
499 for (int i=0; i<initRowCount; i++)
500 {
501 //Always remove the first to avoid dealing with changing row ids
502 dataTabModel.removeRow(0);
503 }
504 }
505
506//-----------------------------------------------------------------------------
507
516 public void clearMolecularViewer(boolean dataIsComing)
517 {
518 if (!dataIsComing)
519 jmolPanel.viewer.evalString("zap");
520 }
521
522//-----------------------------------------------------------------------------
523
524 private void setJmolViewer()
525 {
526 StringBuilder sb = new StringBuilder();
527 sb.append("select none").append(NL);
528 sb.append("SelectionHalos ON").append(NL);
529 sb.append("set picking ATOMS").append(NL);
530 jmolPanel.viewer.evalString(sb.toString());
531 }
532
533//-----------------------------------------------------------------------------
534
535 /*
536 * This is needed to stop Jmol threads
537 */
538 public void dispose()
539 {
541 }
542
543//-----------------------------------------------------------------------------
544
545}
General set of constants used in DENOPTIM.
static final String PROVENANCE
SDF tag containing provenance data for a graph.
static final String UNIQUEIDTAG
SDF tag containing the unique identifier of a candidate.
static final String MOLERRORTAG
SDF tag containing errors during execution of molecule specific tasks.
static final String FITNESSTAG
SDF tag containing the fitness of a candidate.
A candidate is the combination of a denoptim graph with molecular representation and may include also...
Definition: Candidate.java:40
boolean hasFitness
Flag signaling the presence of a fitness value associated.
Definition: Candidate.java:89
static Candidate fromAtomContainerNoGraph(IAtomContainer iac, boolean allowNoUID)
Wraps an atom container as a candidate even if a graph is not available.
Definition: Candidate.java:301
int getGeneration()
The generation this candidate belong to is that in which it was generated.
Definition: Candidate.java:578
The collection of tunable preferences.
static TreeSet< String > chosenSDFTags
MolecularViewer: list of SDF tags specifying which properties to display.
static Map< String, String > defualtSDFTags
MolecularViewer: default list of SDF tags with corresponding string to display instead of tag.
A panel with a molecular viewer and data table.
void cleardataTable()
Clears the table of attachment points.
void clearMolecularViewer(boolean dataIsComing)
Clears the molecular viewer.
void enablePartialData(boolean enable)
Sets the behavior in case of request to visualize partial data.
void initialize(int dividerPosition)
void clearAll(boolean dataIsComing)
Removes the currently visualized molecule and AP table.
Candidate item
The currently loaded item.
void loadChemicalStructureFromFile(File file)
Loads a structure in the Jmol viewer.
void loadChemicalStructure(IAtomContainer mol)
Loads a structure in the Jmol viewer.
static final long serialVersionUID
Version UID.
void loadChemicalStructureFromFile(String pathName)
Loads a structure in the Jmol viewer.
boolean toleratePartialData
Flag controlling behavior in case of partial data (e.r., lack of fitness/error)
MoleculeViewPanel(int dividerPosition)
Constructor that allows to specify whether the data table is editable or not.
void fillDataTable(IAtomContainer mol)
static String getTempFile(String tmpFileName)
Returns the pathname to a tmp file.
Definition: Utils.java:36
Utility methods for input/output.
static ArrayList< Candidate > readCandidates(File file)
Reads SDF files that represent one or more tested candidates.
static void writeSDFFile(String fileName, IAtomContainer mol)
Writes IAtomContainer to SDF file.
static List< IAtomContainer > readAllAtomContainers(File file)
Returns a single collection with all atom containers found in a file of any format.