diff --git a/doc/pulseview.1 b/doc/pulseview.1 index 9663fdf9..d08ae315 100644 --- a/doc/pulseview.1 +++ b/doc/pulseview.1 @@ -133,6 +133,20 @@ Ungroup the traces in the currently selected trace group. .B "CTRL+up/down arrow keys" Scroll down/up. .TP +.B "CTRL+m/CTRL+n" +Scroll forward or backward (right or left) in time (horizontally) for the +entire width of the capture, currently shown in the PulseView window viewport, +according to current zoom settings (could be called a "full-page" scroll, by +analogy with printable document viewers) +.TP +.B "CTRL+b" +Save a bitmap screen capture (screenshot) of only the ruler and the viewport, +currently shown in the PulseView window; which allows these screenshots to be +stitched together horizontally afterwards, if they are obtained through a +"full-page" scroll (note: not pixel-perfect). Screenshots are saved with a +filename of 'pulseview_yyyyMMdd_hhmmss.png' (UTC timestamp), in the +temporary directory of the OS. +.TP .B "CTRL+q" Quit, i.e. shutdown PulseView (closing all session tabs). .TP diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index 47cb96b2..e3ed4ab0 100644 --- a/pv/views/trace/view.cpp +++ b/pv/views/trace/view.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include @@ -236,6 +239,22 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) : SLOT(on_scroll_to_end_shortcut_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); end_shortcut_->setAutoRepeat(false); + scroll_view_left_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_N), this, + SLOT(on_h_scroll_view_left_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + scroll_view_left_->setAutoRepeat(false); + + scroll_view_right_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_M), this, + SLOT(on_h_scroll_view_right_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + scroll_view_right_->setAutoRepeat(false); + + bitmap_screenshot_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_B), this, + SLOT(on_bitmap_screenshot_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + bitmap_screenshot_->setAutoRepeat(false); + + svg_screenshot_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G), this, + SLOT(on_svg_screenshot_triggered()), nullptr, Qt::WidgetWithChildrenShortcut); + svg_screenshot_->setAutoRepeat(false); + grab_ruler_left_shortcut_ = new QShortcut(QKeySequence(Qt::Key_1), this, nullptr, nullptr, Qt::WidgetWithChildrenShortcut); connect(grab_ruler_left_shortcut_, &QShortcut::activated, @@ -1397,6 +1416,27 @@ void View::set_scroll_default() set_v_offset(extents.first); } +void View::h_scroll_view_fullpage(int direction) +{ + if (updating_scroll_) + return; + + // Disable sticky scrolling when user moves the horizontal scroll bar + // during a running acquisition + if (sticky_scrolling_ && (session_.get_capture_state() == Session::Running)) { + sticky_scrolling_ = false; + sticky_scrolling_changed(false); + } + + const QSize areaSize = viewport_->size(); + double length = scale_ * areaSize.width(); + Timestamp new_offset = offset_ + direction*length; + set_offset(new_offset); + + ruler_->update(); + viewport_->update(); +} + void View::determine_if_header_was_shrunk() { const int header_pane_width = @@ -1738,6 +1778,64 @@ void View::on_scroll_to_end_shortcut_triggered() set_h_offset(get_h_scrollbar_maximum()); } +void View::on_h_scroll_view_left_triggered() +{ + h_scroll_view_fullpage(-1); +} + +void View::on_h_scroll_view_right_triggered() +{ + h_scroll_view_fullpage(1); +} + +void View::on_bitmap_screenshot_triggered() +{ + //note: viewport_ does not contain track name markings at left, nor ruler + //scrollarea_ is the same, except with added scrollbars + //here we will get only viewport_ and ruler_, so as to assist in stitching/appending images + QSize vpSize = viewport_->size(); + QSize rlSize = ruler_->size(); + QSize imgsize(vpSize.width(), vpSize.height()+rlSize.height()); + QImage img(imgsize, QImage::Format::Format_ARGB32); + QPainter painter(&img); + ruler_->render(&painter, QPoint(0, 0)); + scrollarea_->render(&painter, QPoint(0, rlSize.height())); + QString fileStamp = QDateTime::currentDateTimeUtc().toString("yyyyMMdd_hhmmss"); + QString fileName = QString("pulseview_%1.png").arg(fileStamp); + QString filePath = QDir( QDir::tempPath() ).filePath(fileName); + bool issaved = img.save(filePath); + qDebug() << "Screenshot grabbed (" << issaved << "): " << filePath; +} + +void View::on_svg_screenshot_triggered() +{ + //note: viewport_ does not contain track name markings at left, nor ruler + //scrollarea_ is the same, except with added scrollbars + //here we will get only viewport_ and ruler_, so as to assist in stitching/appending images + QSize vpSize = viewport_->size(); + QSize rlSize = ruler_->size(); + QSize imgsize(vpSize.width(), vpSize.height()+rlSize.height()); + QString fileStamp = QDateTime::currentDateTimeUtc().toString("yyyyMMdd_hhmmss"); + QString fileName = QString("pulseview_%1.svg").arg(fileStamp); + QString filePath = QDir( QDir::tempPath() ).filePath(fileName); + + // note that there is no explicit .save command for SVG generator + QSvgGenerator generator; + generator.setFileName(filePath); + generator.setSize(imgsize); + generator.setViewBox(QRect(0, 0, imgsize.width(), imgsize.height())); + generator.setTitle(fileName); + generator.setTitle(tr("An SVG drawing created by the Qt5 SVG Generator from PulseView")); + + QPainter painter; + painter.begin(&generator); + ruler_->render(&painter, QPoint(0, 0)); + scrollarea_->render(&painter, QPoint(0, rlSize.height())); + painter.end(); + + qDebug() << "Screenshot grabbed: " << filePath; +} + void View::h_scroll_value_changed(int value) { if (updating_scroll_) diff --git a/pv/views/trace/view.hpp b/pv/views/trace/view.hpp index f8506cf4..d064b885 100644 --- a/pv/views/trace/view.hpp +++ b/pv/views/trace/view.hpp @@ -423,6 +423,8 @@ public Q_SLOTS: void update_view_range_metaobject() const; void update_hover_point(); + void h_scroll_view_fullpage(int direction); + public: void row_item_appearance_changed(bool label, bool content); void time_item_appearance_changed(bool label, bool content); @@ -437,6 +439,10 @@ private Q_SLOTS: void on_zoom_out_shortcut_triggered(); void on_scroll_to_start_shortcut_triggered(); void on_scroll_to_end_shortcut_triggered(); + void on_h_scroll_view_left_triggered(); + void on_h_scroll_view_right_triggered(); + void on_bitmap_screenshot_triggered(); + void on_svg_screenshot_triggered(); void h_scroll_value_changed(int value); void v_scroll_value_changed(); @@ -509,6 +515,10 @@ private Q_SLOTS: QShortcut *home_shortcut_, *end_shortcut_; QShortcut *grab_ruler_left_shortcut_, *grab_ruler_right_shortcut_; QShortcut *cancel_grab_shortcut_; + QShortcut *scroll_view_left_; + QShortcut *scroll_view_right_; + QShortcut *bitmap_screenshot_; + QShortcut *svg_screenshot_; mutable mutex signal_mutex_; vector< shared_ptr > signals_;