From 9d08e955170fc7498163ea45c4f69930eae11da1 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Sat, 10 Jun 2006 02:38:47 +0000 Subject: [PATCH] partial implementation of sequence track copy this includes the code to track the portion of a sequence track that has been selected, and provides a MussaWindow option to "copy" the selection as a fasta file. However the selection doesn't go to the clipboard yet, and the MussaAlignedWindow also needs the option. The actual Qt Slot that calls the glseqbrowser::copySelectedTrackAsFasta is in the SequenceBrowserWidget. --- alg/glseqbrowser.cpp | 68 ++++++++++++++++++++++-- alg/glseqbrowser.hpp | 44 +++++++++++---- alg/sequence.cpp | 4 +- alg/sequence.hpp | 2 +- alg/test/test_sequence.cpp | 1 + qui/MussaWindow.cpp | 11 ++++ qui/MussaWindow.hpp | 1 + qui/seqbrowser/SequenceBrowserWidget.cpp | 6 +++ qui/seqbrowser/SequenceBrowserWidget.hpp | 3 ++ 9 files changed, 125 insertions(+), 15 deletions(-) diff --git a/alg/glseqbrowser.cpp b/alg/glseqbrowser.cpp index f8a7b8c..560662b 100644 --- a/alg/glseqbrowser.cpp +++ b/alg/glseqbrowser.cpp @@ -62,7 +62,7 @@ void GlSeqBrowser::paintGL() const glFlush(); } -void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize) +void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize, const rect& r) { GLuint *ptr; GLuint names; @@ -74,6 +74,7 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize GLuint path_index = 0; GLuint pair_key_0 = 0; GLuint pair_key_1 = 0; + TrackRegion track; selected_paths.clear(); selected_tracks.clear(); @@ -114,7 +115,14 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize break; case MussaTrack: objid = *ptr++; ++consumed_names; - selected_tracks.insert(objid); + + int left = track_container[objid].leftbase(r.left); + int right = track_container[objid].rightbase(r.right); + // the static_cast should be ok, since basepairs line up on + // integral values + //TrackRegion track(objid, left, right); + track.set(objid, left, right); + selected_tracks.push_back(track); break; default: cout << "unknown type " << objtype << " "; @@ -168,7 +176,7 @@ void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right) glFlush(); glPopMatrix(); hits = glRenderMode(GL_RENDER); - processSelection(hits, selectBuf, select_buf_size); + processSelection(hits, selectBuf, select_buf_size, selectedRegion); } float GlSeqBrowser::border() const @@ -377,6 +385,60 @@ const set& GlSeqBrowser::selectedPaths() const return selected_paths; } +//! copy sequence from selected track using formating function +void GlSeqBrowser::copySelectedTracks(std::string& copy_buffer, + format_track formatter) +{ + copy_buffer.clear(); + + for(selected_track_iterator track_i = selected_tracks.begin(); + track_i != selected_tracks.end(); + ++track_i) + { + int track_index = track_i->track_id; + if (track_index >= track_container.size()) { + // should this be an exception instead? + clog << "track " << track_index << " > " << track_container.size() + << endl; + } else { + // we should be safe + const Sequence& seq = track_container[track_index].sequence(); + copy_buffer += formatter(seq, track_i->left, track_i->right); + } + } + cout << "copy" << endl << copy_buffer << endl; +} + +//! copy sequence from selected tracks as plain sequences +void GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer) +{ + struct AsString { + static string formatter(const Sequence& seq, int left, int right) + { + stringstream s; + s << seq.subseq(left, right-left+1) << std::endl; + return s.str(); + } + }; + + copySelectedTracks(copy_buffer, AsString::formatter); +} + +//! copy sequence from selected tracks as FASTA sequences +void GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer) +{ + struct AsFasta { + static string formatter(const Sequence& seq, int left, int right) + { + stringstream s; + s << ">" << seq.get_header() << std::endl + << seq.subseq(left, right-left+1) << std::endl; + return s.str(); + } + }; + copySelectedTracks(copy_buffer, AsFasta::formatter); +} + void GlSeqBrowser::centerOnPath(const vector& paths) { if (paths.size() != track_container.size()) { diff --git a/alg/glseqbrowser.hpp b/alg/glseqbrowser.hpp index 5a6638d..109bc50 100644 --- a/alg/glseqbrowser.hpp +++ b/alg/glseqbrowser.hpp @@ -51,11 +51,12 @@ public: //! zoom in to a reasonable sequence view double zoomToSequence(); //! set the current zoom level in (base pairs / pix ) - /*! its a double as zoom levels of [0.01, 1.0] make nice sequence views - */ + //! its a double as zoom levels of [0.01, 1.0] make nice sequence views void setZoom(double zoom_level); //! returns the current zoom level as basepairs per pixel double zoom() const; + //! center the provided path in the current viewport + void centerOnPath(const std::vector&); void setColorMapper(AnnotationColors& cm); AnnotationColors& colorMapper(); @@ -76,9 +77,16 @@ public: void link(const std::vector& path, const std::vector& isRC, int length); //! returns the index of pathids based on order added by link const std::set& selectedPaths() const; - //! center the provided path in the current viewport - void centerOnPath(const std::vector&); - + //! define our function for formating sequence copy + typedef std::string format_track(const Sequence& s, int left, int right); + //! copy sequence from selected track using formating function + void copySelectedTracks(std::string& copy_buffer, format_track func); + //! copy sequence from selected tracks as plain sequences + void copySelectedTracksAsString(std::string& copy_buffer); + //! copy sequence from selected tracks as FASTA sequences + void copySelectedTracksAsFasta(std::string& copy_buffer); + + //! Provide a logical name for a type discriminator for our glName stack enum FeatureType { MussaTrack, MussaSegment }; @@ -124,6 +132,21 @@ public: typedef std::map pair_segment_map; typedef std::vector path_segment_map_vector; path_segment_map_vector path_segments; + + struct TrackRegion + { + GLuint track_id; + int left; + int right; + + TrackRegion():track_id(0), left(0), right(0) {}; + TrackRegion(const TrackRegion& o) + : track_id(o.track_id), left(o.left), right(o.right) {} + TrackRegion(GLuint id, int l, int r) + : track_id(id), left(l), right(r) {} + void set(GLuint id, int l, int r) { track_id = id; left=l; right=r; }; + }; + private: //! recalculate the viewable world /*! depending on the size of our canvas, our zoom level and @@ -135,7 +158,10 @@ private: void update_layout(); //! convert opengl selections into the list of paths we should highlight - void processSelection(GLuint hits, GLuint buffer[], GLuint bufsize); + void processSelection(GLuint hits, + GLuint buffer[], + GLuint bufsize, + const rect& r); //! master scene drawing function /*! draw is broken out for the opengl selection code @@ -164,14 +190,14 @@ private: int pathid; protected: - //! where to draw our box + //! where to draw our box (world coordinates) rect selectedRegion; //! true if we have a selection bool selectedMode; //! indicate which paths are selected std::set selected_paths; //! which track is selected (it only makes sense to have one track selected). - std::set selected_tracks; - + std::list selected_tracks; + typedef std::list::iterator selected_track_iterator; }; #endif diff --git a/alg/sequence.cpp b/alg/sequence.cpp index 90b4805..1c4d2a6 100644 --- a/alg/sequence.cpp +++ b/alg/sequence.cpp @@ -543,9 +543,9 @@ Sequence::get_seq() const std::string -Sequence::subseq(int start, int end) const +Sequence::subseq(int start, int count) const { - return sequence.substr(start, end); + return sequence.substr(start, count); } diff --git a/alg/sequence.hpp b/alg/sequence.hpp index 4aa6713..41c80cd 100644 --- a/alg/sequence.hpp +++ b/alg/sequence.hpp @@ -110,7 +110,7 @@ class Sequence void set_seq(const std::string& a_seq); const std::string& get_seq() const; const char *c_seq() const; - std::string subseq(int start, int end) const; + std::string subseq(int start, int count) const; std::string rev_comp() const; // string-like functions diff --git a/alg/test/test_sequence.cpp b/alg/test/test_sequence.cpp index 2516483..98051e2 100644 --- a/alg/test/test_sequence.cpp +++ b/alg/test/test_sequence.cpp @@ -87,6 +87,7 @@ BOOST_AUTO_TEST_CASE( sequence_load ) Sequence s; s.load_fasta(seq_path); BOOST_CHECK_EQUAL(s.subseq(0, 5), "GGATC"); // first few chars of fasta file + BOOST_CHECK_EQUAL(s.subseq(2, 3), "ATC"); BOOST_CHECK_EQUAL(s.get_header(), "gi|180579|gb|M21487.1|HUMCKMM1 Human " "muscle creatine kinase gene (CKMM), " "5' flank"); diff --git a/qui/MussaWindow.cpp b/qui/MussaWindow.cpp index 04fe617..b5057cb 100644 --- a/qui/MussaWindow.cpp +++ b/qui/MussaWindow.cpp @@ -33,6 +33,7 @@ MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) : threshold(), progress_dialog(0), aboutAction(0), + copySelectedSequenceAsFastaAction(0), closeAction(0), createNewAnalysisAction(0), createSubAnalysisAction(0), @@ -96,6 +97,8 @@ MussaWindow::~MussaWindow() if (progress_dialog != 0) delete progress_dialog; if (aboutAction != 0) delete aboutAction; + if (copySelectedSequenceAsFastaAction != 0) + delete copySelectedSequenceAsFastaAction; if (closeAction != 0) delete closeAction; if (createNewAnalysisAction != 0) delete createNewAnalysisAction; if (createSubAnalysisAction != 0) delete createSubAnalysisAction; @@ -137,6 +140,11 @@ void MussaWindow::setupActions() connect(aboutAction, SIGNAL(triggered()), this, SLOT(about())); aboutAction->setIcon(QIcon(":/icons/info.png")); + // copy sequence + copySelectedSequenceAsFastaAction = new QAction(tr("&Copy As Fasta"), this); + connect(copySelectedSequenceAsFastaAction, SIGNAL(triggered()), + &browser, SLOT(copySelectedSequenceAsFasta())); + // add exit closeAction = new QAction(tr("&Close"), this); closeAction->setStatusTip(tr("Close this window")); @@ -240,6 +248,9 @@ void MussaWindow::setupMainMenu() newMenu->addSeparator(); newMenu->addAction(closeAction); + newMenu = menuBar()->addMenu(tr("&Edit")); + newMenu->addAction(copySelectedSequenceAsFastaAction); + newMenu = menuBar()->addMenu(tr("&View")); newMenu->addAction(editMotifsAction); newMenu->addAction(viewMussaAlignmentAction); diff --git a/qui/MussaWindow.hpp b/qui/MussaWindow.hpp index 42c90a2..544cea7 100644 --- a/qui/MussaWindow.hpp +++ b/qui/MussaWindow.hpp @@ -93,6 +93,7 @@ protected: QProgressDialog *progress_dialog; QAction *aboutAction; + QAction *copySelectedSequenceAsFastaAction; QAction *closeAction; QAction *createNewAnalysisAction; QAction *createSubAnalysisAction; diff --git a/qui/seqbrowser/SequenceBrowserWidget.cpp b/qui/seqbrowser/SequenceBrowserWidget.cpp index 5e629a7..8c3633d 100644 --- a/qui/seqbrowser/SequenceBrowserWidget.cpp +++ b/qui/seqbrowser/SequenceBrowserWidget.cpp @@ -79,6 +79,12 @@ const vector& SequenceBrowserWidget::sequences() const return scrollable_browser.browser().sequences(); } +void SequenceBrowserWidget::copySelectedSequenceAsFasta() +{ + std::string buffer; + scrollable_browser.browser().copySelectedTracksAsFasta(buffer); +} + void SequenceBrowserWidget::clear_links() { scrollable_browser.browser().clear_links(); diff --git a/qui/seqbrowser/SequenceBrowserWidget.hpp b/qui/seqbrowser/SequenceBrowserWidget.hpp index 09a0b48..63214ca 100644 --- a/qui/seqbrowser/SequenceBrowserWidget.hpp +++ b/qui/seqbrowser/SequenceBrowserWidget.hpp @@ -43,6 +43,9 @@ public: void centerOnPath(const std::vector& paths); public slots: + //! copy selected sequence + void copySelectedSequenceAsFasta(); + //! set the zoom level of our browser void setZoom(double); //! zoom to fit the whole scene on the screen -- 2.30.2