1 #include "py/python.hpp"
2 #include "qui/MussaWindow.hpp"
3 #include "mussa_exceptions.hpp"
6 #include <QApplication>
7 #include <QAssistantClient>
10 #include <QHBoxLayout>
13 #include <QMessageBox>
17 #include <QStringList>
24 #include <boost/filesystem/path.hpp>
25 namespace fs = boost::filesystem;
26 #include <boost/bind.hpp>
30 MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
34 setup_analysis_dialog(this),
36 mussaViewTB("Path Views"),
42 createNewAnalysisAction(0),
43 createSubAnalysisAction(0),
45 loadMotifListAction(0),
47 loadSavedAnalysisAction(0),
48 mussaManualAssistantAction(0),
49 newMussaWindowAction(0),
50 saveMotifListAction(0),
51 showMussaViewToolbarAction(0),
52 toggleMotifsAction(0),
53 saveBrowserPixmapAction(0),
55 viewMussaAlignmentAction(0),
62 //This next setWhatsThis function prevents
63 // a segfault when using WhatsThis feature with
65 //scene->setWhatsThis(tr("Mussa in OpenGL!"));
66 setCentralWidget(&browser);
67 // well updatePosition isn't quite right as we really just need
69 connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update()));
71 //mussaViewTB.addAction(toggleMotifsAction);
72 mussaViewTB.addWidget(&zoom);
74 connect(&zoom, SIGNAL(valueChanged(double)),
75 &browser, SLOT(setZoom(double)));
77 // threshold range is set in updateAnalysis
79 //scene->setClipPlane(20);
80 // FIXME: for when we get the paths drawn at the appropriate depth
81 //connect(&threshold, SIGNAL(thresholdChanged(int)),
82 // this, SLOT(setClipPlane(int)));
83 connect(&threshold, SIGNAL(thresholdChanged(int)),
84 this, SLOT(setSoftThreshold(int)));
85 mussaViewTB.addWidget(&threshold);
87 addToolBar(&mussaViewTB);
89 statusBar()->showMessage("Welcome to mussa", 2000);
90 connect(analysis, SIGNAL(progress(const std::string&, int, int)),
91 this, SLOT(updateProgress(const std::string&, int, int)));
95 void MussaWindow::setAnalysis(Mussa *new_analysis)
97 if (new_analysis != 0) {
98 // only switch mussas if we loaded without error
101 analysis = new_analysis;
102 setWindowTitle(analysis->get_name().c_str());
107 void MussaWindow::setupActions()
109 // we really don't want to run this more than once.
110 assert (closeAction == 0);
112 // the ever popular about box
113 aboutAction = new QAction(tr("&About"), this);
114 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
115 aboutAction->setIcon(QIcon(":/icons/info.png"));
118 closeAction = new QAction(tr("&Close"), this);
119 closeAction->setStatusTip(tr("Close this window"));
120 connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
121 closeAction->setIcon(QIcon(":/icons/exit.png"));
123 createNewAnalysisAction = new QAction(tr("Create Analysis"), this);
124 connect(createNewAnalysisAction, SIGNAL(triggered()),
125 this, SLOT(createNewAnalysis()));
126 createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
128 createSubAnalysisAction = new QAction(tr("Add to Subanalysis"), this);
129 connect(createSubAnalysisAction, SIGNAL(triggered()),
130 this, SLOT(createSubAnalysis()));
132 editMotifsAction = new QAction(tr("Edit Motifs"), this);;
133 connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
135 loadMotifListAction = new QAction(tr("Load Motif List"), this);
136 connect(loadMotifListAction, SIGNAL(triggered()),
137 this, SLOT(loadMotifList()));
138 loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
140 loadMupaAction = new QAction(tr("Create Analysis from File"), this);
141 connect(loadMupaAction, SIGNAL(triggered()),
142 this, SLOT(loadMupa()));
143 loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
145 loadSavedAnalysisAction = new QAction(tr("Load Existing &Analysis"), this);
146 connect(loadSavedAnalysisAction, SIGNAL(triggered()),
147 this, SLOT(loadSavedAnalysis()));
148 loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
150 mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
151 mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
152 connect(mussaManualAssistantAction, SIGNAL(triggered()),
153 this, SLOT(showManual()));
155 newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
156 newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
157 connect(newMussaWindowAction, SIGNAL(triggered()),
158 this, SLOT(newMussaWindow()));
159 newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
161 saveMotifListAction = new QAction(tr("Save Motifs"), this);
162 connect(saveMotifListAction, SIGNAL(triggered()),
163 this, SLOT(saveMotifList()));
164 saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
166 showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
167 connect(showMussaViewToolbarAction, SIGNAL(triggered()),
168 this, SLOT(showMussaToolbar()));
169 showMussaViewToolbarAction->setCheckable(true);
170 showMussaViewToolbarAction->setChecked(true);
172 toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
173 connect(toggleMotifsAction, SIGNAL(triggered()),
174 this, SLOT(toggleMotifs()));
175 toggleMotifsAction->setCheckable(true);
176 toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
177 toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
178 "You can load motif annotations via "
179 "'File->Load Motif List' menu option."));
181 //Save pixel map action
182 saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
183 connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
184 &browser, SLOT(promptSaveBrowserPixmap()));
185 saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
187 viewMussaAlignmentAction = new QAction(tr("View sequence alignment"), this);
188 connect(viewMussaAlignmentAction, SIGNAL(triggered()),
189 this, SLOT(viewMussaAlignment() ));
190 viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
191 "showing alignment of the seqcomp "
194 whatsThisAction = QWhatsThis::createAction(this);
195 whatsThisAction->setIcon(QIcon(":/icons/help.png"));
200 void MussaWindow::setupMainMenu()
202 // we need to run setupActions first
203 assert (closeAction != 0);
205 QMenu *newMenu = menuBar()->addMenu(tr("&File"));
207 newMenu->addAction(newMussaWindowAction);
208 newMenu->addAction(createNewAnalysisAction);
209 newMenu->addAction(loadMupaAction);
210 newMenu->addAction(loadSavedAnalysisAction);
211 newMenu->addSeparator();
212 newMenu->addAction(loadMotifListAction);
213 newMenu->addAction(saveMotifListAction);
214 newMenu->addSeparator();
215 newMenu->addAction(saveBrowserPixmapAction);
216 newMenu->addSeparator();
217 newMenu->addAction(closeAction);
219 newMenu = menuBar()->addMenu(tr("&Edit"));
220 newMenu->addAction(editMotifsAction);
221 newMenu->addAction(&browser.getCopySelectedSequenceAsFastaAction());
222 newMenu->addAction(createSubAnalysisAction);
224 newMenu = menuBar()->addMenu(tr("&View"));
225 newMenu->addAction(viewMussaAlignmentAction);
226 newMenu->addAction(showMussaViewToolbarAction);
228 newMenu = menuBar()->addMenu(tr("&Help"));
229 newMenu->addAction(mussaManualAssistantAction);
230 newMenu->addAction(whatsThisAction);
231 newMenu->addSeparator();
232 newMenu->addAction(aboutAction);
234 // add some extra features to the context menu
235 QMenu& popupMenu = browser.getPopupMenu();
236 popupMenu.addAction(viewMussaAlignmentAction);
237 popupMenu.addAction(createSubAnalysisAction);
240 void MussaWindow::setupAssistant()
242 #if 0 && defined(Q_WS_MAC)
243 CFURLRef pluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
244 CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef,
245 kCFURLPOSIXPathStyle);
246 const char *pathPtr = CFStringGetCStringPtr(macPath,
247 CFStringGetSystemEncoding());
248 qDebug("Path = %s", pathPtr);
249 CFRelease(pluginRef);
252 #if defined(QT_ASSISTANT_LIB)
253 QStringList manualAssistantArgs;
254 manualAssistantArgs = QStringList();
255 manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
256 manualAssistant = new QAssistantClient("assistant", this);
257 manualAssistant->setArguments(manualAssistantArgs);
258 connect(manualAssistant, SIGNAL(error(QString)),
259 this, SLOT(assistantError(QString)));
263 void MussaWindow::about()
265 QString msg("Welcome to Multiple Species Sequence Analysis\n"
266 "(c) 2005-2006 California Institute of Technology\n"
267 "Tristan De Buysscher, Diane Trout\n");
270 msg += QDir::currentPath();
273 msg += (char *)glGetString(GL_VERSION);
275 QMessageBox::about(this, tr("About mussa"), msg);
278 void MussaWindow::clear()
280 aligned_windows.clear();
284 void MussaWindow::createNewAnalysis()
287 if (setup_analysis_dialog.exec()) {
289 m = setup_analysis_dialog.getMussa();
292 std::cout << "New mussa exp. aborted!\n";
294 } catch(mussa_error e) {
295 QString msg(e.what());
296 QMessageBox::warning(this, tr("Create New Analysis"), msg);
300 void MussaWindow::createSubAnalysis()
302 list<SequenceLocation> result;
303 SequenceLocationModel& model = subanalysis_window.getModel();
304 browser.copySelectedTracksAsSeqLocation(result);
305 for(list<SequenceLocation>::iterator result_itor = result.begin();
306 result_itor != result.end();
309 model.push_back(*result_itor);
312 if (not subanalysis_window.isVisible()) {
313 subanalysis_window.show();
317 void MussaWindow::editMotifs()
319 if (motif_editor != 0) {
320 motif_editor->hide();
323 motif_editor = new MotifEditor(analysis);
324 connect(motif_editor, SIGNAL(changedMotifs()),
325 this, SLOT(updateAnnotations()));
326 motif_editor->show();
329 void MussaWindow::loadMotifList()
331 QString caption("Load a motif list");
332 QString filter("Motif list(*.txt *.mtl)");
333 QString path = QFileDialog::getOpenFileName(this,
340 // try to load safely
342 fs::path converted_path(path.toStdString(), fs::native);
343 analysis->load_motifs(converted_path);
344 } catch (runtime_error e) {
345 QString msg("Unable to load ");
349 QMessageBox::warning(this, "Load Motifs", msg);
351 assert (analysis != 0);
354 void MussaWindow::saveMotifList()
359 void MussaWindow::loadMupa()
361 QString caption("Load a mussa parameter file");
362 QString filter("Mussa Parameters (*.mupa)");
363 QString mupa_path = QFileDialog::getOpenFileName(this,
368 if (mupa_path.isNull())
370 // try to load safely
372 Mussa *m = new Mussa;
373 fs::path converted_path(mupa_path.toStdString(), fs::native);
374 connect(m, SIGNAL(progress(const std::string&, int, int)),
375 this, SLOT(updateProgress(const std::string&, int, int)));
376 m->load_mupa_file(converted_path);
379 setWindowTitle(converted_path.native_file_string().c_str());
380 } catch (mussa_load_error e) {
381 QString msg("Unable to load ");
385 QMessageBox::warning(this, "Load Parameter", msg);
387 assert (analysis != 0);
390 void MussaWindow::loadSavedAnalysis()
392 QString caption("Load a previously run analysis");
393 QString muway_dir = QFileDialog::getExistingDirectory(this,
395 QDir::currentPath());
397 if (muway_dir.isNull())
399 // try to safely load
401 Mussa *m = new Mussa;
402 fs::path converted_path(muway_dir.toStdString(), fs::native);
403 connect(m, SIGNAL(progress(const std::string&, int, int)),
404 this, SLOT(updateProgress(const std::string&, int, int)));
405 m->load(converted_path);
406 // only switch mussas if we loaded without error
408 setWindowTitle(converted_path.native_file_string().c_str());
409 } catch (mussa_load_error e) {
410 QString msg("Unable to load ");
414 QMessageBox::warning(this, "Load Parameter", msg);
416 assert (analysis != 0);
419 void MussaWindow::newMussaWindow()
421 Mussa *a = new Mussa();
422 MussaWindow *win = new MussaWindow(a);
426 void MussaWindow::setSoftThreshold(int threshold)
428 if (analysis->get_soft_threshold() != threshold) {
429 analysis->set_soft_threshold(threshold);
436 void MussaWindow::showMussaToolbar()
438 if (mussaViewTB.isVisible())
444 void MussaWindow::toggleMotifs()
449 void MussaWindow::showManual()
451 #if QT_QTASSISTANT_FOUND
452 manualAssistant->openAssistant();
455 boost::python::object webopen = get_py()["webbrowser.open"];
456 webopen("http://woldlab.caltech.edu/~king/mussagl_manual/");
457 } catch( boost::python::error_already_set ) {
460 #endif //QT_QTASSISTANT_FOUND
463 void MussaWindow::assistantError(QString message)
465 //std::cout << "QAssistantError: " << message.toStdString() << "\n";
466 QMessageBox::warning ( this, "Warning: Mussagl Manual", message,
468 QMessageBox::NoButton);
471 void MussaWindow::NotImplementedBox()
473 QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
476 void MussaWindow::viewMussaAlignment()
478 const set<int>& selected_paths = browser.selectedPaths();
479 if (selected_paths.size() == 0 ) {
480 QMessageBox::warning(this,
481 QObject::tr("mussa"),
482 QObject::tr("you should probably select some paths "
485 boost::shared_ptr<MussaAlignedWindow> ma_win(
486 new MussaAlignedWindow(*analysis, selected_paths)
489 aligned_windows.push_back(ma_win);
490 connect(this, SIGNAL(changedAnnotations()),
491 aligned_windows.back().get(), SLOT(update()));
492 aligned_windows.back()->show();
496 void MussaWindow::updateAnalysis()
498 threshold.setRange(analysis->get_threshold(),analysis->get_window());
500 const Mussa::vector_sequence_type& seqs = analysis->sequences();
501 browser.setSequences(seqs, analysis->colorMapper());
504 zoom.setValue(browser.zoom());
507 void MussaWindow::updateAnnotations()
509 // motifs were changed in the sequences by
510 // Mussa::update_sequences_motifs
511 emit changedAnnotations();
515 void MussaWindow::updateLinks()
517 browser.clear_links();
518 bool reversed = false;
519 const NwayPaths& nway = analysis->paths();
521 typedef list<ConservedPath> conserved_paths;
522 typedef conserved_paths::const_iterator const_conserved_paths_itor;
523 for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
524 path_itor != nway.refined_pathz.end();
527 // since we were drawing to the start of a window, and opengl lines
528 // are centered around the two connecting points our lines were slightly
529 // offset. the idea of window_offset is to adjust them to the
530 // right for forward compliment or left for reverse compliment
531 // FIXME: figure out how to unit test these computations
532 //GLfloat window_offset = (path_itor->window_size)/2.0;
534 size_t track_len = path_itor->track_indexes.size();
535 vector<int> normalized_path;
536 normalized_path.reserve(track_len);
537 vector<bool> rc_flags(false, track_len);
538 for (size_t track_i=0; track_i != track_len; ++track_i)
540 int x = path_itor->track_indexes[track_i];
541 // at some point when we modify the pathz data structure to keep
542 // track of the score we can put grab the depth here.
544 // are we reverse complimented?
552 normalized_path.push_back(x);
553 rc_flags.push_back(reversed);
555 browser.link(normalized_path, rc_flags, path_itor->window_size);
561 MussaWindow::updateProgress(const string& description, int current, int max)
564 if (current == max) {
565 if (progress_dialog != 0) {
566 progress_dialog->hide();
567 delete progress_dialog;
571 // if we're starting, create the dialog
572 if (progress_dialog == 0) {
573 QString desc(description.c_str());
574 QString cancel("Cancel");
575 progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
576 progress_dialog->show();
578 // just update the dialog
579 progress_dialog->setValue(current);
582 qApp->processEvents();