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 jvVersionSetting.setSelected(pcaModel.isJvCalcMode());
277 top = pcaModel.getTop();
279 } catch (OutOfMemoryError er)
281 new OOMWarning("calculating PCA", er);
285 progress.setProgressBar("", progId);
287 calcSettings.setEnabled(true);
289 if (getParent() == null)
292 Desktop.addInternalFrame(this, MessageManager
293 .getString("label.principal_component_analysis"), 475, 450);
298 protected void nuclSetting_actionPerfomed(ActionEvent arg0)
300 if (!pcaModel.isNucleotide())
302 pcaModel.setNucleotide(true);
303 pcaModel.setScoreModel(ScoreModels.getInstance().getDefaultModel(
305 Thread worker = new Thread(this);
312 protected void protSetting_actionPerfomed(ActionEvent arg0)
315 if (pcaModel.isNucleotide())
317 pcaModel.setNucleotide(false);
318 pcaModel.setScoreModel(ScoreModels.getInstance()
319 .getDefaultModel(true));
320 Thread worker = new Thread(this);
326 protected void jvVersionSetting_actionPerfomed(ActionEvent arg0)
328 pcaModel.setJvCalcMode(jvVersionSetting.isSelected());
329 Thread worker = new Thread(this);
336 void doDimensionChange()
343 int dim1 = top - xCombobox.getSelectedIndex();
344 int dim2 = top - yCombobox.getSelectedIndex();
345 int dim3 = top - zCombobox.getSelectedIndex();
346 pcaModel.updateRcView(dim1, dim2, dim3);
348 rc.rotmat.setIdentity();
350 rc.paint(rc.getGraphics());
360 protected void xCombobox_actionPerformed(ActionEvent e)
372 protected void yCombobox_actionPerformed(ActionEvent e)
384 protected void zCombobox_actionPerformed(ActionEvent e)
390 public void outputValues_actionPerformed(ActionEvent e)
392 CutAndPasteTransfer cap = new CutAndPasteTransfer();
395 cap.setText(pcaModel.getDetails());
396 Desktop.addInternalFrame(cap,
397 MessageManager.getString("label.pca_details"), 500, 500);
398 } catch (OutOfMemoryError oom)
400 new OOMWarning("opening PCA details", oom);
406 public void showLabels_actionPerformed(ActionEvent e)
408 rc.showLabels(showLabels.getState());
412 public void print_actionPerformed(ActionEvent e)
414 PCAPrinter printer = new PCAPrinter();
419 public void originalSeqData_actionPerformed(ActionEvent e)
421 // this was cut'n'pasted from the equivalent TreePanel method - we should
422 // make this an abstract function of all jalview analysis windows
423 if (pcaModel.getSeqtrings() == null)
425 jalview.bin.Cache.log
426 .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
429 // decide if av alignment is sufficiently different to original data to
430 // warrant a new window to be created
431 // create new alignmnt window with hidden regions (unhiding hidden regions
432 // yields unaligned seqs)
433 // or create a selection box around columns in alignment view
434 // test Alignment(SeqCigar[])
438 // we try to get the associated view's gap character
439 // but this may fail if the view was closed...
440 gc = av.getGapCharacter();
441 } catch (Exception ex)
445 Object[] alAndColsel = pcaModel.getSeqtrings()
446 .getAlignmentAndColumnSelection(gc);
448 if (alAndColsel != null && alAndColsel[0] != null)
450 // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
452 AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
453 AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
454 .getAlignment().getDataset() : null;
457 al.setDataset(dataset);
463 AlignFrame af = new AlignFrame(al,
464 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
465 AlignFrame.DEFAULT_HEIGHT);
467 // >>>This is a fix for the moment, until a better solution is
469 // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
471 // af.addSortByOrderMenuItem(ServiceName + " Ordering",
474 Desktop.addInternalFrame(af, MessageManager.formatMessage(
475 "label.original_data_for_params",
476 new String[] { this.title }), AlignFrame.DEFAULT_WIDTH,
477 AlignFrame.DEFAULT_HEIGHT);
481 * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i <
482 * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 +
483 * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] +
486 * Desktop.addInternalFrame(cap, "Original Data", 400, 400);
490 class PCAPrinter extends Thread implements Printable
495 PrinterJob printJob = PrinterJob.getPrinterJob();
496 PageFormat pf = printJob.pageDialog(printJob.defaultPage());
498 printJob.setPrintable(this, pf);
500 if (printJob.printDialog())
505 } catch (Exception PrintException)
507 PrintException.printStackTrace();
513 public int print(Graphics pg, PageFormat pf, int pi)
514 throws PrinterException
516 pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
518 rc.drawBackground(pg, rc.bgColour);
520 if (rc.drawAxes == true)
527 return Printable.PAGE_EXISTS;
531 return Printable.NO_SUCH_PAGE;
543 public void eps_actionPerformed(ActionEvent e)
545 makePCAImage(jalview.util.ImageMaker.TYPE.EPS);
555 public void png_actionPerformed(ActionEvent e)
557 makePCAImage(jalview.util.ImageMaker.TYPE.PNG);
560 void makePCAImage(jalview.util.ImageMaker.TYPE type)
562 int width = rc.getWidth();
563 int height = rc.getHeight();
565 jalview.util.ImageMaker im;
567 if (type == jalview.util.ImageMaker.TYPE.PNG)
569 im = new jalview.util.ImageMaker(this,
570 jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from PCA",
571 width, height, null, null, null, 0, false);
573 else if (type == jalview.util.ImageMaker.TYPE.EPS)
575 im = new jalview.util.ImageMaker(this,
576 jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from PCA",
577 width, height, null, this.getTitle(), null, 0, false);
581 im = new jalview.util.ImageMaker(this,
582 jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
583 width, height, null, this.getTitle(), null, 0, false);
587 if (im.getGraphics() != null)
589 rc.drawBackground(im.getGraphics(), Color.black);
590 rc.drawScene(im.getGraphics());
591 if (rc.drawAxes == true)
593 rc.drawAxes(im.getGraphics());
600 public void viewMenu_menuSelected()
602 buildAssociatedViewMenu();
605 void buildAssociatedViewMenu()
607 AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av
608 .getSequenceSetId());
609 if (aps.length == 1 && rc.av == aps[0].av)
611 associateViewsMenu.setVisible(false);
615 associateViewsMenu.setVisible(true);
617 if ((viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
619 viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
622 associateViewsMenu.removeAll();
624 JRadioButtonMenuItem item;
625 ButtonGroup buttonGroup = new ButtonGroup();
626 int i, iSize = aps.length;
627 final PCAPanel thisPCAPanel = this;
628 for (i = 0; i < iSize; i++)
630 final AlignmentPanel ap = aps[i];
631 item = new JRadioButtonMenuItem(ap.av.viewName, ap.av == rc.av);
632 buttonGroup.add(item);
633 item.addActionListener(new ActionListener()
636 public void actionPerformed(ActionEvent evt)
638 rc.applyToAllViews = false;
641 PaintRefresher.Register(thisPCAPanel, ap.av.getSequenceSetId());
645 associateViewsMenu.add(item);
648 final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");
650 buttonGroup.add(itemf);
652 itemf.setSelected(rc.applyToAllViews);
653 itemf.addActionListener(new ActionListener()
656 public void actionPerformed(ActionEvent evt)
658 rc.applyToAllViews = itemf.isSelected();
661 associateViewsMenu.add(itemf);
669 * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
673 protected void outputPoints_actionPerformed(ActionEvent e)
675 CutAndPasteTransfer cap = new CutAndPasteTransfer();
678 cap.setText(pcaModel.getPointsasCsv(false,
679 xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
680 zCombobox.getSelectedIndex()));
681 Desktop.addInternalFrame(cap, MessageManager.formatMessage(
682 "label.points_for_params", new String[] { this.getTitle() }),
684 } catch (OutOfMemoryError oom)
686 new OOMWarning("exporting PCA points", oom);
695 * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
699 protected void outputProjPoints_actionPerformed(ActionEvent e)
701 CutAndPasteTransfer cap = new CutAndPasteTransfer();
704 cap.setText(pcaModel.getPointsasCsv(true,
705 xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
706 zCombobox.getSelectedIndex()));
707 Desktop.addInternalFrame(cap, MessageManager.formatMessage(
708 "label.transformed_points_for_params",
709 new String[] { this.getTitle() }), 500, 500);
710 } catch (OutOfMemoryError oom)
712 new OOMWarning("exporting transformed PCA points", oom);
720 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
723 public void setProgressBar(String message, long id)
725 progressBar.setProgressBar(message, id);
726 // if (progressBars == null)
728 // progressBars = new Hashtable();
729 // progressBarHandlers = new Hashtable();
732 // JPanel progressPanel;
733 // Long lId = new Long(id);
734 // GridLayout layout = (GridLayout) statusPanel.getLayout();
735 // if (progressBars.get(lId) != null)
737 // progressPanel = (JPanel) progressBars.get(new Long(id));
738 // statusPanel.remove(progressPanel);
739 // progressBars.remove(lId);
740 // progressPanel = null;
741 // if (message != null)
743 // statusBar.setText(message);
745 // if (progressBarHandlers.contains(lId))
747 // progressBarHandlers.remove(lId);
749 // layout.setRows(layout.getRows() - 1);
753 // progressPanel = new JPanel(new BorderLayout(10, 5));
755 // JProgressBar progressBar = new JProgressBar();
756 // progressBar.setIndeterminate(true);
758 // progressPanel.add(new JLabel(message), BorderLayout.WEST);
759 // progressPanel.add(progressBar, BorderLayout.CENTER);
761 // layout.setRows(layout.getRows() + 1);
762 // statusPanel.add(progressPanel);
764 // progressBars.put(lId, progressPanel);
767 // // setMenusForViewport();
772 public void registerHandler(final long id,
773 final IProgressIndicatorHandler handler)
775 progressBar.registerHandler(id, handler);
776 // if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
779 // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
781 // progressBarHandlers.put(new Long(id), handler);
782 // final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
783 // if (handler.canCancel())
785 // JButton cancel = new JButton(
786 // MessageManager.getString("action.cancel"));
787 // final IProgressIndicator us = this;
788 // cancel.addActionListener(new ActionListener()
792 // public void actionPerformed(ActionEvent e)
794 // handler.cancelActivity(id);
795 // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
796 // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
799 // progressPanel.add(cancel, BorderLayout.EAST);
805 * @return true if any progress bars are still active
808 public boolean operationInProgress()
810 return progressBar.operationInProgress();
814 protected void resetButton_actionPerformed(ActionEvent e)
817 top = 0; // ugly - prevents dimensionChanged events from being processed
818 xCombobox.setSelectedIndex(0);
819 yCombobox.setSelectedIndex(1);
821 zCombobox.setSelectedIndex(2);
825 * If the score model is one that requires to get state data from the current
826 * view, allow it to do so
831 protected ScoreModelI configureScoreModel(ScoreModelI sm)
833 if (sm instanceof ViewBasedAnalysisI)
837 sm = sm.getClass().newInstance();
838 ((ViewBasedAnalysisI) sm).configureFromAlignmentView(ap);
839 } catch (Exception q)
841 Cache.log.error("Couldn't create a scoremodel instance for "