make the path view independent of type of connection
[mussa.git] / qui / PathWindow.cpp
index 67ce139d267902c1f89187a1acb19bfc3385324c..7ffbadc5023dfa5546ec3f69d183b86bcfb80b3b 100644 (file)
@@ -6,62 +6,59 @@
 #include <QMenuBar>
 #include <QMessageBox>
 #include <QScrollBar>
-#include <QSpinBox>
 #include <QStatusBar>
 #include <QString>
-#include <QToolBar>
 #include <QWhatsThis>
 
 #include "qui/PathScene.hpp"
-#include "qui/PathWidget.hpp"
 #include "qui/PathWindow.hpp"
-#include "qui/ThresholdWidget.hpp"
 #include "qui/ImageSaveDialog.hpp"
+#include "mussa_exceptions.hpp"
 
 #include <iostream>
 
-PathWindow::PathWindow(Mussa *analysis, QWidget *) :
+using namespace std;
+
+PathWindow::PathWindow(Mussa *analysis_, QWidget *parent) :
+  QMainWindow(parent),
+  analysis(analysis_),
+  path_view(this),
+  mussaViewTB("Path Views"),
+  zoomBox(),
+  threshold(),
   closeAction(0) // initialize one of the pointers to null as a saftey flag
 {
-  scene = new PathScene(analysis, this);
-
   setupActions();
   setupMainMenu();
 
   //This next setWhatsThis function prevents
   // a segfault when using WhatsThis feature with 
   // opengl widget.
-  scene->setWhatsThis(tr("Mussa in OpenGL!"));
-  // make a widget so we can have a scroll bar
-  PathWidget *path_widget = new PathWidget(scene, this);
-  setCentralWidget(path_widget);
-
-  mussaViewTB = new QToolBar("Path Views");
-  mussaViewTB->addAction(toggleMotifsAction);
-
-  QSpinBox *zoom = new QSpinBox();
-  zoom->setWhatsThis("zoom magnification factor");
-  zoom->setRange(2,1000);
-  mussaViewTB->addWidget(zoom);
-  connect(zoom, SIGNAL(valueChanged(int)), scene, SLOT(setZoom(int)));
+  //scene->setWhatsThis(tr("Mussa in OpenGL!"));
+  setCentralWidget(&path_view);
+
+  mussaViewTB.addAction(toggleMotifsAction);
+
+  zoomBox.setWhatsThis("zoom magnification factor");
+  zoomBox.setRange(2,1000);
+  mussaViewTB.addWidget(&zoomBox);
+  connect(&zoomBox, SIGNAL(valueChanged(int)), 
+          &path_view.scene, SLOT(setZoom(int)));
   
-  ThresholdWidget *threshold = new ThresholdWidget;
-  threshold->setRange(19, 30);
-  threshold->setThreshold(19);
-  scene->setClipPlane(20);
+  threshold.setRange(19, 30);
+  threshold.setThreshold(19);
+  //scene->setClipPlane(20);
   // FIXME: for when we get the paths drawn at the appropriate depth
   //connect(threshold, SIGNAL(thresholdChanged(int)),
   //        scene, SLOT(setClipPlane(int)));
-  connect(threshold, SIGNAL(thresholdChanged(int)),
-          scene, SLOT(setSoftThreshold(int)));
-  mussaViewTB->addWidget(threshold);
+  //connect(&threshold, SIGNAL(thresholdChanged(int)),
+  //        &scene, SLOT(setSoftThreshold(int)));
+  mussaViewTB.addWidget(&threshold);
 
-  //Image Save Dialog
-  imageSaveDialog = new ImageSaveDialog(scene, this);
-
-  addToolBar(mussaViewTB);
+  addToolBar(&mussaViewTB);
 
   statusBar()->showMessage("Welcome to mussa", 2000);
+  updateAnalysis();
 }
 
 void PathWindow::setupActions()
@@ -91,15 +88,15 @@ void PathWindow::setupActions()
 
   loadMotifListAction = new QAction(tr("Load Motif List"), this);
   connect(loadMotifListAction, SIGNAL(triggered()), 
-          scene, SLOT(loadMotifList()));
+          this, SLOT(loadMotifList()));
   
   loadMupaAction = new QAction(tr("Load Mussa Parameters"), this);
   connect(loadMupaAction, SIGNAL(triggered()), 
-          scene, SLOT(loadMupa()));
+          this, SLOT(loadMupa()));
 
   loadSavedAnalysisAction = new QAction(tr("Load &Analysis"), this);
   connect(loadSavedAnalysisAction, SIGNAL(triggered()), 
-          scene, SLOT(loadSavedAnalysis()));
+          this, SLOT(loadSavedAnalysis()));
   loadSavedAnalysisAction->setIcon(QIcon("icons/fileopen.png"));
 
   saveMotifListAction = new QAction(tr("Save Motifs"), this);
@@ -178,19 +175,109 @@ void PathWindow::createSubAnalysis()
   NotImplementedBox();
 }
 
+
+void PathWindow::loadMotifList()
+{
+  QString caption("Load a motif list");
+  QString filter("Motif list(*.txt *.mtl)");
+  QString path = QFileDialog::getOpenFileName(this,
+                                                   caption, 
+                                                   QDir::currentPath(),
+                                                   filter);
+  // user hit cancel?
+  if (path.isNull()) 
+    return;
+  // try to load safely
+  try {
+    analysis->load_motifs(path.toStdString());
+  } catch (runtime_error e) {
+    QString msg("Unable to load ");
+    msg += path;
+    msg += "\n";
+    msg += e.what();
+    QMessageBox::warning(this, "Load Motifs", msg);
+  }
+  assert (analysis != 0);
+}
+
 void PathWindow::saveMotifList()
 {
   NotImplementedBox();
 }
 
+void PathWindow::loadMupa()
+{
+  QString caption("Load a mussa parameter file");
+  QString filter("Mussa Parameters (*.mupa)");
+  QString mupa_path = QFileDialog::getOpenFileName(this,
+                                                   caption, 
+                                                   QDir::currentPath(),
+                                                   filter);
+  // user hit cancel?
+  if (mupa_path.isNull()) 
+    return;
+  // try to load safely
+  try {
+    Mussa *m = new Mussa;
+    m->load_mupa_file(mupa_path.toStdString());
+    m->analyze(0, 0, Mussa::TransitiveNway, 0.0);
+    // only switch mussas if we loaded without error
+    delete analysis;
+    analysis = m;
+    updateAnalysis();
+  } catch (mussa_load_error e) {
+    QString msg("Unable to load ");
+    msg += mupa_path;
+    msg += "\n";
+    msg += e.what();
+    QMessageBox::warning(this, "Load Parameter", msg);
+  }
+  assert (analysis != 0);
+}
+
+void PathWindow::loadSavedAnalysis()
+{
+  QString caption("Load a previously run analysis");
+  QString muway_dir = QFileDialog::getExistingDirectory(this,
+                                                        caption, 
+                                                        QDir::currentPath());
+  // user hit cancel?
+  if (muway_dir.isNull()) 
+    return;
+  // try to safely load
+  try {
+    Mussa *m = new Mussa;
+    m->load(muway_dir.toStdString());
+    // only switch mussas if we loaded without error
+    delete analysis;
+    analysis = m;
+    updateAnalysis();
+  } catch (mussa_load_error e) {
+    QString msg("Unable to load ");
+    msg += muway_dir;
+    msg += "\n";
+    msg += e.what();
+    QMessageBox::warning(this, "Load Parameter", msg);
+  }
+  assert (analysis != 0);
+}
+
+void PathWindow::setSoftThreshold(int threshold)
+{
+  if (analysis->get_threshold() != threshold) {
+    analysis->set_soft_thres(threshold);
+    analysis->nway();
+    updateLinks();
+    update();
+  }
+}
+
 void PathWindow::showMussaToolbar()
 {
-  std::clog << "isVis?" << mussaViewTB->isVisible() <<std::endl;
-  if (mussaViewTB->isVisible())
-    mussaViewTB->hide();
+  if (mussaViewTB.isVisible())
+    mussaViewTB.hide();
   else
-    mussaViewTB->show();
-  std::clog << "isVis?" << mussaViewTB->isVisible() <<std::endl;
+    mussaViewTB.show();
 }
 
 void PathWindow::toggleMotifs()
@@ -206,9 +293,68 @@ void PathWindow::NotImplementedBox()
 void PathWindow::promptSaveOpenGlPixmap()
 {
   QSize size;
-  size = scene->size();
-  imageSaveDialog->setSize(size.width(), size.height());
-  int result = imageSaveDialog->exec();
-  std::cout << "Result: " << result << "\n";
+  size = path_view.scene.size();
+  //Image Save Dialog
+  ImageSaveDialog imageSaveDialog(&path_view.scene, this);
+  imageSaveDialog.setSize(size.width(), size.height());
+  int result = imageSaveDialog.exec();
+  cout << "Result: " << result << "\n";
+}
+
+void PathWindow::updateAnalysis()
+{
+  cout << "analysis updated" << endl;
+  path_view.scene.clear();
+  const vector<Sequence>& seqs = analysis->sequences();
+  for(vector<Sequence>::const_iterator seq_i = seqs.begin();
+      seq_i != seqs.end();
+      ++seq_i)
+  {
+    path_view.scene.push_sequence(*seq_i);
+  }
+  updateLinks();
+}
+
+void PathWindow::updateLinks()
+{
+  path_view.scene.clear_links();
+  bool reversed = false;
+  const NwayPaths& nway = analysis->paths();
+
+  typedef list<ExtendedConservedPath> conserved_paths;
+  typedef conserved_paths::const_iterator const_conserved_paths_itor;
+  for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
+      path_itor != nway.refined_pathz.end();
+      ++path_itor)
+  {
+    // since we were drawing to the start of a window, and opengl lines
+    // are centered around the two connecting points our lines were slightly
+    // offset. the idea of window_offset is to adjust them to the
+    // right for forward compliment or left for reverse compliment
+    // FIXME: figure out how to unit test these computations
+    //GLfloat window_offset = (path_itor->window_size)/2.0;
+
+    size_t track_len = path_itor->track_indexes.size();
+    vector<int> normalized_path;
+    normalized_path.reserve(track_len);
+    vector<bool> rc_flags(false, track_len);
+    for (size_t track_i=0; track_i != track_len; ++track_i)
+    {
+      int x = path_itor->track_indexes[track_i];
+      // at some point when we modify the pathz data structure to keep
+      // track of the score we can put grab the depth here.
+      //
+      // are we reverse complimented?
+      if ( x>=0) {
+        reversed = false;
+      } else {
+        reversed = true;
+        x = -x; // make positive
+      }
+      normalized_path.push_back(x);
+      rc_flags.push_back(reversed);
+    }
+    path_view.scene.link(normalized_path, rc_flags, path_itor->window_size);
+  }
 }