2 #include <QAssistantClient>
12 #include <QStringList>
15 #include "qui/MussaWindow.hpp"
16 #include "mussa_exceptions.hpp"
20 #include <boost/filesystem/path.hpp>
21 namespace fs = boost::filesystem;
25 MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
30 mussaViewTB("Path Views"),
35 createNewAnalysisAction(0),
36 createSubAnalysisAction(0),
38 loadMotifListAction(0),
40 loadSavedAnalysisAction(0),
41 mussaManualAssistantAction(0),
42 newMussaWindowAction(0),
43 saveMotifListAction(0),
44 showMussaViewToolbarAction(0),
45 toggleMotifsAction(0),
46 saveBrowserPixmapAction(0),
48 viewMussaAlignmentAction(0),
56 //This next setWhatsThis function prevents
57 // a segfault when using WhatsThis feature with
59 //scene->setWhatsThis(tr("Mussa in OpenGL!"));
60 setCentralWidget(&browser);
61 // well updatePosition isn't quite right as we really just need
63 connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update()));
65 mussaViewTB.addAction(toggleMotifsAction);
66 mussaViewTB.addWidget(&zoom);
68 connect(&zoom, SIGNAL(valueChanged(double)),
69 &browser, SLOT(setZoom(double)));
71 // threshold range is set in updateAnalysis
73 //scene->setClipPlane(20);
74 // FIXME: for when we get the paths drawn at the appropriate depth
75 //connect(&threshold, SIGNAL(thresholdChanged(int)),
76 // this, SLOT(setClipPlane(int)));
77 connect(&threshold, SIGNAL(thresholdChanged(int)),
78 this, SLOT(setSoftThreshold(int)));
79 mussaViewTB.addWidget(&threshold);
81 addToolBar(&mussaViewTB);
83 statusBar()->showMessage("Welcome to mussa", 2000);
87 MussaWindow::~MussaWindow()
89 if (analysis != 0) delete analysis;
90 aligned_windows.clear();
91 if (motif_editor != 0) delete motif_editor;
93 if (aboutAction != 0) delete aboutAction;
94 if (closeAction != 0) delete closeAction;
95 if (createNewAnalysisAction != 0) delete createNewAnalysisAction;
96 if (createSubAnalysisAction != 0) delete createSubAnalysisAction;
97 if (editMotifsAction != 0) delete editMotifsAction;
98 if (loadMotifListAction != 0) delete loadMotifListAction;
99 if (loadMupaAction != 0) delete loadMupaAction;
100 if (loadSavedAnalysisAction != 0) delete loadSavedAnalysisAction;
101 if (mussaManualAssistantAction != 0) delete mussaManualAssistantAction;
102 if (newMussaWindowAction != 0) delete newMussaWindowAction;
103 if (saveBrowserPixmapAction != 0) delete saveBrowserPixmapAction;
104 if (saveMotifListAction != 0) delete saveMotifListAction;
105 if (showMussaViewToolbarAction != 0) delete showMussaViewToolbarAction;
106 if (toggleMotifsAction != 0) delete toggleMotifsAction;
107 if (whatsThisAction != 0) delete whatsThisAction;
108 if (viewMussaAlignmentAction != 0) delete viewMussaAlignmentAction;
110 if (manualAssistant != 0) delete manualAssistant;
114 void MussaWindow::setAnalysis(Mussa *new_analysis)
116 if (new_analysis != 0) {
117 // only switch mussas if we loaded without error
119 analysis = new_analysis;
120 setWindowTitle(analysis->get_name().c_str());
125 void MussaWindow::setupActions()
127 // we really don't want to run this more than once.
128 assert (closeAction == 0);
130 // the ever popular about box
131 aboutAction = new QAction(tr("&About"), this);
132 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
133 aboutAction->setIcon(QIcon(":/icons/info.png"));
136 closeAction = new QAction(tr("&Close"), this);
137 closeAction->setStatusTip(tr("Close this window"));
138 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
139 closeAction->setIcon(QIcon(":/icons/exit.png"));
141 createNewAnalysisAction = new QAction(tr("Define Analysis"), this);
142 connect(createNewAnalysisAction, SIGNAL(triggered()),
143 this, SLOT(createNewAnalysis()));
144 createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
146 createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this);
147 connect(createSubAnalysisAction, SIGNAL(triggered()),
148 this, SLOT(createSubAnalysis()));
150 editMotifsAction = new QAction(tr("Edit Motifs"), this);;
151 connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
153 loadMotifListAction = new QAction(tr("Load Motif List"), this);
154 connect(loadMotifListAction, SIGNAL(triggered()),
155 this, SLOT(loadMotifList()));
156 loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
158 loadMupaAction = new QAction(tr("Load Mussa Parameters"), this);
159 connect(loadMupaAction, SIGNAL(triggered()),
160 this, SLOT(loadMupa()));
161 loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
163 loadSavedAnalysisAction = new QAction(tr("Load &Analysis"), this);
164 connect(loadSavedAnalysisAction, SIGNAL(triggered()),
165 this, SLOT(loadSavedAnalysis()));
166 loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
168 mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
169 mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
170 connect(mussaManualAssistantAction, SIGNAL(triggered()),
171 this, SLOT(showManual()));
173 newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
174 newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
175 connect(newMussaWindowAction, SIGNAL(triggered()),
176 this, SLOT(newMussaWindow()));
177 newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
179 saveMotifListAction = new QAction(tr("Save Motifs"), this);
180 connect(saveMotifListAction, SIGNAL(triggered()),
181 this, SLOT(saveMotifList()));
182 saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
184 showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
185 connect(showMussaViewToolbarAction, SIGNAL(triggered()),
186 this, SLOT(showMussaToolbar()));
187 showMussaViewToolbarAction->setCheckable(true);
188 showMussaViewToolbarAction->setChecked(true);
190 toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
191 connect(toggleMotifsAction, SIGNAL(triggered()),
192 this, SLOT(toggleMotifs()));
193 toggleMotifsAction->setCheckable(true);
194 toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
195 toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
196 "You can load motif annotations via "
197 "'File->Load Motif List' menu option."));
199 //Save pixel map action
200 saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
201 connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
202 &browser, SLOT(promptSaveBrowserPixmap()));
203 saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
205 viewMussaAlignmentAction = new QAction(tr("View mussa alignment"), this);
206 connect(viewMussaAlignmentAction, SIGNAL(triggered()),
207 this, SLOT(viewMussaAlignment() ));
208 viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
209 "showing alignment of the seqcomp "
212 whatsThisAction = QWhatsThis::createAction(this);
213 whatsThisAction->setIcon(QIcon(":/icons/help.png"));
218 void MussaWindow::setupMainMenu()
220 // we need to run setupActions first
221 assert (closeAction != 0);
223 QMenu *newMenu = menuBar()->addMenu(tr("&File"));
225 newMenu->addAction(newMussaWindowAction);
226 newMenu->addAction(createNewAnalysisAction);
227 newMenu->addAction(loadMupaAction);
228 newMenu->addAction(loadSavedAnalysisAction);
229 //newMenu->addAction(createSubAnalysisAction);
230 newMenu->addSeparator();
231 newMenu->addAction(loadMotifListAction);
232 newMenu->addAction(saveMotifListAction);
233 newMenu->addSeparator();
234 newMenu->addAction(saveBrowserPixmapAction);
235 newMenu->addSeparator();
236 newMenu->addAction(closeAction);
238 newMenu = menuBar()->addMenu(tr("&View"));
239 newMenu->addAction(editMotifsAction);
240 newMenu->addAction(viewMussaAlignmentAction);
241 newMenu->addAction(showMussaViewToolbarAction);
243 newMenu = menuBar()->addMenu(tr("&Help"));
244 newMenu->addAction(mussaManualAssistantAction);
245 newMenu->addAction(whatsThisAction);
246 newMenu->addSeparator();
247 newMenu->addAction(aboutAction);
250 void MussaWindow::setupAssistant()
252 QStringList manualAssistantArgs;
253 manualAssistantArgs = QStringList();
254 manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
255 manualAssistant = new QAssistantClient("assistant", this);
256 manualAssistant->setArguments(manualAssistantArgs);
257 connect(manualAssistant, SIGNAL(error(QString)),
258 this, SLOT(assistantError(QString)));
261 void MussaWindow::about()
263 QString msg("Welcome to Multiple Species Sequence Analysis\n"
264 "(c) 2005-2006 California Institute of Technology\n"
265 "Tristan De Buysscher, Diane Trout\n");
268 msg += QDir::currentPath();
271 msg += (char *)glGetString(GL_VERSION);
273 QMessageBox::about(this, tr("About mussa"), msg);
276 void MussaWindow::createNewAnalysis()
279 if (setup_analysis_dialog.exec()) {
281 m = setup_analysis_dialog.getMussa();
284 std::cout << "New mussa exp. aborted!\n";
286 } catch(mussa_error e) {
287 QString msg(e.what());
288 QMessageBox::warning(this, tr("Create New Analysis"), msg);
292 void MussaWindow::createSubAnalysis()
297 void MussaWindow::editMotifs()
299 if (motif_editor != 0) {
300 motif_editor->hide();
303 motif_editor = new MotifEditor(analysis);
304 connect(motif_editor, SIGNAL(changedMotifs()),
305 this, SLOT(updateAnnotations()));
306 motif_editor->show();
309 void MussaWindow::loadMotifList()
311 QString caption("Load a motif list");
312 QString filter("Motif list(*.txt *.mtl)");
313 QString path = QFileDialog::getOpenFileName(this,
320 // try to load safely
322 fs::path converted_path(path.toStdString(), fs::native);
323 analysis->load_motifs(converted_path);
324 } catch (runtime_error e) {
325 QString msg("Unable to load ");
329 QMessageBox::warning(this, "Load Motifs", msg);
331 assert (analysis != 0);
334 void MussaWindow::saveMotifList()
339 void MussaWindow::loadMupa()
341 QString caption("Load a mussa parameter file");
342 QString filter("Mussa Parameters (*.mupa)");
343 QString mupa_path = QFileDialog::getOpenFileName(this,
348 if (mupa_path.isNull())
350 // try to load safely
352 Mussa *m = new Mussa;
353 fs::path converted_path(mupa_path.toStdString(), fs::native);
354 m->load_mupa_file(converted_path);
357 setWindowTitle(converted_path.native_file_string().c_str());
358 } catch (mussa_load_error e) {
359 QString msg("Unable to load ");
363 QMessageBox::warning(this, "Load Parameter", msg);
365 assert (analysis != 0);
368 void MussaWindow::loadSavedAnalysis()
370 QString caption("Load a previously run analysis");
371 QString muway_dir = QFileDialog::getExistingDirectory(this,
373 QDir::currentPath());
375 if (muway_dir.isNull())
377 // try to safely load
379 Mussa *m = new Mussa;
380 fs::path converted_path(muway_dir.toStdString(), fs::native);
381 m->load(converted_path);
382 // only switch mussas if we loaded without error
384 setWindowTitle(converted_path.native_file_string().c_str());
385 } catch (mussa_load_error e) {
386 QString msg("Unable to load ");
390 QMessageBox::warning(this, "Load Parameter", msg);
392 assert (analysis != 0);
395 void MussaWindow::newMussaWindow()
397 Mussa *a = new Mussa();
398 MussaWindow *win = new MussaWindow(a);
402 void MussaWindow::setSoftThreshold(int threshold)
404 if (analysis->get_soft_threshold() != threshold) {
405 analysis->set_soft_threshold(threshold);
412 void MussaWindow::showMussaToolbar()
414 if (mussaViewTB.isVisible())
420 void MussaWindow::toggleMotifs()
425 void MussaWindow::showManual()
427 manualAssistant->openAssistant();
430 void MussaWindow::assistantError(QString message)
432 //std::cout << "QAssistantError: " << message.toStdString() << "\n";
433 QMessageBox::warning ( this, "Warning: Mussagl Manual", message,
435 QMessageBox::NoButton);
438 void MussaWindow::NotImplementedBox()
440 QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
443 void MussaWindow::viewMussaAlignment()
445 const set<int>& selected_paths = browser.selectedPaths();
446 if (selected_paths.size() == 0 ) {
447 QMessageBox::warning(this,
448 QObject::tr("mussa"),
449 QObject::tr("you should probably select some paths "
452 MussaAlignedWindow *ma_win = new MussaAlignedWindow(*analysis,
454 connect(this, SIGNAL(changedAnnotations()),
455 ma_win, SLOT(update()));
456 aligned_windows.push_back(ma_win);
461 void MussaWindow::updateAnalysis()
463 threshold.setRange(analysis->get_threshold(),analysis->get_window());
464 for (list<MussaAlignedWindow *>::iterator maw_i = aligned_windows.begin();
465 maw_i != aligned_windows.end();
473 const vector<Sequence>& seqs = analysis->sequences();
474 browser.setSequences(seqs, analysis->colorMapper());
477 zoom.setValue(browser.zoom());
480 void MussaWindow::updateAnnotations()
482 // motifs were changed in the sequences by
483 // Mussa::update_sequences_motifs
484 emit changedAnnotations();
488 void MussaWindow::updateLinks()
490 browser.clear_links();
491 bool reversed = false;
492 const NwayPaths& nway = analysis->paths();
494 typedef list<ConservedPath> conserved_paths;
495 typedef conserved_paths::const_iterator const_conserved_paths_itor;
496 for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
497 path_itor != nway.refined_pathz.end();
500 // since we were drawing to the start of a window, and opengl lines
501 // are centered around the two connecting points our lines were slightly
502 // offset. the idea of window_offset is to adjust them to the
503 // right for forward compliment or left for reverse compliment
504 // FIXME: figure out how to unit test these computations
505 //GLfloat window_offset = (path_itor->window_size)/2.0;
507 size_t track_len = path_itor->track_indexes.size();
508 vector<int> normalized_path;
509 normalized_path.reserve(track_len);
510 vector<bool> rc_flags(false, track_len);
511 for (size_t track_i=0; track_i != track_len; ++track_i)
513 int x = path_itor->track_indexes[track_i];
514 // at some point when we modify the pathz data structure to keep
515 // track of the score we can put grab the depth here.
517 // are we reverse complimented?
525 normalized_path.push_back(x);
526 rc_flags.push_back(reversed);
528 browser.link(normalized_path, rc_flags, path_itor->window_size);