2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.analysis.scoremodels.ScoreModels;
24 import jalview.api.AlignViewportI;
25 import jalview.api.analysis.ScoreModelI;
26 import jalview.api.analysis.SimilarityParamsI;
27 import jalview.bin.Cache;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.AlignmentView;
31 import jalview.datamodel.HiddenColumns;
32 import jalview.datamodel.SequenceI;
33 import jalview.jbgui.GPCAPanel;
34 import jalview.math.RotatableMatrix.Axis;
35 import jalview.util.ImageMaker;
36 import jalview.util.MessageManager;
37 import jalview.viewmodel.AlignmentViewport;
38 import jalview.viewmodel.PCAModel;
40 import java.awt.BorderLayout;
41 import java.awt.Color;
42 import java.awt.Dimension;
43 import java.awt.Graphics;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.awt.print.PageFormat;
47 import java.awt.print.Printable;
48 import java.awt.print.PrinterException;
49 import java.awt.print.PrinterJob;
51 import javax.swing.ButtonGroup;
52 import javax.swing.JColorChooser;
53 import javax.swing.JMenuItem;
54 import javax.swing.JRadioButtonMenuItem;
55 import javax.swing.event.InternalFrameAdapter;
56 import javax.swing.event.InternalFrameEvent;
59 * The panel holding the Principal Component Analysis 3-D visualisation
61 public class PCAPanel extends GPCAPanel
62 implements Runnable, IProgressIndicator
64 private static final int MIN_WIDTH = 470;
66 private static final int MIN_HEIGHT = 250;
68 private RotatableCanvas rc;
74 private PCAModel pcaModel;
78 private IProgressIndicator progressBar;
80 private boolean working;
83 * Constructor given sequence data, a similarity (or distance) score model
84 * name, and score calculation parameters
90 public PCAPanel(AlignmentPanel alignPanel, String modelName,
91 SimilarityParamsI params)
94 this.av = alignPanel.av;
96 boolean nucleotide = av.getAlignment().isNucleotide();
98 progressBar = new ProgressBar(statusPanel, statusBar);
100 addInternalFrameListener(new InternalFrameAdapter()
103 public void internalFrameClosed(InternalFrameEvent e)
105 close_actionPerformed();
109 boolean selected = av.getSelectionGroup() != null
110 && av.getSelectionGroup().getSize() > 0;
111 AlignmentView seqstrings = av.getAlignmentView(selected);
115 seqs = av.getAlignment().getSequencesArray();
119 seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment());
122 ScoreModelI scoreModel = ScoreModels.getInstance()
123 .getScoreModel(modelName, ap);
124 setPcaModel(new PCAModel(seqstrings, seqs, nucleotide, scoreModel,
126 PaintRefresher.Register(this, av.getSequenceSetId());
128 setRotatableCanvas(new RotatableCanvas(alignPanel));
129 this.getContentPane().add(getRotatableCanvas(), BorderLayout.CENTER);
131 addKeyListener(getRotatableCanvas());
134 this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
138 * Ensure references to potentially very large objects (the PCA matrices) are
139 * nulled when the frame is closed
141 protected void close_actionPerformed()
147 protected void bgcolour_actionPerformed()
149 Color col = JColorChooser.showDialog(this,
150 MessageManager.getString("label.select_background_colour"),
151 getRotatableCanvas().getBgColour());
155 getRotatableCanvas().setBgColour(col);
157 getRotatableCanvas().repaint();
161 * Calculates the PCA and displays the results
167 long progId = System.currentTimeMillis();
168 IProgressIndicator progress = this;
169 String message = MessageManager.getString("label.pca_recalculating");
170 if (getParent() == null)
172 progress = ap.alignFrame;
173 message = MessageManager.getString("label.pca_calculating");
175 progress.setProgressBar(message, progId);
178 getPcaModel().calculate();
180 xCombobox.setSelectedIndex(0);
181 yCombobox.setSelectedIndex(1);
182 zCombobox.setSelectedIndex(2);
184 getPcaModel().updateRc(getRotatableCanvas());
186 setTop(getPcaModel().getTop());
188 } catch (OutOfMemoryError er)
190 new OOMWarning("calculating PCA", er);
195 progress.setProgressBar("", progId);
199 if (getParent() == null)
201 Desktop.addInternalFrame(this,
202 MessageManager.formatMessage("label.calc_title", "PCA",
203 getPcaModel().getScoreModelName()),
210 * Updates the PCA display after a change of component to use for x, y or z
214 protected void doDimensionChange()
221 int dim1 = getTop() - xCombobox.getSelectedIndex();
222 int dim2 = getTop() - yCombobox.getSelectedIndex();
223 int dim3 = getTop() - zCombobox.getSelectedIndex();
224 getPcaModel().updateRcView(dim1, dim2, dim3);
225 getRotatableCanvas().resetView();
229 * Sets the selected checkbox item index for PCA dimension (1, 2, 3...) for
230 * the given axis (X/Y/Z)
235 public void setSelectedDimensionIndex(int index, Axis axis)
240 xCombobox.setSelectedIndex(index);
243 yCombobox.setSelectedIndex(index);
246 zCombobox.setSelectedIndex(index);
253 protected void outputValues_actionPerformed()
255 CutAndPasteTransfer cap = new CutAndPasteTransfer();
258 cap.setText(getPcaModel().getDetails());
259 Desktop.addInternalFrame(cap,
260 MessageManager.getString("label.pca_details"), 500, 500);
261 } catch (OutOfMemoryError oom)
263 new OOMWarning("opening PCA details", oom);
269 protected void showLabels_actionPerformed()
271 getRotatableCanvas().showLabels(showLabels.getState());
275 protected void print_actionPerformed()
277 PCAPrinter printer = new PCAPrinter();
282 * If available, shows the data which formed the inputs for the PCA as a new
286 public void originalSeqData_actionPerformed()
288 // JAL-2647 disabled after load from project (until save to project done)
289 if (getPcaModel().getInputData() == null)
292 "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
295 // decide if av alignment is sufficiently different to original data to
296 // warrant a new window to be created
297 // create new alignment window with hidden regions (unhiding hidden regions
298 // yields unaligned seqs)
299 // or create a selection box around columns in alignment view
300 // test Alignment(SeqCigar[])
304 // we try to get the associated view's gap character
305 // but this may fail if the view was closed...
306 gc = av.getGapCharacter();
307 } catch (Exception ex)
311 Object[] alAndColsel = getPcaModel().getInputData()
312 .getAlignmentAndHiddenColumns(gc);
314 if (alAndColsel != null && alAndColsel[0] != null)
316 // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
318 AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
319 AlignmentI dataset = (av != null && av.getAlignment() != null)
320 ? av.getAlignment().getDataset()
324 al.setDataset(dataset);
330 AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1],
331 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
333 // >>>This is a fix for the moment, until a better solution is
335 // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
337 // af.addSortByOrderMenuItem(ServiceName + " Ordering",
340 Desktop.addInternalFrame(af, MessageManager.formatMessage(
341 "label.original_data_for_params", new String[]
342 { this.title }), AlignFrame.DEFAULT_WIDTH,
343 AlignFrame.DEFAULT_HEIGHT);
347 * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i <
348 * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 +
349 * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] +
352 * Desktop.addInternalFrame(cap, "Original Data", 400, 400);
356 class PCAPrinter extends Thread implements Printable
361 PrinterJob printJob = PrinterJob.getPrinterJob();
362 PageFormat defaultPage = printJob.defaultPage();
363 PageFormat pf = printJob.pageDialog(defaultPage);
365 if (defaultPage == pf)
373 printJob.setPrintable(this, pf);
375 if (printJob.printDialog())
380 } catch (Exception PrintException)
382 PrintException.printStackTrace();
388 public int print(Graphics pg, PageFormat pf, int pi)
389 throws PrinterException
391 pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
393 getRotatableCanvas().drawBackground(pg);
394 getRotatableCanvas().drawScene(pg);
395 if (getRotatableCanvas().drawAxes)
397 getRotatableCanvas().drawAxes(pg);
402 return Printable.PAGE_EXISTS;
406 return Printable.NO_SUCH_PAGE;
412 * Handler for 'Save as EPS' option
415 protected void eps_actionPerformed()
417 makePCAImage(ImageMaker.TYPE.EPS);
421 * Handler for 'Save as PNG' option
424 protected void png_actionPerformed()
426 makePCAImage(ImageMaker.TYPE.PNG);
429 void makePCAImage(ImageMaker.TYPE type)
431 int width = getRotatableCanvas().getWidth();
432 int height = getRotatableCanvas().getHeight();
439 im = new ImageMaker(this, ImageMaker.TYPE.PNG,
440 "Make PNG image from PCA", width, height, null, null, null, 0,
444 im = new ImageMaker(this, ImageMaker.TYPE.EPS,
445 "Make EPS file from PCA", width, height, null,
446 this.getTitle(), null, 0, false);
449 im = new ImageMaker(this, ImageMaker.TYPE.SVG,
450 "Make SVG file from PCA", width, height, null,
451 this.getTitle(), null, 0, false);
454 if (im.getGraphics() != null)
456 getRotatableCanvas().drawBackground(im.getGraphics());
457 getRotatableCanvas().drawScene(im.getGraphics());
458 if (getRotatableCanvas().drawAxes)
460 getRotatableCanvas().drawAxes(im.getGraphics());
467 protected void viewMenu_menuSelected()
469 buildAssociatedViewMenu();
473 * Builds the menu showing the choice of possible views (for the associated
474 * sequence data) to which the PCA may be linked
476 void buildAssociatedViewMenu()
478 AlignmentPanel[] aps = PaintRefresher
479 .getAssociatedPanels(av.getSequenceSetId());
480 if (aps.length == 1 && getRotatableCanvas().av == aps[0].av)
482 associateViewsMenu.setVisible(false);
486 associateViewsMenu.setVisible(true);
489 .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
491 viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
494 associateViewsMenu.removeAll();
496 JRadioButtonMenuItem item;
497 ButtonGroup buttonGroup = new ButtonGroup();
498 int iSize = aps.length;
500 for (int i = 0; i < iSize; i++)
502 final AlignmentPanel panel = aps[i];
503 item = new JRadioButtonMenuItem(panel.av.getViewName(),
504 panel.av == getRotatableCanvas().av);
505 buttonGroup.add(item);
506 item.addActionListener(new ActionListener()
509 public void actionPerformed(ActionEvent evt)
511 selectAssociatedView(panel);
515 associateViewsMenu.add(item);
518 final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
521 buttonGroup.add(itemf);
523 itemf.setSelected(getRotatableCanvas().isApplyToAllViews());
524 itemf.addActionListener(new ActionListener()
527 public void actionPerformed(ActionEvent evt)
529 getRotatableCanvas().setApplyToAllViews(itemf.isSelected());
532 associateViewsMenu.add(itemf);
540 * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
544 protected void outputPoints_actionPerformed()
546 CutAndPasteTransfer cap = new CutAndPasteTransfer();
549 cap.setText(getPcaModel().getPointsasCsv(false,
550 xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
551 zCombobox.getSelectedIndex()));
552 Desktop.addInternalFrame(cap, MessageManager
553 .formatMessage("label.points_for_params", new String[]
554 { this.getTitle() }), 500, 500);
555 } catch (OutOfMemoryError oom)
557 new OOMWarning("exporting PCA points", oom);
566 * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
570 protected void outputProjPoints_actionPerformed()
572 CutAndPasteTransfer cap = new CutAndPasteTransfer();
575 cap.setText(getPcaModel().getPointsasCsv(true,
576 xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
577 zCombobox.getSelectedIndex()));
578 Desktop.addInternalFrame(cap, MessageManager.formatMessage(
579 "label.transformed_points_for_params", new String[]
580 { this.getTitle() }), 500, 500);
581 } catch (OutOfMemoryError oom)
583 new OOMWarning("exporting transformed PCA points", oom);
591 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
594 public void setProgressBar(String message, long id)
596 progressBar.setProgressBar(message, id);
597 // if (progressBars == null)
599 // progressBars = new Hashtable();
600 // progressBarHandlers = new Hashtable();
603 // JPanel progressPanel;
604 // Long lId = new Long(id);
605 // GridLayout layout = (GridLayout) statusPanel.getLayout();
606 // if (progressBars.get(lId) != null)
608 // progressPanel = (JPanel) progressBars.get(new Long(id));
609 // statusPanel.remove(progressPanel);
610 // progressBars.remove(lId);
611 // progressPanel = null;
612 // if (message != null)
614 // statusBar.setText(message);
616 // if (progressBarHandlers.contains(lId))
618 // progressBarHandlers.remove(lId);
620 // layout.setRows(layout.getRows() - 1);
624 // progressPanel = new JPanel(new BorderLayout(10, 5));
626 // JProgressBar progressBar = new JProgressBar();
627 // progressBar.setIndeterminate(true);
629 // progressPanel.add(new JLabel(message), BorderLayout.WEST);
630 // progressPanel.add(progressBar, BorderLayout.CENTER);
632 // layout.setRows(layout.getRows() + 1);
633 // statusPanel.add(progressPanel);
635 // progressBars.put(lId, progressPanel);
638 // // setMenusForViewport();
643 public void registerHandler(final long id,
644 final IProgressIndicatorHandler handler)
646 progressBar.registerHandler(id, handler);
647 // if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
650 // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
652 // progressBarHandlers.put(new Long(id), handler);
653 // final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
654 // if (handler.canCancel())
656 // JButton cancel = new JButton(
657 // MessageManager.getString("action.cancel"));
658 // final IProgressIndicator us = this;
659 // cancel.addActionListener(new ActionListener()
663 // public void actionPerformed(ActionEvent e)
665 // handler.cancelActivity(id);
666 // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
667 // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
670 // progressPanel.add(cancel, BorderLayout.EAST);
676 * @return true if any progress bars are still active
679 public boolean operationInProgress()
681 return progressBar.operationInProgress();
685 protected void resetButton_actionPerformed()
688 setTop(0); // ugly - prevents dimensionChanged events from being processed
689 xCombobox.setSelectedIndex(0);
690 yCombobox.setSelectedIndex(1);
692 zCombobox.setSelectedIndex(2);
696 * Answers true if PCA calculation is in progress, else false
700 public boolean isWorking()
706 * Answers the selected checkbox item index for PCA dimension for the X, Y or
707 * Z axis of the display
712 public int getSelectedDimensionIndex(Axis axis)
717 return xCombobox.getSelectedIndex();
719 return yCombobox.getSelectedIndex();
721 return zCombobox.getSelectedIndex();
725 public void setShowLabels(boolean show)
727 showLabels.setSelected(show);
731 * Sets the input data used to calculate the PCA. This is provided for
732 * 'restore from project', which does not currently support this (AL-2647), so
733 * sets the value to null, and hides the menu option for "Input Data...". J
737 public void setInputData(AlignmentView data)
739 getPcaModel().setInputData(data);
740 originalSeqData.setVisible(data != null);
743 public AlignViewportI getAlignViewport()
748 public PCAModel getPcaModel()
753 public void setPcaModel(PCAModel pcaModel)
755 this.pcaModel = pcaModel;
758 public RotatableCanvas getRotatableCanvas()
763 public void setRotatableCanvas(RotatableCanvas rc)
773 public void setTop(int top)
779 * set the associated view for this PCA.
783 public void selectAssociatedView(AlignmentPanel panel)
785 getRotatableCanvas().setApplyToAllViews(false);
790 getRotatableCanvas().av = panel.av;
791 getRotatableCanvas().ap = panel;
792 PaintRefresher.Register(PCAPanel.this, panel.av.getSequenceSetId());