2 #include <QApplication>
3 #include <QAssistantClient>
13 #include <QStringList>
16 #include "qui/MussaWindow.hpp"
17 #include "mussa_exceptions.hpp"
21 #include <boost/filesystem/path.hpp>
22 namespace fs = boost::filesystem;
26 MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
30 setup_analysis_dialog(this),
32 mussaViewTB("Path Views"),
37 copySelectedSequenceAsFastaAction(0),
39 createNewAnalysisAction(0),
40 createSubAnalysisAction(0),
42 loadMotifListAction(0),
44 loadSavedAnalysisAction(0),
45 mussaManualAssistantAction(0),
46 newMussaWindowAction(0),
47 saveMotifListAction(0),
48 showMussaViewToolbarAction(0),
49 toggleMotifsAction(0),
50 saveBrowserPixmapAction(0),
52 viewMussaAlignmentAction(0),
60 //This next setWhatsThis function prevents
61 // a segfault when using WhatsThis feature with
63 //scene->setWhatsThis(tr("Mussa in OpenGL!"));
64 setCentralWidget(&browser);
65 // well updatePosition isn't quite right as we really just need
67 connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update()));
69 mussaViewTB.addAction(toggleMotifsAction);
70 mussaViewTB.addWidget(&zoom);
72 connect(&zoom, SIGNAL(valueChanged(double)),
73 &browser, SLOT(setZoom(double)));
75 // threshold range is set in updateAnalysis
77 //scene->setClipPlane(20);
78 // FIXME: for when we get the paths drawn at the appropriate depth
79 //connect(&threshold, SIGNAL(thresholdChanged(int)),
80 // this, SLOT(setClipPlane(int)));
81 connect(&threshold, SIGNAL(thresholdChanged(int)),
82 this, SLOT(setSoftThreshold(int)));
83 mussaViewTB.addWidget(&threshold);
85 addToolBar(&mussaViewTB);
87 statusBar()->showMessage("Welcome to mussa", 2000);
88 connect(analysis, SIGNAL(progress(const std::string&, int, int)),
89 this, SLOT(updateProgress(const std::string&, int, int)));
93 MussaWindow::~MussaWindow()
95 if (analysis != 0) delete analysis;
96 aligned_windows.clear();
97 if (motif_editor != 0) delete motif_editor;
98 if (progress_dialog != 0) delete progress_dialog;
100 if (aboutAction != 0) delete aboutAction;
101 if (copySelectedSequenceAsFastaAction != 0)
102 delete copySelectedSequenceAsFastaAction;
103 if (closeAction != 0) delete closeAction;
104 if (createNewAnalysisAction != 0) delete createNewAnalysisAction;
105 if (createSubAnalysisAction != 0) delete createSubAnalysisAction;
106 if (editMotifsAction != 0) delete editMotifsAction;
107 if (loadMotifListAction != 0) delete loadMotifListAction;
108 if (loadMupaAction != 0) delete loadMupaAction;
109 if (loadSavedAnalysisAction != 0) delete loadSavedAnalysisAction;
110 if (mussaManualAssistantAction != 0) delete mussaManualAssistantAction;
111 if (newMussaWindowAction != 0) delete newMussaWindowAction;
112 if (saveBrowserPixmapAction != 0) delete saveBrowserPixmapAction;
113 if (saveMotifListAction != 0) delete saveMotifListAction;
114 if (showMussaViewToolbarAction != 0) delete showMussaViewToolbarAction;
115 if (toggleMotifsAction != 0) delete toggleMotifsAction;
116 if (whatsThisAction != 0) delete whatsThisAction;
117 if (viewMussaAlignmentAction != 0) delete viewMussaAlignmentAction;
119 if (manualAssistant != 0) delete manualAssistant;
123 void MussaWindow::setAnalysis(Mussa *new_analysis)
125 if (new_analysis != 0) {
126 // only switch mussas if we loaded without error
128 analysis = new_analysis;
129 setWindowTitle(analysis->get_name().c_str());
134 void MussaWindow::setupActions()
136 // we really don't want to run this more than once.
137 assert (closeAction == 0);
139 // the ever popular about box
140 aboutAction = new QAction(tr("&About"), this);
141 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
142 aboutAction->setIcon(QIcon(":/icons/info.png"));
145 copySelectedSequenceAsFastaAction = new QAction(tr("&Copy As Fasta"), this);
146 connect(copySelectedSequenceAsFastaAction, SIGNAL(triggered()),
147 &browser, SLOT(copySelectedSequenceAsFasta()));
150 closeAction = new QAction(tr("&Close"), this);
151 closeAction->setStatusTip(tr("Close this window"));
152 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
153 closeAction->setIcon(QIcon(":/icons/exit.png"));
155 createNewAnalysisAction = new QAction(tr("Create Analysis"), this);
156 connect(createNewAnalysisAction, SIGNAL(triggered()),
157 this, SLOT(createNewAnalysis()));
158 createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
160 createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this);
161 connect(createSubAnalysisAction, SIGNAL(triggered()),
162 this, SLOT(createSubAnalysis()));
164 editMotifsAction = new QAction(tr("Edit Motifs"), this);;
165 connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
167 loadMotifListAction = new QAction(tr("Load Motif List"), this);
168 connect(loadMotifListAction, SIGNAL(triggered()),
169 this, SLOT(loadMotifList()));
170 loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
172 loadMupaAction = new QAction(tr("Create Analysis from File"), this);
173 connect(loadMupaAction, SIGNAL(triggered()),
174 this, SLOT(loadMupa()));
175 loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
177 loadSavedAnalysisAction = new QAction(tr("Load Existing &Analysis"), this);
178 connect(loadSavedAnalysisAction, SIGNAL(triggered()),
179 this, SLOT(loadSavedAnalysis()));
180 loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
182 mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
183 mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
184 connect(mussaManualAssistantAction, SIGNAL(triggered()),
185 this, SLOT(showManual()));
187 newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
188 newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
189 connect(newMussaWindowAction, SIGNAL(triggered()),
190 this, SLOT(newMussaWindow()));
191 newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
193 saveMotifListAction = new QAction(tr("Save Motifs"), this);
194 connect(saveMotifListAction, SIGNAL(triggered()),
195 this, SLOT(saveMotifList()));
196 saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
198 showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
199 connect(showMussaViewToolbarAction, SIGNAL(triggered()),
200 this, SLOT(showMussaToolbar()));
201 showMussaViewToolbarAction->setCheckable(true);
202 showMussaViewToolbarAction->setChecked(true);
204 toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
205 connect(toggleMotifsAction, SIGNAL(triggered()),
206 this, SLOT(toggleMotifs()));
207 toggleMotifsAction->setCheckable(true);
208 toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
209 toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
210 "You can load motif annotations via "
211 "'File->Load Motif List' menu option."));
213 //Save pixel map action
214 saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
215 connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
216 &browser, SLOT(promptSaveBrowserPixmap()));
217 saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
219 viewMussaAlignmentAction = new QAction(tr("View mussa alignment"), this);
220 connect(viewMussaAlignmentAction, SIGNAL(triggered()),
221 this, SLOT(viewMussaAlignment() ));
222 viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
223 "showing alignment of the seqcomp "
226 whatsThisAction = QWhatsThis::createAction(this);
227 whatsThisAction->setIcon(QIcon(":/icons/help.png"));
232 void MussaWindow::setupMainMenu()
234 // we need to run setupActions first
235 assert (closeAction != 0);
237 QMenu *newMenu = menuBar()->addMenu(tr("&File"));
239 newMenu->addAction(newMussaWindowAction);
240 newMenu->addAction(createNewAnalysisAction);
241 newMenu->addAction(loadMupaAction);
242 newMenu->addAction(loadSavedAnalysisAction);
243 //newMenu->addAction(createSubAnalysisAction);
244 newMenu->addSeparator();
245 newMenu->addAction(loadMotifListAction);
246 newMenu->addAction(saveMotifListAction);
247 newMenu->addSeparator();
248 newMenu->addAction(saveBrowserPixmapAction);
249 newMenu->addSeparator();
250 newMenu->addAction(closeAction);
252 newMenu = menuBar()->addMenu(tr("&Edit"));
253 newMenu->addAction(copySelectedSequenceAsFastaAction);
255 newMenu = menuBar()->addMenu(tr("&View"));
256 newMenu->addAction(editMotifsAction);
257 newMenu->addAction(viewMussaAlignmentAction);
258 newMenu->addAction(showMussaViewToolbarAction);
260 newMenu = menuBar()->addMenu(tr("&Help"));
261 newMenu->addAction(mussaManualAssistantAction);
262 newMenu->addAction(whatsThisAction);
263 newMenu->addSeparator();
264 newMenu->addAction(aboutAction);
267 void MussaWindow::setupAssistant()
269 QStringList manualAssistantArgs;
270 manualAssistantArgs = QStringList();
271 manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
272 manualAssistant = new QAssistantClient("assistant", this);
273 manualAssistant->setArguments(manualAssistantArgs);
274 connect(manualAssistant, SIGNAL(error(QString)),
275 this, SLOT(assistantError(QString)));
278 void MussaWindow::about()
280 QString msg("Welcome to Multiple Species Sequence Analysis\n"
281 "(c) 2005-2006 California Institute of Technology\n"
282 "Tristan De Buysscher, Diane Trout\n");
285 msg += QDir::currentPath();
288 msg += (char *)glGetString(GL_VERSION);
290 QMessageBox::about(this, tr("About mussa"), msg);
293 void MussaWindow::createNewAnalysis()
296 if (setup_analysis_dialog.exec()) {
298 m = setup_analysis_dialog.getMussa();
301 std::cout << "New mussa exp. aborted!\n";
303 } catch(mussa_error e) {
304 QString msg(e.what());
305 QMessageBox::warning(this, tr("Create New Analysis"), msg);
309 void MussaWindow::createSubAnalysis()
314 void MussaWindow::editMotifs()
316 if (motif_editor != 0) {
317 motif_editor->hide();
320 motif_editor = new MotifEditor(analysis);
321 connect(motif_editor, SIGNAL(changedMotifs()),
322 this, SLOT(updateAnnotations()));
323 motif_editor->show();
326 void MussaWindow::loadMotifList()
328 QString caption("Load a motif list");
329 QString filter("Motif list(*.txt *.mtl)");
330 QString path = QFileDialog::getOpenFileName(this,
337 // try to load safely
339 fs::path converted_path(path.toStdString(), fs::native);
340 analysis->load_motifs(converted_path);
341 } catch (runtime_error e) {
342 QString msg("Unable to load ");
346 QMessageBox::warning(this, "Load Motifs", msg);
348 assert (analysis != 0);
351 void MussaWindow::saveMotifList()
356 void MussaWindow::loadMupa()
358 QString caption("Load a mussa parameter file");
359 QString filter("Mussa Parameters (*.mupa)");
360 QString mupa_path = QFileDialog::getOpenFileName(this,
365 if (mupa_path.isNull())
367 // try to load safely
369 Mussa *m = new Mussa;
370 fs::path converted_path(mupa_path.toStdString(), fs::native);
371 connect(m, SIGNAL(progress(const std::string&, int, int)),
372 this, SLOT(updateProgress(const std::string&, int, int)));
373 m->load_mupa_file(converted_path);
376 setWindowTitle(converted_path.native_file_string().c_str());
377 } catch (mussa_load_error e) {
378 QString msg("Unable to load ");
382 QMessageBox::warning(this, "Load Parameter", msg);
384 assert (analysis != 0);
387 void MussaWindow::loadSavedAnalysis()
389 QString caption("Load a previously run analysis");
390 QString muway_dir = QFileDialog::getExistingDirectory(this,
392 QDir::currentPath());
394 if (muway_dir.isNull())
396 // try to safely load
398 Mussa *m = new Mussa;
399 fs::path converted_path(muway_dir.toStdString(), fs::native);
400 connect(m, SIGNAL(progress(const std::string&, int, int)),
401 this, SLOT(updateProgress(const std::string&, int, int)));
402 m->load(converted_path);
403 // only switch mussas if we loaded without error
405 setWindowTitle(converted_path.native_file_string().c_str());
406 } catch (mussa_load_error e) {
407 QString msg("Unable to load ");
411 QMessageBox::warning(this, "Load Parameter", msg);
413 assert (analysis != 0);
416 void MussaWindow::newMussaWindow()
418 Mussa *a = new Mussa();
419 MussaWindow *win = new MussaWindow(a);
423 void MussaWindow::setSoftThreshold(int threshold)
425 if (analysis->get_soft_threshold() != threshold) {
426 analysis->set_soft_threshold(threshold);
433 void MussaWindow::showMussaToolbar()
435 if (mussaViewTB.isVisible())
441 void MussaWindow::toggleMotifs()
446 void MussaWindow::showManual()
448 manualAssistant->openAssistant();
451 void MussaWindow::assistantError(QString message)
453 //std::cout << "QAssistantError: " << message.toStdString() << "\n";
454 QMessageBox::warning ( this, "Warning: Mussagl Manual", message,
456 QMessageBox::NoButton);
459 void MussaWindow::NotImplementedBox()
461 QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
464 void MussaWindow::viewMussaAlignment()
466 const set<int>& selected_paths = browser.selectedPaths();
467 if (selected_paths.size() == 0 ) {
468 QMessageBox::warning(this,
469 QObject::tr("mussa"),
470 QObject::tr("you should probably select some paths "
473 MussaAlignedWindow *ma_win = new MussaAlignedWindow(*analysis,
475 connect(this, SIGNAL(changedAnnotations()),
476 ma_win, SLOT(update()));
477 aligned_windows.push_back(ma_win);
482 void MussaWindow::updateAnalysis()
484 threshold.setRange(analysis->get_threshold(),analysis->get_window());
485 for (list<MussaAlignedWindow *>::iterator maw_i = aligned_windows.begin();
486 maw_i != aligned_windows.end();
494 const vector<Sequence>& seqs = analysis->sequences();
495 browser.setSequences(seqs, analysis->colorMapper());
498 zoom.setValue(browser.zoom());
501 void MussaWindow::updateAnnotations()
503 // motifs were changed in the sequences by
504 // Mussa::update_sequences_motifs
505 emit changedAnnotations();
509 void MussaWindow::updateLinks()
511 browser.clear_links();
512 bool reversed = false;
513 const NwayPaths& nway = analysis->paths();
515 typedef list<ConservedPath> conserved_paths;
516 typedef conserved_paths::const_iterator const_conserved_paths_itor;
517 for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
518 path_itor != nway.refined_pathz.end();
521 // since we were drawing to the start of a window, and opengl lines
522 // are centered around the two connecting points our lines were slightly
523 // offset. the idea of window_offset is to adjust them to the
524 // right for forward compliment or left for reverse compliment
525 // FIXME: figure out how to unit test these computations
526 //GLfloat window_offset = (path_itor->window_size)/2.0;
528 size_t track_len = path_itor->track_indexes.size();
529 vector<int> normalized_path;
530 normalized_path.reserve(track_len);
531 vector<bool> rc_flags(false, track_len);
532 for (size_t track_i=0; track_i != track_len; ++track_i)
534 int x = path_itor->track_indexes[track_i];
535 // at some point when we modify the pathz data structure to keep
536 // track of the score we can put grab the depth here.
538 // are we reverse complimented?
546 normalized_path.push_back(x);
547 rc_flags.push_back(reversed);
549 browser.link(normalized_path, rc_flags, path_itor->window_size);
555 MussaWindow::updateProgress(const string& description, int current, int max)
558 if (current == max) {
559 if (progress_dialog != 0) {
560 progress_dialog->hide();
561 delete progress_dialog;
565 // if we're starting, create the dialog
566 if (progress_dialog == 0) {
567 QString desc(description.c_str());
568 QString cancel("Cancel");
569 progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
570 progress_dialog->show();
572 // just update the dialog
573 progress_dialog->setValue(current);
576 qApp->processEvents();