2 #include <QApplication>
3 #include <QAssistantClient>
13 #include <QStringList>
17 #include <CoreFoundation/CoreFoundation.h>
20 #include "qui/MussaWindow.hpp"
21 #include "mussa_exceptions.hpp"
27 #include <boost/filesystem/path.hpp>
28 namespace fs = boost::filesystem;
29 #include <boost/bind.hpp>
33 MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
37 setup_analysis_dialog(this),
39 mussaViewTB("Path Views"),
45 createNewAnalysisAction(0),
46 createSubAnalysisAction(0),
48 loadMotifListAction(0),
50 loadSavedAnalysisAction(0),
51 mussaManualAssistantAction(0),
52 newMussaWindowAction(0),
53 saveMotifListAction(0),
54 showMussaViewToolbarAction(0),
55 toggleMotifsAction(0),
56 saveBrowserPixmapAction(0),
58 viewMussaAlignmentAction(0),
65 //This next setWhatsThis function prevents
66 // a segfault when using WhatsThis feature with
68 //scene->setWhatsThis(tr("Mussa in OpenGL!"));
69 setCentralWidget(&browser);
70 // well updatePosition isn't quite right as we really just need
72 connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update()));
74 mussaViewTB.addAction(toggleMotifsAction);
75 mussaViewTB.addWidget(&zoom);
77 connect(&zoom, SIGNAL(valueChanged(double)),
78 &browser, SLOT(setZoom(double)));
80 // threshold range is set in updateAnalysis
82 //scene->setClipPlane(20);
83 // FIXME: for when we get the paths drawn at the appropriate depth
84 //connect(&threshold, SIGNAL(thresholdChanged(int)),
85 // this, SLOT(setClipPlane(int)));
86 connect(&threshold, SIGNAL(thresholdChanged(int)),
87 this, SLOT(setSoftThreshold(int)));
88 mussaViewTB.addWidget(&threshold);
90 addToolBar(&mussaViewTB);
92 statusBar()->showMessage("Welcome to mussa", 2000);
93 connect(analysis, SIGNAL(progress(const std::string&, int, int)),
94 this, SLOT(updateProgress(const std::string&, int, int)));
98 MussaWindow::~MussaWindow()
100 if (analysis != 0) delete analysis;
101 aligned_windows.clear();
102 if (motif_editor != 0) delete motif_editor;
103 if (progress_dialog != 0) delete progress_dialog;
105 if (aboutAction != 0) delete aboutAction;
106 if (closeAction != 0) delete closeAction;
107 if (createNewAnalysisAction != 0) delete createNewAnalysisAction;
108 if (createSubAnalysisAction != 0) delete createSubAnalysisAction;
109 if (editMotifsAction != 0) delete editMotifsAction;
110 if (loadMotifListAction != 0) delete loadMotifListAction;
111 if (loadMupaAction != 0) delete loadMupaAction;
112 if (loadSavedAnalysisAction != 0) delete loadSavedAnalysisAction;
113 if (mussaManualAssistantAction != 0) delete mussaManualAssistantAction;
114 if (newMussaWindowAction != 0) delete newMussaWindowAction;
115 if (saveBrowserPixmapAction != 0) delete saveBrowserPixmapAction;
116 if (saveMotifListAction != 0) delete saveMotifListAction;
117 if (showMussaViewToolbarAction != 0) delete showMussaViewToolbarAction;
118 if (toggleMotifsAction != 0) delete toggleMotifsAction;
119 if (whatsThisAction != 0) delete whatsThisAction;
120 if (viewMussaAlignmentAction != 0) delete viewMussaAlignmentAction;
122 if (manualAssistant != 0) delete manualAssistant;
126 void MussaWindow::setAnalysis(Mussa *new_analysis)
128 if (new_analysis != 0) {
129 // only switch mussas if we loaded without error
132 analysis = new_analysis;
133 setWindowTitle(analysis->get_name().c_str());
138 void MussaWindow::setupActions()
140 // we really don't want to run this more than once.
141 assert (closeAction == 0);
143 // the ever popular about box
144 aboutAction = new QAction(tr("&About"), this);
145 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
146 aboutAction->setIcon(QIcon(":/icons/info.png"));
149 closeAction = new QAction(tr("&Close"), this);
150 closeAction->setStatusTip(tr("Close this window"));
151 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
152 closeAction->setIcon(QIcon(":/icons/exit.png"));
154 createNewAnalysisAction = new QAction(tr("Create Analysis"), this);
155 connect(createNewAnalysisAction, SIGNAL(triggered()),
156 this, SLOT(createNewAnalysis()));
157 createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
159 createSubAnalysisAction = new QAction(tr("Add to Subanalysis"), this);
160 connect(createSubAnalysisAction, SIGNAL(triggered()),
161 this, SLOT(createSubAnalysis()));
163 editMotifsAction = new QAction(tr("Edit Motifs"), this);;
164 connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
166 loadMotifListAction = new QAction(tr("Load Motif List"), this);
167 connect(loadMotifListAction, SIGNAL(triggered()),
168 this, SLOT(loadMotifList()));
169 loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
171 loadMupaAction = new QAction(tr("Create Analysis from File"), this);
172 connect(loadMupaAction, SIGNAL(triggered()),
173 this, SLOT(loadMupa()));
174 loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
176 loadSavedAnalysisAction = new QAction(tr("Load Existing &Analysis"), this);
177 connect(loadSavedAnalysisAction, SIGNAL(triggered()),
178 this, SLOT(loadSavedAnalysis()));
179 loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
181 mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
182 mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
183 connect(mussaManualAssistantAction, SIGNAL(triggered()),
184 this, SLOT(showManual()));
186 newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
187 newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
188 connect(newMussaWindowAction, SIGNAL(triggered()),
189 this, SLOT(newMussaWindow()));
190 newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
192 saveMotifListAction = new QAction(tr("Save Motifs"), this);
193 connect(saveMotifListAction, SIGNAL(triggered()),
194 this, SLOT(saveMotifList()));
195 saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
197 showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
198 connect(showMussaViewToolbarAction, SIGNAL(triggered()),
199 this, SLOT(showMussaToolbar()));
200 showMussaViewToolbarAction->setCheckable(true);
201 showMussaViewToolbarAction->setChecked(true);
203 toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
204 connect(toggleMotifsAction, SIGNAL(triggered()),
205 this, SLOT(toggleMotifs()));
206 toggleMotifsAction->setCheckable(true);
207 toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
208 toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
209 "You can load motif annotations via "
210 "'File->Load Motif List' menu option."));
212 //Save pixel map action
213 saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
214 connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
215 &browser, SLOT(promptSaveBrowserPixmap()));
216 saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
218 viewMussaAlignmentAction = new QAction(tr("View sequence alignment"), this);
219 connect(viewMussaAlignmentAction, SIGNAL(triggered()),
220 this, SLOT(viewMussaAlignment() ));
221 viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
222 "showing alignment of the seqcomp "
225 whatsThisAction = QWhatsThis::createAction(this);
226 whatsThisAction->setIcon(QIcon(":/icons/help.png"));
231 void MussaWindow::setupMainMenu()
233 // we need to run setupActions first
234 assert (closeAction != 0);
236 QMenu *newMenu = menuBar()->addMenu(tr("&File"));
238 newMenu->addAction(newMussaWindowAction);
239 newMenu->addAction(createNewAnalysisAction);
240 newMenu->addAction(loadMupaAction);
241 newMenu->addAction(loadSavedAnalysisAction);
242 newMenu->addSeparator();
243 newMenu->addAction(loadMotifListAction);
244 newMenu->addAction(saveMotifListAction);
245 newMenu->addSeparator();
246 newMenu->addAction(saveBrowserPixmapAction);
247 newMenu->addSeparator();
248 newMenu->addAction(closeAction);
250 newMenu = menuBar()->addMenu(tr("&Edit"));
251 newMenu->addAction(editMotifsAction);
252 newMenu->addAction(&browser.getCopySelectedSequenceAsFastaAction());
253 newMenu->addAction(createSubAnalysisAction);
255 newMenu = menuBar()->addMenu(tr("&View"));
256 newMenu->addAction(viewMussaAlignmentAction);
257 newMenu->addAction(showMussaViewToolbarAction);
259 newMenu = menuBar()->addMenu(tr("&Help"));
260 #if defined(QT_ASSISTANT_LIB)
261 newMenu->addAction(mussaManualAssistantAction);
263 newMenu->addAction(whatsThisAction);
264 newMenu->addSeparator();
265 newMenu->addAction(aboutAction);
267 // add some extra features to the context menu
268 QMenu& popupMenu = browser.getPopupMenu();
269 popupMenu.addAction(viewMussaAlignmentAction);
270 popupMenu.addAction(createSubAnalysisAction);
273 void MussaWindow::setupAssistant()
275 #if 0 && defined(Q_WS_MAC)
276 CFURLRef pluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
277 CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef,
278 kCFURLPOSIXPathStyle);
279 const char *pathPtr = CFStringGetCStringPtr(macPath,
280 CFStringGetSystemEncoding());
281 qDebug("Path = %s", pathPtr);
282 CFRelease(pluginRef);
285 #if defined(QT_ASSISTANT_LIB)
286 QStringList manualAssistantArgs;
287 manualAssistantArgs = QStringList();
288 manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
289 manualAssistant = new QAssistantClient("assistant", this);
290 manualAssistant->setArguments(manualAssistantArgs);
291 connect(manualAssistant, SIGNAL(error(QString)),
292 this, SLOT(assistantError(QString)));
296 void MussaWindow::about()
298 QString msg("Welcome to Multiple Species Sequence Analysis\n"
299 "(c) 2005-2006 California Institute of Technology\n"
300 "Tristan De Buysscher, Diane Trout\n");
303 msg += QDir::currentPath();
306 msg += (char *)glGetString(GL_VERSION);
308 QMessageBox::about(this, tr("About mussa"), msg);
311 void MussaWindow::clear()
313 aligned_windows.clear();
317 void MussaWindow::createNewAnalysis()
320 if (setup_analysis_dialog.exec()) {
322 m = setup_analysis_dialog.getMussa();
325 std::cout << "New mussa exp. aborted!\n";
327 } catch(mussa_error e) {
328 QString msg(e.what());
329 QMessageBox::warning(this, tr("Create New Analysis"), msg);
333 void MussaWindow::createSubAnalysis()
335 list<SequenceLocation> result;
336 SequenceLocationModel& model = subanalysis_window.getModel();
337 browser.copySelectedTracksAsSeqLocation(result);
338 for(list<SequenceLocation>::iterator result_itor = result.begin();
339 result_itor != result.end();
342 model.push_back(*result_itor);
345 if (not subanalysis_window.isVisible()) {
346 subanalysis_window.show();
350 void MussaWindow::editMotifs()
352 if (motif_editor != 0) {
353 motif_editor->hide();
356 motif_editor = new MotifEditor(analysis);
357 connect(motif_editor, SIGNAL(changedMotifs()),
358 this, SLOT(updateAnnotations()));
359 motif_editor->show();
362 void MussaWindow::loadMotifList()
364 QString caption("Load a motif list");
365 QString filter("Motif list(*.txt *.mtl)");
366 QString path = QFileDialog::getOpenFileName(this,
373 // try to load safely
375 fs::path converted_path(path.toStdString(), fs::native);
376 analysis->load_motifs(converted_path);
377 } catch (runtime_error e) {
378 QString msg("Unable to load ");
382 QMessageBox::warning(this, "Load Motifs", msg);
384 assert (analysis != 0);
387 void MussaWindow::saveMotifList()
392 void MussaWindow::loadMupa()
394 QString caption("Load a mussa parameter file");
395 QString filter("Mussa Parameters (*.mupa)");
396 QString mupa_path = QFileDialog::getOpenFileName(this,
401 if (mupa_path.isNull())
403 // try to load safely
405 Mussa *m = new Mussa;
406 fs::path converted_path(mupa_path.toStdString(), fs::native);
407 connect(m, SIGNAL(progress(const std::string&, int, int)),
408 this, SLOT(updateProgress(const std::string&, int, int)));
409 m->load_mupa_file(converted_path);
412 setWindowTitle(converted_path.native_file_string().c_str());
413 } catch (mussa_load_error e) {
414 QString msg("Unable to load ");
418 QMessageBox::warning(this, "Load Parameter", msg);
420 assert (analysis != 0);
423 void MussaWindow::loadSavedAnalysis()
425 QString caption("Load a previously run analysis");
426 QString muway_dir = QFileDialog::getExistingDirectory(this,
428 QDir::currentPath());
430 if (muway_dir.isNull())
432 // try to safely load
434 Mussa *m = new Mussa;
435 fs::path converted_path(muway_dir.toStdString(), fs::native);
436 connect(m, SIGNAL(progress(const std::string&, int, int)),
437 this, SLOT(updateProgress(const std::string&, int, int)));
438 m->load(converted_path);
439 // only switch mussas if we loaded without error
441 setWindowTitle(converted_path.native_file_string().c_str());
442 } catch (mussa_load_error e) {
443 QString msg("Unable to load ");
447 QMessageBox::warning(this, "Load Parameter", msg);
449 assert (analysis != 0);
452 void MussaWindow::newMussaWindow()
454 Mussa *a = new Mussa();
455 MussaWindow *win = new MussaWindow(a);
459 void MussaWindow::setSoftThreshold(int threshold)
461 if (analysis->get_soft_threshold() != threshold) {
462 analysis->set_soft_threshold(threshold);
469 void MussaWindow::showMussaToolbar()
471 if (mussaViewTB.isVisible())
477 void MussaWindow::toggleMotifs()
482 void MussaWindow::showManual()
484 manualAssistant->openAssistant();
487 void MussaWindow::assistantError(QString message)
489 //std::cout << "QAssistantError: " << message.toStdString() << "\n";
490 QMessageBox::warning ( this, "Warning: Mussagl Manual", message,
492 QMessageBox::NoButton);
495 void MussaWindow::NotImplementedBox()
497 QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
500 void MussaWindow::viewMussaAlignment()
502 const set<int>& selected_paths = browser.selectedPaths();
503 if (selected_paths.size() == 0 ) {
504 QMessageBox::warning(this,
505 QObject::tr("mussa"),
506 QObject::tr("you should probably select some paths "
509 boost::shared_ptr<MussaAlignedWindow> ma_win(
510 new MussaAlignedWindow(*analysis, selected_paths)
513 aligned_windows.push_back(ma_win);
514 connect(this, SIGNAL(changedAnnotations()),
515 aligned_windows.back().get(), SLOT(update()));
516 aligned_windows.back()->show();
520 void MussaWindow::updateAnalysis()
522 threshold.setRange(analysis->get_threshold(),analysis->get_window());
524 const Mussa::vector_sequence_type& seqs = analysis->sequences();
525 browser.setSequences(seqs, analysis->colorMapper());
528 zoom.setValue(browser.zoom());
531 void MussaWindow::updateAnnotations()
533 // motifs were changed in the sequences by
534 // Mussa::update_sequences_motifs
535 emit changedAnnotations();
539 void MussaWindow::updateLinks()
541 browser.clear_links();
542 bool reversed = false;
543 const NwayPaths& nway = analysis->paths();
545 typedef list<ConservedPath> conserved_paths;
546 typedef conserved_paths::const_iterator const_conserved_paths_itor;
547 for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
548 path_itor != nway.refined_pathz.end();
551 // since we were drawing to the start of a window, and opengl lines
552 // are centered around the two connecting points our lines were slightly
553 // offset. the idea of window_offset is to adjust them to the
554 // right for forward compliment or left for reverse compliment
555 // FIXME: figure out how to unit test these computations
556 //GLfloat window_offset = (path_itor->window_size)/2.0;
558 size_t track_len = path_itor->track_indexes.size();
559 vector<int> normalized_path;
560 normalized_path.reserve(track_len);
561 vector<bool> rc_flags(false, track_len);
562 for (size_t track_i=0; track_i != track_len; ++track_i)
564 int x = path_itor->track_indexes[track_i];
565 // at some point when we modify the pathz data structure to keep
566 // track of the score we can put grab the depth here.
568 // are we reverse complimented?
576 normalized_path.push_back(x);
577 rc_flags.push_back(reversed);
579 browser.link(normalized_path, rc_flags, path_itor->window_size);
585 MussaWindow::updateProgress(const string& description, int current, int max)
588 if (current == max) {
589 if (progress_dialog != 0) {
590 progress_dialog->hide();
591 delete progress_dialog;
595 // if we're starting, create the dialog
596 if (progress_dialog == 0) {
597 QString desc(description.c_str());
598 QString cancel("Cancel");
599 progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
600 progress_dialog->show();
602 // just update the dialog
603 progress_dialog->setValue(current);
606 qApp->processEvents();