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.analysis.scoremodels.SimilarityParams;
25 import jalview.api.analysis.ScoreModelI;
26 import jalview.api.analysis.SimilarityParamsI;
27 import jalview.api.analysis.ViewBasedAnalysisI;
28 import jalview.bin.Cache;
29 import jalview.datamodel.Alignment;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.AlignmentView;
32 import jalview.datamodel.ColumnSelection;
33 import jalview.datamodel.SeqCigar;
34 import jalview.datamodel.SequenceI;
35 import jalview.jbgui.GPCAPanel;
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.Graphics;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.print.PageFormat;
46 import java.awt.print.Printable;
47 import java.awt.print.PrinterException;
48 import java.awt.print.PrinterJob;
50 import javax.swing.ButtonGroup;
51 import javax.swing.JCheckBoxMenuItem;
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;
64 public class PCAPanel extends GPCAPanel implements Runnable,
68 private IProgressIndicator progressBar;
81 * Creates a new PCAPanel object using default score model and parameters
85 public PCAPanel(AlignmentPanel alignPanel)
87 this(alignPanel, ScoreModels.getInstance().getDefaultModel(
88 !alignPanel.av.getAlignment().isNucleotide()),
89 SimilarityParams.SeqSpace);
93 * Constructor given sequence data, a similarity (or distance) score model,
94 * and score calculation parameters
100 public PCAPanel(AlignmentPanel alignPanel, ScoreModelI scoreModel,
101 SimilarityParamsI params)
104 this.av = alignPanel.av;
105 this.ap = alignPanel;
106 boolean nucleotide = av.getAlignment().isNucleotide();
108 progressBar = new ProgressBar(statusPanel, statusBar);
110 addInternalFrameListener(new InternalFrameAdapter()
113 public void internalFrameClosed(InternalFrameEvent e)
115 close_actionPerformed();
119 boolean selected = av.getSelectionGroup() != null
120 && av.getSelectionGroup().getSize() > 0;
121 AlignmentView seqstrings = av.getAlignmentView(selected);
125 seqs = av.getAlignment().getSequencesArray();
129 seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment());
132 // TODO can we allow PCA on unaligned data given choice of
133 // similarity measure parameters?
134 if (!checkAligned(seqstrings))
136 JvOptionPane.showMessageDialog(Desktop.desktop,
137 MessageManager.getString("label.pca_sequences_not_aligned"),
138 MessageManager.getString("label.sequences_not_aligned"),
139 JvOptionPane.WARNING_MESSAGE);
144 pcaModel = new PCAModel(seqstrings, seqs, nucleotide, scoreModel,
146 PaintRefresher.Register(this, av.getSequenceSetId());
148 rc = new RotatableCanvas(alignPanel);
149 this.getContentPane().add(rc, BorderLayout.CENTER);
150 Thread worker = new Thread(this);
155 * Answers true if all sequences have the same aligned length, else false
160 protected boolean checkAligned(AlignmentView seqstrings)
162 SeqCigar sq[] = seqstrings.getSequences();
163 int length = sq[0].getWidth();
164 boolean sameLength = true;
165 for (int i = 0; i < sq.length; i++)
167 if (sq[i].getWidth() != length)
177 * Ensure references to potentially very large objects (the PCA matrices) are
178 * nulled when the frame is closed
180 protected void close_actionPerformed()
186 * Repopulate the options and actions under the score model menu when it is
187 * selected. Options will depend on whether 'nucleotide' or 'peptide'
188 * modelling is selected (and also possibly on whether any additional score
189 * models have been added).
192 protected void scoreModel_menuSelected()
194 scoreModelMenu.removeAll();
195 for (final ScoreModelI sm : ScoreModels.getInstance().getModels())
197 final String name = sm.getName();
198 JCheckBoxMenuItem jm = new JCheckBoxMenuItem(name);
201 * if the score model doesn't provide a description, try to look one
202 * up in the text bundle, falling back on its name
204 String tooltip = sm.getDescription();
207 tooltip = MessageManager.getStringOrReturn("label.score_model_",
210 jm.setToolTipText(tooltip);
211 jm.setSelected(pcaModel.getScoreModelName().equals(name));
212 if ((pcaModel.isNucleotide() && sm.isDNA())
213 || (!pcaModel.isNucleotide() && sm.isProtein()))
215 jm.addActionListener(new ActionListener()
218 public void actionPerformed(ActionEvent e)
220 if (!pcaModel.getScoreModelName().equals(name))
222 ScoreModelI sm2 = configureScoreModel(sm);
223 pcaModel.setScoreModel(sm2);
224 Thread worker = new Thread(PCAPanel.this);
229 scoreModelMenu.add(jm);
235 public void bgcolour_actionPerformed(ActionEvent e)
237 Color col = JColorChooser.showDialog(this,
238 MessageManager.getString("label.select_background_colour"),
254 long progId = System.currentTimeMillis();
255 IProgressIndicator progress = this;
256 String message = MessageManager.getString("label.pca_recalculating");
257 if (getParent() == null)
259 progress = ap.alignFrame;
260 message = MessageManager.getString("label.pca_calculating");
262 progress.setProgressBar(message, progId);
265 calcSettings.setEnabled(false);
268 xCombobox.setSelectedIndex(0);
269 yCombobox.setSelectedIndex(1);
270 zCombobox.setSelectedIndex(2);
272 pcaModel.updateRc(rc);
274 nuclSetting.setSelected(pcaModel.isNucleotide());
275 protSetting.setSelected(!pcaModel.isNucleotide());
276 top = pcaModel.getTop();
278 } catch (OutOfMemoryError er)
280 new OOMWarning("calculating PCA", er);
284 progress.setProgressBar("", progId);
286 calcSettings.setEnabled(true);
288 if (getParent() == null)
291 Desktop.addInternalFrame(this, MessageManager
292 .getString("label.principal_component_analysis"), 475, 450);
297 protected void nuclSetting_actionPerfomed(ActionEvent arg0)
299 if (!pcaModel.isNucleotide())
301 pcaModel.setNucleotide(true);
302 pcaModel.setScoreModel(ScoreModels.getInstance().getDefaultModel(
304 Thread worker = new Thread(this);
311 protected void protSetting_actionPerfomed(ActionEvent arg0)
314 if (pcaModel.isNucleotide())
316 pcaModel.setNucleotide(false);
317 pcaModel.setScoreModel(ScoreModels.getInstance()
318 .getDefaultModel(true));
319 Thread worker = new Thread(this);
327 void doDimensionChange()
334 int dim1 = top - xCombobox.getSelectedIndex();
335 int dim2 = top - yCombobox.getSelectedIndex();
336 int dim3 = top - zCombobox.getSelectedIndex();
337 pcaModel.updateRcView(dim1, dim2, dim3);
339 rc.rotmat.setIdentity();
341 rc.paint(rc.getGraphics());
351 protected void xCombobox_actionPerformed(ActionEvent e)
363 protected void yCombobox_actionPerformed(ActionEvent e)
375 protected void zCombobox_actionPerformed(ActionEvent e)
381 public void outputValues_actionPerformed(ActionEvent e)
383 CutAndPasteTransfer cap = new CutAndPasteTransfer();
386 cap.setText(pcaModel.getDetails());
387 Desktop.addInternalFrame(cap,
388 MessageManager.getString("label.pca_details"), 500, 500);
389 } catch (OutOfMemoryError oom)
391 new OOMWarning("opening PCA details", oom);
397 public void showLabels_actionPerformed(ActionEvent e)
399 rc.showLabels(showLabels.getState());
403 public void print_actionPerformed(ActionEvent e)
405 PCAPrinter printer = new PCAPrinter();
410 public void originalSeqData_actionPerformed(ActionEvent e)
412 // this was cut'n'pasted from the equivalent TreePanel method - we should
413 // make this an abstract function of all jalview analysis windows
414 if (pcaModel.getSeqtrings() == null)
416 jalview.bin.Cache.log
417 .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
420 // decide if av alignment is sufficiently different to original data to
421 // warrant a new window to be created
422 // create new alignmnt window with hidden regions (unhiding hidden regions
423 // yields unaligned seqs)
424 // or create a selection box around columns in alignment view
425 // test Alignment(SeqCigar[])
429 // we try to get the associated view's gap character
430 // but this may fail if the view was closed...
431 gc = av.getGapCharacter();
432 } catch (Exception ex)
436 Object[] alAndColsel = pcaModel.getSeqtrings()
437 .getAlignmentAndColumnSelection(gc);
439 if (alAndColsel != null && alAndColsel[0] != null)
441 // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
443 AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
444 AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
445 .getAlignment().getDataset() : null;
448 al.setDataset(dataset);
454 AlignFrame af = new AlignFrame(al,
455 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
456 AlignFrame.DEFAULT_HEIGHT);
458 // >>>This is a fix for the moment, until a better solution is
460 // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
462 // af.addSortByOrderMenuItem(ServiceName + " Ordering",
465 Desktop.addInternalFrame(af, MessageManager.formatMessage(
466 "label.original_data_for_params",
467 new String[] { this.title }), AlignFrame.DEFAULT_WIDTH,
468 AlignFrame.DEFAULT_HEIGHT);
472 * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i <
473 * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 +
474 * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] +
477 * Desktop.addInternalFrame(cap, "Original Data", 400, 400);
481 class PCAPrinter extends Thread implements Printable
486 PrinterJob printJob = PrinterJob.getPrinterJob();
487 PageFormat pf = printJob.pageDialog(printJob.defaultPage());
489 printJob.setPrintable(this, pf);
491 if (printJob.printDialog())
496 } catch (Exception PrintException)
498 PrintException.printStackTrace();
504 public int print(Graphics pg, PageFormat pf, int pi)
505 throws PrinterException
507 pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
509 rc.drawBackground(pg, rc.bgColour);
511 if (rc.drawAxes == true)
518 return Printable.PAGE_EXISTS;
522 return Printable.NO_SUCH_PAGE;
534 public void eps_actionPerformed(ActionEvent e)
536 makePCAImage(jalview.util.ImageMaker.TYPE.EPS);
546 public void png_actionPerformed(ActionEvent e)
548 makePCAImage(jalview.util.ImageMaker.TYPE.PNG);
551 void makePCAImage(jalview.util.ImageMaker.TYPE type)
553 int width = rc.getWidth();
554 int height = rc.getHeight();
556 jalview.util.ImageMaker im;
558 if (type == jalview.util.ImageMaker.TYPE.PNG)
560 im = new jalview.util.ImageMaker(this,
561 jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from PCA",
562 width, height, null, null, null, 0, false);
564 else if (type == jalview.util.ImageMaker.TYPE.EPS)
566 im = new jalview.util.ImageMaker(this,
567 jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from PCA",
568 width, height, null, this.getTitle(), null, 0, false);
572 im = new jalview.util.ImageMaker(this,
573 jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
574 width, height, null, this.getTitle(), null, 0, false);
578 if (im.getGraphics() != null)
580 rc.drawBackground(im.getGraphics(), Color.black);
581 rc.drawScene(im.getGraphics());
582 if (rc.drawAxes == true)
584 rc.drawAxes(im.getGraphics());
591 public void viewMenu_menuSelected()
593 buildAssociatedViewMenu();
596 void buildAssociatedViewMenu()
598 AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av
599 .getSequenceSetId());
600 if (aps.length == 1 && rc.av == aps[0].av)
602 associateViewsMenu.setVisible(false);
606 associateViewsMenu.setVisible(true);
608 if ((viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
610 viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
613 associateViewsMenu.removeAll();
615 JRadioButtonMenuItem item;
616 ButtonGroup buttonGroup = new ButtonGroup();
617 int i, iSize = aps.length;
618 final PCAPanel thisPCAPanel = this;
619 for (i = 0; i < iSize; i++)
621 final AlignmentPanel ap = aps[i];
622 item = new JRadioButtonMenuItem(ap.av.viewName, ap.av == rc.av);
623 buttonGroup.add(item);
624 item.addActionListener(new ActionListener()
627 public void actionPerformed(ActionEvent evt)
629 rc.applyToAllViews = false;
632 PaintRefresher.Register(thisPCAPanel, ap.av.getSequenceSetId());
636 associateViewsMenu.add(item);
639 final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");
641 buttonGroup.add(itemf);
643 itemf.setSelected(rc.applyToAllViews);
644 itemf.addActionListener(new ActionListener()
647 public void actionPerformed(ActionEvent evt)
649 rc.applyToAllViews = itemf.isSelected();
652 associateViewsMenu.add(itemf);
660 * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
664 protected void outputPoints_actionPerformed(ActionEvent e)
666 CutAndPasteTransfer cap = new CutAndPasteTransfer();
669 cap.setText(pcaModel.getPointsasCsv(false,
670 xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
671 zCombobox.getSelectedIndex()));
672 Desktop.addInternalFrame(cap, MessageManager.formatMessage(
673 "label.points_for_params", new String[] { this.getTitle() }),
675 } catch (OutOfMemoryError oom)
677 new OOMWarning("exporting PCA points", oom);
686 * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
690 protected void outputProjPoints_actionPerformed(ActionEvent e)
692 CutAndPasteTransfer cap = new CutAndPasteTransfer();
695 cap.setText(pcaModel.getPointsasCsv(true,
696 xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
697 zCombobox.getSelectedIndex()));
698 Desktop.addInternalFrame(cap, MessageManager.formatMessage(
699 "label.transformed_points_for_params",
700 new String[] { this.getTitle() }), 500, 500);
701 } catch (OutOfMemoryError oom)
703 new OOMWarning("exporting transformed PCA points", oom);
711 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
714 public void setProgressBar(String message, long id)
716 progressBar.setProgressBar(message, id);
717 // if (progressBars == null)
719 // progressBars = new Hashtable();
720 // progressBarHandlers = new Hashtable();
723 // JPanel progressPanel;
724 // Long lId = new Long(id);
725 // GridLayout layout = (GridLayout) statusPanel.getLayout();
726 // if (progressBars.get(lId) != null)
728 // progressPanel = (JPanel) progressBars.get(new Long(id));
729 // statusPanel.remove(progressPanel);
730 // progressBars.remove(lId);
731 // progressPanel = null;
732 // if (message != null)
734 // statusBar.setText(message);
736 // if (progressBarHandlers.contains(lId))
738 // progressBarHandlers.remove(lId);
740 // layout.setRows(layout.getRows() - 1);
744 // progressPanel = new JPanel(new BorderLayout(10, 5));
746 // JProgressBar progressBar = new JProgressBar();
747 // progressBar.setIndeterminate(true);
749 // progressPanel.add(new JLabel(message), BorderLayout.WEST);
750 // progressPanel.add(progressBar, BorderLayout.CENTER);
752 // layout.setRows(layout.getRows() + 1);
753 // statusPanel.add(progressPanel);
755 // progressBars.put(lId, progressPanel);
758 // // setMenusForViewport();
763 public void registerHandler(final long id,
764 final IProgressIndicatorHandler handler)
766 progressBar.registerHandler(id, handler);
767 // if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
770 // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
772 // progressBarHandlers.put(new Long(id), handler);
773 // final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
774 // if (handler.canCancel())
776 // JButton cancel = new JButton(
777 // MessageManager.getString("action.cancel"));
778 // final IProgressIndicator us = this;
779 // cancel.addActionListener(new ActionListener()
783 // public void actionPerformed(ActionEvent e)
785 // handler.cancelActivity(id);
786 // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
787 // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
790 // progressPanel.add(cancel, BorderLayout.EAST);
796 * @return true if any progress bars are still active
799 public boolean operationInProgress()
801 return progressBar.operationInProgress();
805 protected void resetButton_actionPerformed(ActionEvent e)
808 top = 0; // ugly - prevents dimensionChanged events from being processed
809 xCombobox.setSelectedIndex(0);
810 yCombobox.setSelectedIndex(1);
812 zCombobox.setSelectedIndex(2);
816 * If the score model is one that requires to get state data from the current
817 * view, allow it to do so
822 protected ScoreModelI configureScoreModel(ScoreModelI sm)
824 if (sm instanceof ViewBasedAnalysisI)
828 sm = sm.getClass().newInstance();
829 ((ViewBasedAnalysisI) sm).configureFromAlignmentView(ap);
830 } catch (Exception q)
832 Cache.log.error("Couldn't create a scoremodel instance for "