2 #include <QApplication>
3 #include <QAssistantClient>
13 #include <QStringList>
17 #include <CoreFoundation/CoreFoundation.h>
20 #include "qui/MussaWindow.hpp"
21 #include "mussa_exceptions.hpp"
26 #include <boost/filesystem/path.hpp>
27 namespace fs = boost::filesystem;
28 #include <boost/bind.hpp>
32 MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
36 setup_analysis_dialog(this),
38 mussaViewTB("Path Views"),
44 createNewAnalysisAction(0),
45 createSubAnalysisAction(0),
47 loadMotifListAction(0),
49 loadSavedAnalysisAction(0),
50 mussaManualAssistantAction(0),
51 newMussaWindowAction(0),
52 saveMotifListAction(0),
53 showMussaViewToolbarAction(0),
54 toggleMotifsAction(0),
55 saveBrowserPixmapAction(0),
57 viewMussaAlignmentAction(0),
64 //This next setWhatsThis function prevents
65 // a segfault when using WhatsThis feature with
67 //scene->setWhatsThis(tr("Mussa in OpenGL!"));
68 setCentralWidget(&browser);
69 // well updatePosition isn't quite right as we really just need
71 connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update()));
73 mussaViewTB.addAction(toggleMotifsAction);
74 mussaViewTB.addWidget(&zoom);
76 connect(&zoom, SIGNAL(valueChanged(double)),
77 &browser, SLOT(setZoom(double)));
79 // threshold range is set in updateAnalysis
81 //scene->setClipPlane(20);
82 // FIXME: for when we get the paths drawn at the appropriate depth
83 //connect(&threshold, SIGNAL(thresholdChanged(int)),
84 // this, SLOT(setClipPlane(int)));
85 connect(&threshold, SIGNAL(thresholdChanged(int)),
86 this, SLOT(setSoftThreshold(int)));
87 mussaViewTB.addWidget(&threshold);
89 addToolBar(&mussaViewTB);
91 statusBar()->showMessage("Welcome to mussa", 2000);
92 connect(analysis, SIGNAL(progress(const std::string&, int, int)),
93 this, SLOT(updateProgress(const std::string&, int, int)));
97 MussaWindow::~MussaWindow()
99 if (analysis != 0) delete analysis;
100 aligned_windows.clear();
101 if (motif_editor != 0) delete motif_editor;
102 if (progress_dialog != 0) delete progress_dialog;
104 if (aboutAction != 0) delete aboutAction;
105 if (closeAction != 0) delete closeAction;
106 if (createNewAnalysisAction != 0) delete createNewAnalysisAction;
107 if (createSubAnalysisAction != 0) delete createSubAnalysisAction;
108 if (editMotifsAction != 0) delete editMotifsAction;
109 if (loadMotifListAction != 0) delete loadMotifListAction;
110 if (loadMupaAction != 0) delete loadMupaAction;
111 if (loadSavedAnalysisAction != 0) delete loadSavedAnalysisAction;
112 if (mussaManualAssistantAction != 0) delete mussaManualAssistantAction;
113 if (newMussaWindowAction != 0) delete newMussaWindowAction;
114 if (saveBrowserPixmapAction != 0) delete saveBrowserPixmapAction;
115 if (saveMotifListAction != 0) delete saveMotifListAction;
116 if (showMussaViewToolbarAction != 0) delete showMussaViewToolbarAction;
117 if (toggleMotifsAction != 0) delete toggleMotifsAction;
118 if (whatsThisAction != 0) delete whatsThisAction;
119 if (viewMussaAlignmentAction != 0) delete viewMussaAlignmentAction;
121 if (manualAssistant != 0) delete manualAssistant;
125 void MussaWindow::setAnalysis(Mussa *new_analysis)
127 if (new_analysis != 0) {
128 // only switch mussas if we loaded without error
131 analysis = new_analysis;
132 setWindowTitle(analysis->get_name().c_str());
137 void MussaWindow::setupActions()
139 // we really don't want to run this more than once.
140 assert (closeAction == 0);
142 // the ever popular about box
143 aboutAction = new QAction(tr("&About"), this);
144 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
145 aboutAction->setIcon(QIcon(":/icons/info.png"));
148 closeAction = new QAction(tr("&Close"), this);
149 closeAction->setStatusTip(tr("Close this window"));
150 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
151 closeAction->setIcon(QIcon(":/icons/exit.png"));
153 createNewAnalysisAction = new QAction(tr("Create Analysis"), this);
154 connect(createNewAnalysisAction, SIGNAL(triggered()),
155 this, SLOT(createNewAnalysis()));
156 createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
158 createSubAnalysisAction = new QAction(tr("Add to Subanalysis"), this);
159 connect(createSubAnalysisAction, SIGNAL(triggered()),
160 this, SLOT(createSubAnalysis()));
162 editMotifsAction = new QAction(tr("Edit Motifs"), this);;
163 connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
165 loadMotifListAction = new QAction(tr("Load Motif List"), this);
166 connect(loadMotifListAction, SIGNAL(triggered()),
167 this, SLOT(loadMotifList()));
168 loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
170 loadMupaAction = new QAction(tr("Create Analysis from File"), this);
171 connect(loadMupaAction, SIGNAL(triggered()),
172 this, SLOT(loadMupa()));
173 loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
175 loadSavedAnalysisAction = new QAction(tr("Load Existing &Analysis"), this);
176 connect(loadSavedAnalysisAction, SIGNAL(triggered()),
177 this, SLOT(loadSavedAnalysis()));
178 loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
180 mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
181 mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
182 connect(mussaManualAssistantAction, SIGNAL(triggered()),
183 this, SLOT(showManual()));
185 newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
186 newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
187 connect(newMussaWindowAction, SIGNAL(triggered()),
188 this, SLOT(newMussaWindow()));
189 newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
191 saveMotifListAction = new QAction(tr("Save Motifs"), this);
192 connect(saveMotifListAction, SIGNAL(triggered()),
193 this, SLOT(saveMotifList()));
194 saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
196 showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
197 connect(showMussaViewToolbarAction, SIGNAL(triggered()),
198 this, SLOT(showMussaToolbar()));
199 showMussaViewToolbarAction->setCheckable(true);
200 showMussaViewToolbarAction->setChecked(true);
202 toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
203 connect(toggleMotifsAction, SIGNAL(triggered()),
204 this, SLOT(toggleMotifs()));
205 toggleMotifsAction->setCheckable(true);
206 toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
207 toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
208 "You can load motif annotations via "
209 "'File->Load Motif List' menu option."));
211 //Save pixel map action
212 saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
213 connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
214 &browser, SLOT(promptSaveBrowserPixmap()));
215 saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
217 viewMussaAlignmentAction = new QAction(tr("View mussa alignment"), this);
218 connect(viewMussaAlignmentAction, SIGNAL(triggered()),
219 this, SLOT(viewMussaAlignment() ));
220 viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
221 "showing alignment of the seqcomp "
224 whatsThisAction = QWhatsThis::createAction(this);
225 whatsThisAction->setIcon(QIcon(":/icons/help.png"));
230 void MussaWindow::setupMainMenu()
232 // we need to run setupActions first
233 assert (closeAction != 0);
235 QMenu *newMenu = menuBar()->addMenu(tr("&File"));
237 newMenu->addAction(newMussaWindowAction);
238 newMenu->addAction(createNewAnalysisAction);
239 newMenu->addAction(loadMupaAction);
240 newMenu->addAction(loadSavedAnalysisAction);
241 newMenu->addSeparator();
242 newMenu->addAction(loadMotifListAction);
243 newMenu->addAction(saveMotifListAction);
244 newMenu->addSeparator();
245 newMenu->addAction(saveBrowserPixmapAction);
246 newMenu->addSeparator();
247 newMenu->addAction(closeAction);
249 newMenu = menuBar()->addMenu(tr("&Edit"));
250 newMenu->addAction(&browser.getCopySelectedSequenceAsFastaAction());
251 newMenu->addAction(createSubAnalysisAction);
253 newMenu = menuBar()->addMenu(tr("&View"));
254 newMenu->addAction(editMotifsAction);
255 newMenu->addAction(viewMussaAlignmentAction);
256 newMenu->addAction(showMussaViewToolbarAction);
258 newMenu = menuBar()->addMenu(tr("&Help"));
259 #if defined(QT_ASSISTANT_LIB)
260 newMenu->addAction(mussaManualAssistantAction);
262 newMenu->addAction(whatsThisAction);
263 newMenu->addSeparator();
264 newMenu->addAction(aboutAction);
266 // add some extra features to the context menu
267 QMenu& popupMenu = browser.getPopupMenu();
268 popupMenu.addAction(viewMussaAlignmentAction);
269 popupMenu.addAction(createSubAnalysisAction);
272 void MussaWindow::setupAssistant()
274 #if 0 && defined(Q_WS_MAC)
275 CFURLRef pluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
276 CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef,
277 kCFURLPOSIXPathStyle);
278 const char *pathPtr = CFStringGetCStringPtr(macPath,
279 CFStringGetSystemEncoding());
280 qDebug("Path = %s", pathPtr);
281 CFRelease(pluginRef);
284 #if defined(QT_ASSISTANT_LIB)
285 QStringList manualAssistantArgs;
286 manualAssistantArgs = QStringList();
287 manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
288 manualAssistant = new QAssistantClient("assistant", this);
289 manualAssistant->setArguments(manualAssistantArgs);
290 connect(manualAssistant, SIGNAL(error(QString)),
291 this, SLOT(assistantError(QString)));
295 void MussaWindow::about()
297 QString msg("Welcome to Multiple Species Sequence Analysis\n"
298 "(c) 2005-2006 California Institute of Technology\n"
299 "Tristan De Buysscher, Diane Trout\n");
302 msg += QDir::currentPath();
305 msg += (char *)glGetString(GL_VERSION);
307 QMessageBox::about(this, tr("About mussa"), msg);
310 void MussaWindow::clear()
312 aligned_windows.clear();
316 void MussaWindow::createNewAnalysis()
319 if (setup_analysis_dialog.exec()) {
321 m = setup_analysis_dialog.getMussa();
324 std::cout << "New mussa exp. aborted!\n";
326 } catch(mussa_error e) {
327 QString msg(e.what());
328 QMessageBox::warning(this, tr("Create New Analysis"), msg);
332 void MussaWindow::createSubAnalysis()
334 if (not subanalysis_window.isVisible()) {
335 subanalysis_window.show();
339 void MussaWindow::editMotifs()
341 if (motif_editor != 0) {
342 motif_editor->hide();
345 motif_editor = new MotifEditor(analysis);
346 connect(motif_editor, SIGNAL(changedMotifs()),
347 this, SLOT(updateAnnotations()));
348 motif_editor->show();
351 void MussaWindow::loadMotifList()
353 QString caption("Load a motif list");
354 QString filter("Motif list(*.txt *.mtl)");
355 QString path = QFileDialog::getOpenFileName(this,
362 // try to load safely
364 fs::path converted_path(path.toStdString(), fs::native);
365 analysis->load_motifs(converted_path);
366 } catch (runtime_error e) {
367 QString msg("Unable to load ");
371 QMessageBox::warning(this, "Load Motifs", msg);
373 assert (analysis != 0);
376 void MussaWindow::saveMotifList()
381 void MussaWindow::loadMupa()
383 QString caption("Load a mussa parameter file");
384 QString filter("Mussa Parameters (*.mupa)");
385 QString mupa_path = QFileDialog::getOpenFileName(this,
390 if (mupa_path.isNull())
392 // try to load safely
394 Mussa *m = new Mussa;
395 fs::path converted_path(mupa_path.toStdString(), fs::native);
396 connect(m, SIGNAL(progress(const std::string&, int, int)),
397 this, SLOT(updateProgress(const std::string&, int, int)));
398 m->load_mupa_file(converted_path);
401 setWindowTitle(converted_path.native_file_string().c_str());
402 } catch (mussa_load_error e) {
403 QString msg("Unable to load ");
407 QMessageBox::warning(this, "Load Parameter", msg);
409 assert (analysis != 0);
412 void MussaWindow::loadSavedAnalysis()
414 QString caption("Load a previously run analysis");
415 QString muway_dir = QFileDialog::getExistingDirectory(this,
417 QDir::currentPath());
419 if (muway_dir.isNull())
421 // try to safely load
423 Mussa *m = new Mussa;
424 fs::path converted_path(muway_dir.toStdString(), fs::native);
425 connect(m, SIGNAL(progress(const std::string&, int, int)),
426 this, SLOT(updateProgress(const std::string&, int, int)));
427 m->load(converted_path);
428 // only switch mussas if we loaded without error
430 setWindowTitle(converted_path.native_file_string().c_str());
431 } catch (mussa_load_error e) {
432 QString msg("Unable to load ");
436 QMessageBox::warning(this, "Load Parameter", msg);
438 assert (analysis != 0);
441 void MussaWindow::newMussaWindow()
443 Mussa *a = new Mussa();
444 MussaWindow *win = new MussaWindow(a);
448 void MussaWindow::setSoftThreshold(int threshold)
450 if (analysis->get_soft_threshold() != threshold) {
451 analysis->set_soft_threshold(threshold);
458 void MussaWindow::showMussaToolbar()
460 if (mussaViewTB.isVisible())
466 void MussaWindow::toggleMotifs()
471 void MussaWindow::showManual()
473 manualAssistant->openAssistant();
476 void MussaWindow::assistantError(QString message)
478 //std::cout << "QAssistantError: " << message.toStdString() << "\n";
479 QMessageBox::warning ( this, "Warning: Mussagl Manual", message,
481 QMessageBox::NoButton);
484 void MussaWindow::NotImplementedBox()
486 QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
489 void MussaWindow::viewMussaAlignment()
491 const set<int>& selected_paths = browser.selectedPaths();
492 if (selected_paths.size() == 0 ) {
493 QMessageBox::warning(this,
494 QObject::tr("mussa"),
495 QObject::tr("you should probably select some paths "
498 boost::shared_ptr<MussaAlignedWindow> ma_win(
499 new MussaAlignedWindow(*analysis, selected_paths)
502 aligned_windows.push_back(ma_win);
503 connect(this, SIGNAL(changedAnnotations()),
504 aligned_windows.back().get(), SLOT(update()));
505 aligned_windows.back()->show();
509 void MussaWindow::updateAnalysis()
511 threshold.setRange(analysis->get_threshold(),analysis->get_window());
513 const vector<Sequence>& seqs = analysis->sequences();
514 browser.setSequences(seqs, analysis->colorMapper());
517 zoom.setValue(browser.zoom());
520 void MussaWindow::updateAnnotations()
522 // motifs were changed in the sequences by
523 // Mussa::update_sequences_motifs
524 emit changedAnnotations();
528 void MussaWindow::updateLinks()
530 browser.clear_links();
531 bool reversed = false;
532 const NwayPaths& nway = analysis->paths();
534 typedef list<ConservedPath> conserved_paths;
535 typedef conserved_paths::const_iterator const_conserved_paths_itor;
536 for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
537 path_itor != nway.refined_pathz.end();
540 // since we were drawing to the start of a window, and opengl lines
541 // are centered around the two connecting points our lines were slightly
542 // offset. the idea of window_offset is to adjust them to the
543 // right for forward compliment or left for reverse compliment
544 // FIXME: figure out how to unit test these computations
545 //GLfloat window_offset = (path_itor->window_size)/2.0;
547 size_t track_len = path_itor->track_indexes.size();
548 vector<int> normalized_path;
549 normalized_path.reserve(track_len);
550 vector<bool> rc_flags(false, track_len);
551 for (size_t track_i=0; track_i != track_len; ++track_i)
553 int x = path_itor->track_indexes[track_i];
554 // at some point when we modify the pathz data structure to keep
555 // track of the score we can put grab the depth here.
557 // are we reverse complimented?
565 normalized_path.push_back(x);
566 rc_flags.push_back(reversed);
568 browser.link(normalized_path, rc_flags, path_itor->window_size);
574 MussaWindow::updateProgress(const string& description, int current, int max)
577 if (current == max) {
578 if (progress_dialog != 0) {
579 progress_dialog->hide();
580 delete progress_dialog;
584 // if we're starting, create the dialog
585 if (progress_dialog == 0) {
586 QString desc(description.c_str());
587 QString cancel("Cancel");
588 progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
589 progress_dialog->show();
591 // just update the dialog
592 progress_dialog->setValue(current);
595 qApp->processEvents();