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) :
31 mussaViewTB("Path Views"),
37 createNewAnalysisAction(0),
38 createSubAnalysisAction(0),
40 loadMotifListAction(0),
42 loadSavedAnalysisAction(0),
43 mussaManualAssistantAction(0),
44 newMussaWindowAction(0),
45 saveMotifListAction(0),
46 showMussaViewToolbarAction(0),
47 toggleMotifsAction(0),
48 saveBrowserPixmapAction(0),
50 viewMussaAlignmentAction(0),
58 //This next setWhatsThis function prevents
59 // a segfault when using WhatsThis feature with
61 //scene->setWhatsThis(tr("Mussa in OpenGL!"));
62 setCentralWidget(&browser);
63 // well updatePosition isn't quite right as we really just need
65 connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update()));
67 mussaViewTB.addAction(toggleMotifsAction);
68 mussaViewTB.addWidget(&zoom);
70 connect(&zoom, SIGNAL(valueChanged(double)),
71 &browser, SLOT(setZoom(double)));
73 // threshold range is set in updateAnalysis
75 //scene->setClipPlane(20);
76 // FIXME: for when we get the paths drawn at the appropriate depth
77 //connect(&threshold, SIGNAL(thresholdChanged(int)),
78 // this, SLOT(setClipPlane(int)));
79 connect(&threshold, SIGNAL(thresholdChanged(int)),
80 this, SLOT(setSoftThreshold(int)));
81 mussaViewTB.addWidget(&threshold);
83 addToolBar(&mussaViewTB);
85 statusBar()->showMessage("Welcome to mussa", 2000);
86 connect(analysis, SIGNAL(progress(const std::string&, int, int)),
87 this, SLOT(updateProgress(const std::string&, int, int)));
91 MussaWindow::~MussaWindow()
93 if (analysis != 0) delete analysis;
94 aligned_windows.clear();
95 if (motif_editor != 0) delete motif_editor;
96 if (progress_dialog != 0) delete progress_dialog;
98 if (aboutAction != 0) delete aboutAction;
99 if (closeAction != 0) delete closeAction;
100 if (createNewAnalysisAction != 0) delete createNewAnalysisAction;
101 if (createSubAnalysisAction != 0) delete createSubAnalysisAction;
102 if (editMotifsAction != 0) delete editMotifsAction;
103 if (loadMotifListAction != 0) delete loadMotifListAction;
104 if (loadMupaAction != 0) delete loadMupaAction;
105 if (loadSavedAnalysisAction != 0) delete loadSavedAnalysisAction;
106 if (mussaManualAssistantAction != 0) delete mussaManualAssistantAction;
107 if (newMussaWindowAction != 0) delete newMussaWindowAction;
108 if (saveBrowserPixmapAction != 0) delete saveBrowserPixmapAction;
109 if (saveMotifListAction != 0) delete saveMotifListAction;
110 if (showMussaViewToolbarAction != 0) delete showMussaViewToolbarAction;
111 if (toggleMotifsAction != 0) delete toggleMotifsAction;
112 if (whatsThisAction != 0) delete whatsThisAction;
113 if (viewMussaAlignmentAction != 0) delete viewMussaAlignmentAction;
115 if (manualAssistant != 0) delete manualAssistant;
119 void MussaWindow::setAnalysis(Mussa *new_analysis)
121 if (new_analysis != 0) {
122 // only switch mussas if we loaded without error
124 analysis = new_analysis;
125 setWindowTitle(analysis->get_name().c_str());
130 void MussaWindow::setupActions()
132 // we really don't want to run this more than once.
133 assert (closeAction == 0);
135 // the ever popular about box
136 aboutAction = new QAction(tr("&About"), this);
137 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
138 aboutAction->setIcon(QIcon(":/icons/info.png"));
141 closeAction = new QAction(tr("&Close"), this);
142 closeAction->setStatusTip(tr("Close this window"));
143 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
144 closeAction->setIcon(QIcon(":/icons/exit.png"));
146 createNewAnalysisAction = new QAction(tr("Create Analysis"), this);
147 connect(createNewAnalysisAction, SIGNAL(triggered()),
148 this, SLOT(createNewAnalysis()));
149 createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
151 createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this);
152 connect(createSubAnalysisAction, SIGNAL(triggered()),
153 this, SLOT(createSubAnalysis()));
155 editMotifsAction = new QAction(tr("Edit Motifs"), this);;
156 connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
158 loadMotifListAction = new QAction(tr("Load Motif List"), this);
159 connect(loadMotifListAction, SIGNAL(triggered()),
160 this, SLOT(loadMotifList()));
161 loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
163 loadMupaAction = new QAction(tr("Create Analysis from File"), this);
164 connect(loadMupaAction, SIGNAL(triggered()),
165 this, SLOT(loadMupa()));
166 loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
168 loadSavedAnalysisAction = new QAction(tr("Load Existing &Analysis"), this);
169 connect(loadSavedAnalysisAction, SIGNAL(triggered()),
170 this, SLOT(loadSavedAnalysis()));
171 loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
173 mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
174 mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
175 connect(mussaManualAssistantAction, SIGNAL(triggered()),
176 this, SLOT(showManual()));
178 newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
179 newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
180 connect(newMussaWindowAction, SIGNAL(triggered()),
181 this, SLOT(newMussaWindow()));
182 newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
184 saveMotifListAction = new QAction(tr("Save Motifs"), this);
185 connect(saveMotifListAction, SIGNAL(triggered()),
186 this, SLOT(saveMotifList()));
187 saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
189 showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
190 connect(showMussaViewToolbarAction, SIGNAL(triggered()),
191 this, SLOT(showMussaToolbar()));
192 showMussaViewToolbarAction->setCheckable(true);
193 showMussaViewToolbarAction->setChecked(true);
195 toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
196 connect(toggleMotifsAction, SIGNAL(triggered()),
197 this, SLOT(toggleMotifs()));
198 toggleMotifsAction->setCheckable(true);
199 toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
200 toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
201 "You can load motif annotations via "
202 "'File->Load Motif List' menu option."));
204 //Save pixel map action
205 saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
206 connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
207 &browser, SLOT(promptSaveBrowserPixmap()));
208 saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
210 viewMussaAlignmentAction = new QAction(tr("View mussa alignment"), this);
211 connect(viewMussaAlignmentAction, SIGNAL(triggered()),
212 this, SLOT(viewMussaAlignment() ));
213 viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
214 "showing alignment of the seqcomp "
217 whatsThisAction = QWhatsThis::createAction(this);
218 whatsThisAction->setIcon(QIcon(":/icons/help.png"));
223 void MussaWindow::setupMainMenu()
225 // we need to run setupActions first
226 assert (closeAction != 0);
228 QMenu *newMenu = menuBar()->addMenu(tr("&File"));
230 newMenu->addAction(newMussaWindowAction);
231 newMenu->addAction(createNewAnalysisAction);
232 newMenu->addAction(loadMupaAction);
233 newMenu->addAction(loadSavedAnalysisAction);
234 //newMenu->addAction(createSubAnalysisAction);
235 newMenu->addSeparator();
236 newMenu->addAction(loadMotifListAction);
237 newMenu->addAction(saveMotifListAction);
238 newMenu->addSeparator();
239 newMenu->addAction(saveBrowserPixmapAction);
240 newMenu->addSeparator();
241 newMenu->addAction(closeAction);
243 newMenu = menuBar()->addMenu(tr("&View"));
244 newMenu->addAction(editMotifsAction);
245 newMenu->addAction(viewMussaAlignmentAction);
246 newMenu->addAction(showMussaViewToolbarAction);
248 newMenu = menuBar()->addMenu(tr("&Help"));
249 newMenu->addAction(mussaManualAssistantAction);
250 newMenu->addAction(whatsThisAction);
251 newMenu->addSeparator();
252 newMenu->addAction(aboutAction);
255 void MussaWindow::setupAssistant()
257 QStringList manualAssistantArgs;
258 manualAssistantArgs = QStringList();
259 manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
260 manualAssistant = new QAssistantClient("assistant", this);
261 manualAssistant->setArguments(manualAssistantArgs);
262 connect(manualAssistant, SIGNAL(error(QString)),
263 this, SLOT(assistantError(QString)));
266 void MussaWindow::about()
268 QString msg("Welcome to Multiple Species Sequence Analysis\n"
269 "(c) 2005-2006 California Institute of Technology\n"
270 "Tristan De Buysscher, Diane Trout\n");
273 msg += QDir::currentPath();
276 msg += (char *)glGetString(GL_VERSION);
278 QMessageBox::about(this, tr("About mussa"), msg);
281 void MussaWindow::createNewAnalysis()
284 if (setup_analysis_dialog.exec()) {
286 m = setup_analysis_dialog.getMussa();
289 std::cout << "New mussa exp. aborted!\n";
291 } catch(mussa_error e) {
292 QString msg(e.what());
293 QMessageBox::warning(this, tr("Create New Analysis"), msg);
297 void MussaWindow::createSubAnalysis()
302 void MussaWindow::editMotifs()
304 if (motif_editor != 0) {
305 motif_editor->hide();
308 motif_editor = new MotifEditor(analysis);
309 connect(motif_editor, SIGNAL(changedMotifs()),
310 this, SLOT(updateAnnotations()));
311 motif_editor->show();
314 void MussaWindow::loadMotifList()
316 QString caption("Load a motif list");
317 QString filter("Motif list(*.txt *.mtl)");
318 QString path = QFileDialog::getOpenFileName(this,
325 // try to load safely
327 fs::path converted_path(path.toStdString(), fs::native);
328 analysis->load_motifs(converted_path);
329 } catch (runtime_error e) {
330 QString msg("Unable to load ");
334 QMessageBox::warning(this, "Load Motifs", msg);
336 assert (analysis != 0);
339 void MussaWindow::saveMotifList()
344 void MussaWindow::loadMupa()
346 QString caption("Load a mussa parameter file");
347 QString filter("Mussa Parameters (*.mupa)");
348 QString mupa_path = QFileDialog::getOpenFileName(this,
353 if (mupa_path.isNull())
355 // try to load safely
357 Mussa *m = new Mussa;
358 fs::path converted_path(mupa_path.toStdString(), fs::native);
359 connect(m, SIGNAL(progress(const std::string&, int, int)),
360 this, SLOT(updateProgress(const std::string&, int, int)));
361 m->load_mupa_file(converted_path);
364 setWindowTitle(converted_path.native_file_string().c_str());
365 } catch (mussa_load_error e) {
366 QString msg("Unable to load ");
370 QMessageBox::warning(this, "Load Parameter", msg);
372 assert (analysis != 0);
375 void MussaWindow::loadSavedAnalysis()
377 QString caption("Load a previously run analysis");
378 QString muway_dir = QFileDialog::getExistingDirectory(this,
380 QDir::currentPath());
382 if (muway_dir.isNull())
384 // try to safely load
386 Mussa *m = new Mussa;
387 fs::path converted_path(muway_dir.toStdString(), fs::native);
388 connect(m, SIGNAL(progress(const std::string&, int, int)),
389 this, SLOT(updateProgress(const std::string&, int, int)));
390 m->load(converted_path);
391 // only switch mussas if we loaded without error
393 setWindowTitle(converted_path.native_file_string().c_str());
394 } catch (mussa_load_error e) {
395 QString msg("Unable to load ");
399 QMessageBox::warning(this, "Load Parameter", msg);
401 assert (analysis != 0);
404 void MussaWindow::newMussaWindow()
406 Mussa *a = new Mussa();
407 MussaWindow *win = new MussaWindow(a);
411 void MussaWindow::setSoftThreshold(int threshold)
413 if (analysis->get_soft_threshold() != threshold) {
414 analysis->set_soft_threshold(threshold);
421 void MussaWindow::showMussaToolbar()
423 if (mussaViewTB.isVisible())
429 void MussaWindow::toggleMotifs()
434 void MussaWindow::showManual()
436 manualAssistant->openAssistant();
439 void MussaWindow::assistantError(QString message)
441 //std::cout << "QAssistantError: " << message.toStdString() << "\n";
442 QMessageBox::warning ( this, "Warning: Mussagl Manual", message,
444 QMessageBox::NoButton);
447 void MussaWindow::NotImplementedBox()
449 QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
452 void MussaWindow::viewMussaAlignment()
454 const set<int>& selected_paths = browser.selectedPaths();
455 if (selected_paths.size() == 0 ) {
456 QMessageBox::warning(this,
457 QObject::tr("mussa"),
458 QObject::tr("you should probably select some paths "
461 MussaAlignedWindow *ma_win = new MussaAlignedWindow(*analysis,
463 connect(this, SIGNAL(changedAnnotations()),
464 ma_win, SLOT(update()));
465 aligned_windows.push_back(ma_win);
470 void MussaWindow::updateAnalysis()
472 threshold.setRange(analysis->get_threshold(),analysis->get_window());
473 for (list<MussaAlignedWindow *>::iterator maw_i = aligned_windows.begin();
474 maw_i != aligned_windows.end();
482 const vector<Sequence>& seqs = analysis->sequences();
483 browser.setSequences(seqs, analysis->colorMapper());
486 zoom.setValue(browser.zoom());
489 void MussaWindow::updateAnnotations()
491 // motifs were changed in the sequences by
492 // Mussa::update_sequences_motifs
493 emit changedAnnotations();
497 void MussaWindow::updateLinks()
499 browser.clear_links();
500 bool reversed = false;
501 const NwayPaths& nway = analysis->paths();
503 typedef list<ConservedPath> conserved_paths;
504 typedef conserved_paths::const_iterator const_conserved_paths_itor;
505 for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
506 path_itor != nway.refined_pathz.end();
509 // since we were drawing to the start of a window, and opengl lines
510 // are centered around the two connecting points our lines were slightly
511 // offset. the idea of window_offset is to adjust them to the
512 // right for forward compliment or left for reverse compliment
513 // FIXME: figure out how to unit test these computations
514 //GLfloat window_offset = (path_itor->window_size)/2.0;
516 size_t track_len = path_itor->track_indexes.size();
517 vector<int> normalized_path;
518 normalized_path.reserve(track_len);
519 vector<bool> rc_flags(false, track_len);
520 for (size_t track_i=0; track_i != track_len; ++track_i)
522 int x = path_itor->track_indexes[track_i];
523 // at some point when we modify the pathz data structure to keep
524 // track of the score we can put grab the depth here.
526 // are we reverse complimented?
534 normalized_path.push_back(x);
535 rc_flags.push_back(reversed);
537 browser.link(normalized_path, rc_flags, path_itor->window_size);
543 MussaWindow::updateProgress(const string& description, int current, int max)
546 if (current == max) {
547 if (progress_dialog != 0) {
548 progress_dialog->hide();
549 delete progress_dialog;
553 // if we're starting, create the dialog
554 if (progress_dialog == 0) {
555 QString desc(description.c_str());
556 QString cancel("Cancel");
557 progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
558 progress_dialog->show();
560 // just update the dialog
561 progress_dialog->setValue(current);
564 qApp->processEvents();