Gtkmm scroll_to() purpose - c++

I'm wondering about this member-function's scroll_to(TextBuffer::iterator& iter, double within_margin = 0) parameter within_margin. The API says this:
The effective screen for purposes of this function is reduced by a margin of size within_margin.
...
Parameters
within_margin margin as a [0.0,0.5] fraction of screen size.
I just don't get it. What and when does this parameter modifies the behaviour? Every langugage-binding of Gtk includes the same description. I've written a small application, so you can change the passed argument to the parameter yourself.
#include <gtkmm.h>
#include <iostream>
#include <string>
using namespace std;
Gtk::TextView* text_view;
void on_add_button_clicked();
void on_scroll_button_clicked();
int main(int argc, char* argv[]) {
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
Gtk::Window window;
Glib::RefPtr<Gdk::Monitor> primary_monitor = window.get_screen()->get_display()->get_primary_monitor();
Gdk::Rectangle monitor_size;
primary_monitor->get_geometry(monitor_size);
// half-size of primary-monitor
int width = monitor_size.get_width() / 2;
int height = monitor_size.get_height() / 2;
window.set_default_size(width, height);
window.set_title(__FILE__);
Gtk::Grid grid;
grid.set_row_spacing(5);
grid.set_column_spacing(5);
Gtk::ScrolledWindow scroll_window;
text_view = new Gtk::TextView();
text_view->set_editable(true);
scroll_window.add(*text_view);
scroll_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
scroll_window.set_hexpand(true);
scroll_window.set_vexpand(true);
Glib::RefPtr<Gtk::TextBuffer> text_buffer = text_view->get_buffer();
text_buffer->set_text("Hello!\n");
text_view->set_buffer(text_buffer);
grid.attach(scroll_window, 0, 0, 2, 2);
Gtk::Button add_button("add text");
add_button.signal_clicked().connect(sigc::ptr_fun(&on_add_button_clicked));
grid.attach_next_to(add_button, scroll_window, Gtk::POS_BOTTOM, 1, 1);
Gtk::Button scroll_button("scroll to somewhere");
scroll_button.signal_clicked().connect(sigc::ptr_fun(&on_scroll_button_clicked));
grid.attach_next_to(scroll_button, add_button, Gtk::POS_RIGHT, 1, 1);
window.add(grid);
window.show_all();
return app->run(window);
}
void on_add_button_clicked() {
Glib::RefPtr<Gtk::TextBuffer> text_buffer = text_view->get_buffer();
for (int i = 0; i != 100; ++i) {
text_buffer->insert_at_cursor("foobar\n");
}
}
void on_scroll_button_clicked() {
Glib::RefPtr<Gtk::TextBuffer> text_buffer = text_view->get_buffer();
Gtk::TextBuffer::iterator it = text_buffer->end();
text_view->scroll_to(it, 0.49);
}
You can compile the code with g++ -o scroll scroll.cpp -Wall -pedantic-errors `pkg-config gtkmm-3.0 --cflags --libs` .
Thank you

If margin is 0, then scroll_to() is free to put the target anywhere in the screen. If margin is 0.45, for example, then scroll_to() will put the target in the middle 10% of the screen, if possible.
The reason you don't see this in your example, is because you are scrolling to the end iterator, and it's not possible to scroll view so that the end of the text is displayed in the middle of the screen. (Some text views include extra space after the text in order to make this possible; Gtk::TextView doesn't.)

Related

Why when drawBackground is called does it override the setBackgroundBrush colour?

I'm following this series on youtube Node Editor. I'm attempting to learn both Qt and C++ at the same time, possibly a stupid idea, but "hello world" tutorials don't do much for me.
Episode 1 We are to create a grid using the drawBackground method, however before this in the class constructor we have set the background to a light grey colour. When I run the code without the drawBackground the scene shows the light grey background. However when I run it with the drawBackground method uncommented the grid is drawn but the background is now white.
Why does the drawBackground() method override the QDMGraphicsScene() setBackgroundBrush?
qdmgraphicsscene.h
#ifndef QDMGRAPHICSSCENE_H
#define QDMGRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QtGui>
#include <QtCore>
#include "math.h"
class QDMGraphicsScene : public QGraphicsScene
{
public:
QDMGraphicsScene();
//Settings
QColor _colour_background, _colour_light, _colour_dark;
QPen _pen_light, _pen_dark;
int scene_width, scene_height, gridSize, gridSquares;
QVector<QLine> lines_light, lines_dark;
void drawBackground(QPainter *painter, const QRectF &rect);
};
#endif // QDMGRAPHICSSCENE_H
qdmgraphicsscene.cpp
#include "qdmgraphicsscene.h"
QDMGraphicsScene::QDMGraphicsScene()
{
this->gridSize = 60;
this->gridSquares = 3;
this->_colour_background = QColor(57,57,57);
this->_colour_light = QColor(47,47,47);
this->_colour_dark = QColor(41, 41, 41);
this->_pen_light = QPen(this->_colour_light);
this->_pen_light.setWidth(1);
this->_pen_dark = QPen(this->_colour_dark);
this->_pen_dark.setWidth(2);
this->scene_width = 64000;
this->scene_height = 64000;
this->setSceneRect(-this->scene_width/2, -this->scene_height/2, this->scene_width, this->scene_height);
this->setBackgroundBrush(this->_colour_background);
}
void QDMGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
{
//create grid
int left = (int)floor(rect.left());
int right = (int)ceil(rect.right());
int top = (int)floor(rect.top());
int bottom = (int)ceil(rect.bottom());
int first_left = left - (left % this->gridSize);
int first_top = top - (top % this->gridSize);
//compute lines
for(int x = first_left; x < right; x=x+this->gridSize){
if(x % (this->gridSize * this->gridSquares) != 0){
this->lines_light.append(QLine(x,top,x,bottom));
} else {
this->lines_dark.append(QLine(x,top,x,bottom));
}
}
for(int y = first_top; y < bottom; y=y+this->gridSize){
if(y % (this->gridSize * this->gridSquares) != 0){
this->lines_light.append(QLine(left,y,right,y));
} else {
this->lines_dark.append(QLine(left,y,right,y));
}
}
//draw lines
painter->setPen(this->_pen_light);
painter->drawLines(this->lines_light);
painter->setPen(this->_pen_dark);
painter->drawLines(this->lines_dark);
}
I suspect the problem is simply that your drawBackground override doesn't use the background brush -- you just set the QPainter pen and use it to draw lines.
I think the simplest solution would be to call the base class implementation of drawBackground at the top of your own, so...
void QDMGraphicsScene::drawBackground (QPainter *painter, const QRectF &rect)
{
/*
* Call base class drawBackground implementation to draw background.
*/
QGraphicsScene::drawBackground(painter, rect);
//create grid
.
.
.

QTableWidget: Prioritize horizontal space for a specific column

I have a QTableWidget that has a column (#3) that needs more space than others. I want to resize all columns to their contents, and give priority to column #3. If column #3 pushes the table's width past what's available, I want column #3 to be truncated with '...' without a horizontal scrollBar.
The screenshot below is the simplest example of the behavior I'm chasing, but I've had to manually adjust the column widths. I want the table to do this automatically.
The following code shows examples of what I've tried on QTableWidget, but none have worked. I've provided inline comments on why the following methods do not work:
table->horizontalHeader()->setStretchLastSection(true);
table->resizeColumnsToContents();
Thank you to anyone who volunteers your time to help me with this.
#include <QApplication>
#include <QTableWidget>
#include <QStringList>
#include <QRect>
#include <QLayout>
#include <QDialog>
#include <QHeaderView>
#include <iostream>
int main( int argc, char* argv[] )
{
QApplication a(argc, argv);
QDialog* d = new QDialog();
d->setLayout( new QVBoxLayout() );
QTableWidget* table = new QTableWidget(1,4);
QStringList headers = {"1", "2", "3", "4"};
table->setHorizontalHeaderLabels(headers);
table->setItem(0, 0, new QTableWidgetItem("1"));
table->setItem(0, 1, new QTableWidgetItem("22222"));
table->setItem(0, 2, new QTableWidgetItem("33333333333333333333333333333"));
table->setItem(0, 3, new QTableWidgetItem("4"));
// Do nothing
//
// The table exceeds the dimensions of the dialog,
// and we get a horizontal scrollbar
// This also results in a horizontal scrollbar
//
// table->horizontalHeader()->setStretchLastSection(true);
// Resizing the columns introduces a horizontal scrollbar, and
// prevents the user from from changing column width
//
// table->resizeColumnsToContents();
// The table fits, but all columns are equally spaced.
// (We want column 3 to take up as much space as possible)
//
// table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// Columns are resized to their contents,
// but column 3 is not truncated and we get a horizontal scrollBar
//
// table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
d->layout()->addWidget( table );
d->show();
return a.exec();
}
This should work, try it out
#include <QApplication>
#include <QTableWidget>
#include <QStringList>
#include <QRect>
#include <QLayout>
#include <QDialog>
#include <QHeaderView>
#include <QScrollBar>
#include <iostream>
int main( int argc, char* argv[] )
{
QApplication a(argc, argv);
QDialog* d = new QDialog();
d->setLayout( new QVBoxLayout() );
QTableWidget* table = new QTableWidget(1,4);
QStringList headers = {"1", "2", "3", "4"};
table->setHorizontalHeaderLabels(headers);
table->setItem(0, 0, new QTableWidgetItem("1"));
table->setItem(0, 1, new QTableWidgetItem("22222"));
table->setItem(0, 2, new QTableWidgetItem("33333333333333333333333333333"));
table->setItem(0, 3, new QTableWidgetItem("4"));
// Do nothing
//
// The table exceeds the dimensions of the dialog,
// and we get a horizontal scrollbar
// This also results in a horizontal scrollbar
//
// table->horizontalHeader()->setStretchLastSection(true);
// Resizing the columns introduces a horizontal scrollbar, and
// prevents the user from changing column width
//
//resize table
d->layout()->addWidget( table );
d->show();
table->resizeColumnsToContents();
int tableWidth = table->width();
int columsWidth = 0;
int maxColumnWidth = 0;
int maxColumnIndex = 0;
int w = 0;
for(int n = 0; n < table->columnCount(); n++)
{
w = table->columnWidth(n);
columsWidth += w;
if(w > maxColumnWidth)
{
maxColumnWidth = w;
maxColumnIndex = n;
}
}
if(columsWidth > tableWidth)
{
int delta = columsWidth - tableWidth + table->horizontalScrollBar()->height();
maxColumnWidth -= delta;
if(maxColumnWidth < 0)
maxColumnWidth = 0;
table->setColumnWidth(maxColumnIndex, maxColumnWidth);
}
// This table fits, but all the columns are equally spaced.
// (We want column 3 to take up as much space as possible)
//
// table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// Columns are resized to their contents,
// but column 3 is not truncated and we get a horizontal scrollBar
//
// table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
return a.exec();
}
Get back to me if you have any problems with this code.

Stop QTableView from scrolling as data is added above current position

I have a simple QTableView with a QSortFilterProxyModel and a source model of a custom TableModel subclass that inherits from QAbstractTableModel. The model is dynamically updated with additional rows.
My problem is this: If I sort the table on a column, then scroll to a specific row, and then more rows are added above this row it pushes the row down. Data is coming in fast enough that it makes it difficult to click on rows to edit them without the row changing underneath my cursor.
Is there a way to stop the table from scrolling and maintain the position of the table relative to say a selected row?
QTableView::rowViewportPosition() can be used to get the current view port position which has to be corrected if something is inserted before current index.
It can be retrieved before and after insertion of a row using signal handlers.
Thus, the scrolling can be adjusted accordingly in the signal handler after the insertion. This is done changing the value of the vertical scrollbar. (The vertical scroll mode is changed to QTableView::ScrollPerPixel to ensure correct vertical adjustment.)
A minimal code sample:
#include <iostream>
#include <QApplication>
#include <QMainWindow>
#include <QScrollBar>
#include <QStandardItemModel>
#include <QTableView>
#include <QTimer>
enum { NCols = 2 }; // number of columns
enum { Interval = 1000 }; // interval of auto action
enum { NRep = 5 }; // how often selected auto action is repeated
// fills a table model with sample data
void populate(QStandardItemModel &tblModel, bool prepend)
{
int row = tblModel.rowCount();
if (prepend) tblModel.insertRow(0);
for (int col = 0; col < NCols; ++col) {
QStandardItem *pItem = new QStandardItem(QString("row %0, col %1").arg(row).arg(col));
tblModel.setItem(prepend ? 0 : row, col, pItem);
}
}
// does some auto action
void timeout(QTimer &timer, QStandardItemModel &tblModel)
{
static int step = 0;
++step;
std::cout << "step: " << step << std::endl;
switch (step / NRep % 3) {
case 0: break; // pause
case 1: populate(tblModel, false); break; // append
case 2: populate(tblModel, true); break; // prepend
}
}
// managing the non-scrolling when something is inserted.
struct NoScrCtxt {
QTableView &tblView;
int y;
NoScrCtxt(QTableView &tblView_): tblView(tblView_) { }
void rowsAboutToBeInserted()
{
y = tblView.rowViewportPosition(tblView.currentIndex().row());
}
void rowsInserted()
{
int yNew = tblView.rowViewportPosition(tblView.currentIndex().row());
if (y != yNew) {
if (QScrollBar *pScrBar = tblView.verticalScrollBar()) {
pScrBar->setValue(pScrBar->value() + yNew - y);
}
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// build some GUI
QMainWindow win;
QStandardItemModel tblModel(0, NCols);
for (int i = 0; i < 10; ++i) populate(tblModel, false);
QTableView tblView;
tblView.setVerticalScrollMode(QTableView::ScrollPerPixel);
tblView.setModel(&tblModel);
win.setCentralWidget(&tblView);
win.show();
// setup a "no-scroll manager"
NoScrCtxt ctxt(tblView);
QObject::connect(&tblModel, &QStandardItemModel::rowsAboutToBeInserted,
[&ctxt](const QModelIndex&, int, int) { ctxt.rowsAboutToBeInserted(); });
QObject::connect(&tblModel, &QStandardItemModel::rowsInserted,
[&ctxt](const QModelIndex&, int, int) { ctxt.rowsInserted(); });
// initiate some auto action
QTimer timer;
timer.setInterval(Interval); // ms
QObject::connect(&timer, &QTimer::timeout,
[&timer, &tblModel]() { timeout(timer, tblModel); });
timer.start();
// exec. application
return app.exec();
}
I compiled and tested this in Windows 10, VS2013, Qt 5.7:

qt show an image for 1/4 of a second

Hey I have tried several times to complete this using usleep or the qt sleep when showing an image but sometimes (almost every time) it shows up white instead of the image basically i want it to accept any input to say im ready cin will do, then show a random image numbered 1-28 in a random time ranging 1.5-3secs then show it for 250milisecs then hide and wait 2secs the show the picture for 3secs then repeat. I am on debian, g++;
thanks in advance.
int getRandInt(int x){
return rand() % x;
}
class I : public QThread
{
public:
static void sleep(unsigned long secs) {
QThread::msleep(secs);
}
};
QApplication app(argc, argv);
std::ostringstream oss;
oss << "images/" << getRandInt(28) << ".jpg";
std::cout << oss.str();
QString qstr = QString::fromStdString(oss.str());
QPixmap pixmap(qstr);
QPixmap pixmap2(qstr);
QSplashScreen splash(pixmap);
QSplashScreen splash2(pixmap2);
QMainWindow mainWin;
while(1==1){
splash.show();
splash.showMessage(QObject::tr("test"),
Qt::AlignLeft | Qt::AlignTop, Qt::black);
app.processEvents();
I::sleep(250);
splash.finish(0);
splash.raise();
I::sleep(2*1000);
splash2.show();
splash2.showMessage(QObject::tr("test"),Qt::AlignLeft | Qt::AlignTop, Qt::black);
app.processEvents(); I::sleep(5000);splash2.finish(&mainWin);splash2.raise();
}
With an understanding that "show a random image numbered 1-28 in a random time ranging 1.5-3secs then show it for 250milisecs" means "show a random image numbered 1-28 in a random time between 1.75s and 3.25s", the below does it.
The code requires Qt 5.x and a C++11 compiler. I tried to highlight proper use of C++11 pseudorandom number facilities. I don't know the end-use of your code, but if you substituted std::mt19937_64 for random_engine, it'd be good enough for running psychological research as far as randomness goes.
The images shown are randomly drawn from a set of 28 images. Each of the source images are also randomly generated.
Notice the conspicuous absence of any form of sleep or busy-looping, and zealous use of lambdas. This code wouldn't be any shorter were it written in Python. That's what C++11 buys you: conciseness, among other things :)
#include <QApplication>
#include <QStateMachine>
#include <QTimer>
#include <QStackedWidget>
#include <QPainter>
#include <QLabel>
#include <QPushButton>
#include <random>
typedef std::default_random_engine random_engine;
QImage randomImage(int size, random_engine & rng) {
QImage img(size, size, QImage::Format_ARGB32_Premultiplied);
img.fill(Qt::white);
QPainter p(&img);
p.setRenderHint(QPainter::Antialiasing);
int N = std::uniform_int_distribution<>(25, 200)(rng);
std::uniform_real_distribution<> dP(0, size);
std::uniform_int_distribution<> dC(0, 255);
QPointF pt1(dP(rng), dP(rng));
for (int i = 0; i < N; ++i) {
QColor c(dC(rng), dC(rng), dC(rng));
p.setPen(QPen(c, 3));
QPointF pt2(dP(rng), dP(rng));
p.drawLine(pt1, pt2);
pt1 = pt2;
}
return img;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
std::random_device rd;
random_engine gen(rd());
int imageSize = 300;
QList<QImage> images;
for (int n = 0; n < 28; ++n) images << randomImage(imageSize, gen);
std::uniform_int_distribution<> dImage(0, images.size()-1);
QStackedWidget display;
QPushButton ready("I'm Ready!");
QLabel label, labelHidden;
display.addWidget(&ready);
display.addWidget(&label);
display.addWidget(&labelHidden);
QTimer splashTimer;
QStateMachine machine;
QState s1(&machine), s2(&machine), s3(&machine), s4(&machine);
splashTimer.setSingleShot(true);
QObject::connect(&s1, &QState::entered, [&]{
display.setCurrentWidget(&ready);
ready.setDefault(true);
ready.setFocus();
});
s1.addTransition(&ready, "clicked()", &s2);
QObject::connect(&s2, &QState::entered, [&]{
label.setPixmap(QPixmap::fromImage(images.at(dImage(gen))));
display.setCurrentWidget(&label);
splashTimer.start(250 + std::uniform_int_distribution<>(1500, 3000)(gen));
});
s2.addTransition(&splashTimer, "timeout()", &s3);
QObject::connect(&s3, &QState::entered, [&]{
display.setCurrentWidget(&labelHidden);
splashTimer.start(2000);
});
s3.addTransition(&splashTimer, "timeout()", &s4);
QObject::connect(&s4, &QState::entered, [&]{
display.setCurrentWidget(&label);
splashTimer.start(3000);
});
s4.addTransition(&splashTimer, "timeout()", &s1);
machine.setInitialState(&s1);
machine.start();
display.show();
return a.exec();
}

PCL visualization in Qt using QVTK

I am rather new in Qt programming, and I am trying to visualize a point cloud from PCL inside a Qt Widget. I have tried to use this approach: https://stackoverflow.com/a/11939703/2339680, or (similar): http://www.pcl-users.org/QT-PCLVisualizer-mostly-working-td3285187.html.
I get the compile error: "invalid static_cast from type ‘vtkObjectBase* const’ to type ‘vtkRenderWindow*’" when trying to set the render window in my QVTKWidget.
For reference, I have included code from the second source below, which reproduces the error.
#include <pcl/sample_consensus/sac_model_plane.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/common/common.h>
#include <QVTKWidget.h>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QVTKWidget widget;
widget.resize(512, 256);
//
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz (new pcl::PointCloud<pcl::PointXYZ>);
{
for (float y = -0.5f; y <= 0.5f; y += 0.01f)
{
for (float z = -0.5f; z <= 0.5f; z += 0.01f)
{
pcl::PointXYZ point;
point.x = 2.0f - y;
point.y = y;
point.z = z;
cloud_xyz->points.push_back (point);
}
}
cloud_xyz->width = cloud_xyz->points.size ();
cloud_xyz->height = 1;
}
// this creates and displays a window named "test_viz"
// upon calling PCLVisualizerInteractor interactor_->Initialize ();
// how to disable that?
pcl::visualization::PCLVisualizer pviz ("test_viz");
pviz.addPointCloud<pcl::PointXYZ>(cloud_xyz);
pviz.setBackgroundColor(0, 0, 0.1);
vtkSmartPointer<vtkRenderWindow> renderWindow = pviz.getRenderWindow();
widget.SetRenderWindow(renderWindow);
}
widget.show();
app.exec();
return EXIT_SUCCESS;
}
The error is occures at the line
widget.SetRenderWindow(renderWindow);
I am using Qt 4.8.0 and PCL 1.7.0. Does anyone know if it is possible to get around this?
With inspiration from https://stackoverflow.com/a/5808864/2339680 i guess that the problem is, that vtkRenderWindow is only available to the compiler as a forward declaration. If you include
#include "vtkRenderWindow.h"
in the beginning, everything should compile.