QScrollArea does not scroll to maximum after widgets have been added - layout

My setup looks like this:
Main Window
|- QHBoxLayout
|- Some Widgets
|- QScrollArea
| |- QHBoxLayout
| |- QGridLayout
| |- Widgets dynamically loaded in at runtime
| |- QVBoxLayout
|- Some Widgets
After I added in Widgets I want the Scroll Area to completely scroll down.
I did this by:
this->ui->gridLayout_table->addWidget(/*Widget*/); // this works perfectly nice
this->ui->gridLayout_table->addWidget(/*Widget*/); // this also works perfectly nice
this->ui->scrollArea->verticalScrollBar()->setValue(this->ui->scrollArea->verticalScrollBar()->maximum());
At the last command it scrolls to maximum minus the height of the newly added widgets. Is there any way to flush the changes before scrolling?
Thanks in advance
Lukas

Python version for the rangeChanged method:
scroll_area = QtGui.QScrollArea()
scroll_bar = scroll_area.verticalScrollBar()
scroll_bar.rangeChanged.connect(lambda: scroll_bar.setValue(scroll_bar.maximum()))
By connecting the rangeChanged method to the scrolldown, it ensures that each time there is a change in range the scrollbar will scroll down to the max.
Source

I've had the same problem. I found a work-around, which might be useful for you as well.
Using scrollBar->setValue(scrollBar->maximum()); does not work when being used right after having added widgets to the scrolled area. The maximum value of the scroll bar simply is not updated yet.
However it does work if you have a button that the user has to click just to adjust the scroll bar's position to the maximum. It seems that the scroll area notifies its scroll bar asynchronously (through signal) only. So I simply added a hidden button pb_AdjustScrollBar to my widget and simulated a click, using the animateClick(int) method after having added widgets to the scrolled area. Once the signal clicked(bool) is received for the AdjustScrollBar button, I then trigger the scrollBar to use the maximum position with:
scrollBarPtr->triggerAction(QAbstractSlider::SliderToMaximum);
Notes: the animateClick timeout must be set to something like 100ms (default). Putting the timeout much shorter has caused the scrollBar not to update to its real maximum. However I guess 100ms is fast enough for user interactions anyhow.
Still I would be curious to know, how this issue can be solved in a smarter way.

I found out the way. After add a widget put:
scroll->widget()->resize(scroll->widget()->sizeHint());
qApp->processEvents();

One workaround is to use the rangeChanged(int,int) signal of the vertical scroll bar.
#include <QWidget>
#include <QBoxLayout>
#include <QScrollArea>
#include <QScrollBar>
class myClass
{
Q_OBJECT
public:
myClass();
private:
QScrollArea *scrollArea;
int scrollBarMax = 0;
private slots:
void scrollToBottom(int min, int max);
};
myClass::myClass()
{
QVBoxLayout *myLayout = new QVBoxLayout;
QWidget *myWidget = new QWidget;
myWidget->setLayout(myLayout);
scrollArea = new QScrollArea;
scrollArea->setWidget(myWidget);
scrollArea->setWidgetResizeable(true);
QObject::connect(scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(scrollToBottom(int,int)));
}
void myClass::scrollToBottom(int min, int max)
{
(void) min;
if (max > scrollBarMax) scrollArea->verticalScrollBar()->setValue(max);
scrollBarMax = max;
}

Hm, I don't have an answer and the same problem. I however can supply a small reproducing example:
#include <QtGui>
class TestWidget : public QWidget
{
Q_OBJECT
public:
TestWidget(QWidget * parent = 0);
private slots:
void addButton();
private:
QScrollArea * scrollArea;
QWidget * contents;
QVBoxLayout * contentsLayout;
};
TestWidget::TestWidget(QWidget * parent)
: QWidget(parent)
{
QVBoxLayout * layout = new QVBoxLayout(this);
scrollArea = new QScrollArea(this);
layout->addWidget(scrollArea);
contents = new QWidget(this);
scrollArea->setWidget(contents);
contents->resize(300,400);
scrollArea->setWidgetResizable(true);
contentsLayout = new QVBoxLayout(contents);
for (int i = 0; i < 10; ++i)
{
QPushButton * button = new QPushButton(QString("button %1").arg(i), this);
connect(button, SIGNAL(clicked()), this, SLOT(addButton()));
contentsLayout->addWidget(button);
}
}
void TestWidget::addButton()
{
QPushButton * button = new QPushButton("button", this);
connect(button, SIGNAL(clicked()), this, SLOT(addButton()));
contentsLayout->addWidget(button);
QScrollBar * scrollBar = scrollArea->verticalScrollBar();
scrollBar->setValue(scrollBar->maximum());
}
int main(int argc, char * argv[])
{
QApplication app(argc, argv);
TestWidget widget;
widget.show();
app.setQuitOnLastWindowClosed(true);
return app.exec();
}
#include "main.moc"
The problem occurs both with Qt 4.8 and 5.2 (needs minor modifications of the example).

Had the same problem, here is what does the manual say:
"In such cases, setting the layout's size constraint property to one which provides
constraints on the minimum and/or maximum size of the layout (e.g.,
QLayout::SetMinAndMaxSize) will cause the size of the scroll area to be updated
whenever the contents of the layout changes."
In my case QScrollArea's contents size was changed by setMinimumSize method of a QScrollArea's widget, so i simply added ui->scroll_area->widget()->layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); and it started to work fine.
Important: after resizing the QScrollArea's widget, you should change QScrollBar's value inside of a custom event handler. Simple example:
bool MyCustomClassWithScrollArea::event(QEvent * event)
{
switch (event->type())
{
case MyCustomScrollToEndEvent::ScrollToEndEventType:
{
ui->scroll_area->verticalScrollBar()->setValue( ui->scroll_area->verticalScrollBar()->maximum() ); //scrolls to the end
return true;
}
}
return QWidget::event(event);
}

Related

Does GTK4 compiled using mingw-w64 give a memory leak when using drag and drop?

I'm using GTK-4 (version 4.8.2-1) with g++ on mingw-w64. I'm running Windows 11. I'm creating an app with a moveable widget and noticed that on task manager, the app rapidly eats up memory whenever a drag action is performed. Below is a minimal working example that produces this issue on my machine. Obviously this code doesn't actually do anything, but dragging the label repeatedly (on my machine, about 20 times) will cause the app to crash. Am I misunderstanding the API in some fundamental way and inadvertently causing this? Or is this an issue with GTK? mingw?
#include <gtk/gtk.h>
static GdkContentProvider* on_drag_prepare(GtkDragSource *source, double x, double y, GtkWidget* label) {
GValue* a = new GValue;
(*a) = G_VALUE_INIT;
g_value_init(a, G_TYPE_INT);
g_value_set_int(a, 1);
// The content is basically a single integer.
return gdk_content_provider_new_for_value(a);
}
static void activate( GtkApplication *app, gpointer user_data) {
// window
GtkWidget* window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Drag Memory Leak");
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
// label to drag
GtkWidget* label = gtk_label_new("Drag Me");
gtk_window_set_child(GTK_WINDOW (window), label);
// setting up drag callback
GtkDragSource *drag_source = gtk_drag_source_new();
gtk_drag_source_set_actions(drag_source, GDK_ACTION_MOVE);
g_signal_connect (drag_source, "prepare", G_CALLBACK (on_drag_prepare), label);
gtk_widget_add_controller (GTK_WIDGET (label), GTK_EVENT_CONTROLLER (drag_source));
gtk_window_present (GTK_WINDOW (window));
}
int main( int argc, char **argv) {
GtkApplication *app;
int status;
app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
I expected that when I compiled and ran this program, I'd not have a memory leak when dragging a widget repeatedly.

Problem in restoring floating toolbar for QMainWindow

I am seeing a problem while restoring QMainWindow state having QCombobox in floating toolbar. After restoring floating toolbar, my QCombobox is not able to get focus until i click on toolbar handle and move it.
Following is gif showing problem, Using QT 5.13.
File floating_toolbar.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = floating_toolbar
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
File : main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
File : mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
void closeEvent(QCloseEvent *event);
void readSettings();
bool eventFilter(QObject* xObj, QEvent* xEvent);
~MainWindow();
public slots:
void mCheck();
};
#endif // MAINWINDOW_H
File : mainwindow.cpp
#include "mainwindow.h"
#include <QToolBar>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLayout>
#include <QSettings>
#include <QEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QToolBar* lToolbar = new QToolBar(this);
QComboBox* lComobox = new QComboBox(this);
lComobox->setEditable(true);
lToolbar->setWindowTitle("MyToolBar");
lToolbar->setObjectName("NiceBaby");
lToolbar->addWidget(lComobox);
//lToolbar->addAction("check", lComobox, SLOT(clearEditText()));
addToolBar(lToolbar);
lToolbar->installEventFilter(this);
readSettings();
}
void MainWindow::mCheck()
{
}
void MainWindow::closeEvent(QCloseEvent *event)
{
QSettings settings("MyCompany", "MyApp");
settings.setValue("windowState", saveState());
QMainWindow::closeEvent(event);
}
void MainWindow::readSettings()
{
QSettings settings("MyCompany", "MyApp");
restoreState(settings.value("windowState").toByteArray());
}
MainWindow::~MainWindow()
{
}
bool MainWindow::eventFilter(QObject* xObj, QEvent* xEvent)
{
//qDebug()<<xEvent->type();
return QMainWindow::eventFilter(xObj, xEvent);
}
OK, a workaround is to reset the window flags on the toolbar when it is first shown and is floating. I tracked this down by seeing what happens once a toolbar is dropped after being dragged (but not plugged into main window). (It calls setWindowState() and all that does in this situation is hide the toolbar, call updateWindowFlags(), and show it again).
This could be handled from the QMainWindow::showEvent() or from the eventFilter installed onto the QToolBar. I think it's simpler from the former.
UPDATE: This problem actually happens whenever the toolbar is first shown even if not at app startup, e.g. from the toggle view menu by the user once app starts. I updated the code below to fix that issue as well. And see notes below about another issue with minimizing the main window.
I added this to the MainWindow class from the MCVE:
protected:
void showEvent(QShowEvent *e) override {
QMainWindow::showEvent(e);
#ifdef Q_OS_LINUX
if (lToolbar->isFloating()
// remove the next condition and the toolsbar will get hidden the 2nd time main window is minimized.
&& lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint)
) {
const bool vis = !lToolbar->isHidden();
qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
lToolbar->hide();
lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
lToolbar->show();
#endif
}
QToolBar* lToolbar; // Use this in MainWindow constructor to save the instance pointer.
I also noticed another issue with the initially-floating toolbar. When the main window is minimized, the toolbar doesn't get hidden but stays where it was on the screen. Regardless of what is in the toolbar (eg. no combo box, just QActions). This workaround could also sort-of address that issue (see code comment), but only the 2nd time the window is minimized. Needs a better workaround for the first minimize.
Can others confirm this? Potentially a larger issue than the editable combo and I'd be surprised if no one noticed before.
I guess this should be filed as a Qt bug either way.
UPDATE2: This version also fixes the minimize issue. I guess something happens after the QMainWindow::showEvent() that changes how the toolbar behaves. Which explains why the above workaround works only after the 1st minimize. So scheduling the toolbar "fix" for later works around that also.
class MainWindow : public QMainWindow
{
...
#ifdef Q_OS_LINUX
protected:
void showEvent(QShowEvent *e) override
{
QMainWindow::showEvent(e);
if (lToolbar->isFloating() && lToolbar->windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
// QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
// If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
//adjustToolbar();
}
}
private slots:
void adjustToolbar() const
{
const bool vis = !lToolbar->isHidden();
qDebug() << lToolbar->isFloating() << vis << lToolbar->windowFlags();
lToolbar->hide();
lToolbar->setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
lToolbar->show();
}
#endif
private:
QToolBar* lToolbar;
};
ADDED: A QToolBar subclass which applies the workaround on its own, nothing special needed in the QMainWindow. The minimize fix still only works when the adjustToolbar() function is queued or if restoreState() is only called after show() (see code comments).
class ToolBar : public QToolBar
{
Q_OBJECT
public:
using QToolBar::QToolBar;
#ifdef Q_OS_LINUX
protected:
void showEvent(QShowEvent *e) override
{
QToolBar::showEvent(e);
if (isFloating() && windowFlags().testFlag(Qt::X11BypassWindowManagerHint) ) {
// QMainWindow::show() after QMainWindow::restoreState() will break the minimizing again so we should delay calling adjustToolbar().
QMetaObject::invokeMethod(this, "adjustToolbar", Qt::QueuedConnection);
// If we're sure restoreState() is only called after show() then adjustToolbar() could be called here directly instead.
//adjustToolbar();
}
}
private slots:
void adjustToolbar()
{
const bool vis = !isHidden();
hide();
setWindowFlag(Qt::X11BypassWindowManagerHint, false);
if (vis)
show();
}
#endif
};
UPDATE3: The minimizing issue also exists with floating QDockWidget if the QMainWindow state is restored before it is shown. In fact with "older" Qt versions the floating widget doesn't show up at all (doesn't with <= 5.9.5 but does with >= 5.12.4, don't have anything in between to try ATM). So the proper approach is to show() the main window first and then restoreState(). Unfortunately this doesn't seem to work for QToolBar.
UPDATE4: Filed as QTBUG-78293
It seems to work normally on macOS:

Qt C++ Displaying images outside the GUI thread (Boost thread)

I am developing a C++ library realizing its interface by means of Qt, using VS2015. On the library side, 3 boost threads continously load images from 3 folders. I am trying to display these images in 3 different QLabel (or equivalent QWidgets), so the thread body consists of this functionality,
in particular by exploiting the setPixmap method. Although the call to the function is protected by a boost mutex, I got exceptions probably due to threads synchronization. Looking for a solution, I already awared that the QPixmap widget is not "thread-safe" (non-reentrant). I also tried to use QGraphicsView but it in turn relies on QPixmap, thus I came across the same problem.
So my question is: does an alternative to QPixmap exist to display images in Qt in a thread-safe
manner?
I would recommend to do not multi-threading in GUI programming. Although, Qt provides multi-threading support in general, IMHO, the widgets are not well-prepared for this.
Thus, to achieve image loaders which run concurrently in separate threads I would suggest the following concept:
Each threaded image loader feeds a private buffer. The GUI inspects from time to time (using QTimer) these buffers and updates its QPixmap. As access to buffers should be possible from the resp. image loader thread as well as the GUI thread they have to be mutex guarded, of course.
My sample code testLoadImageMT.cc:
#include <atomic>
#include <chrono>
#include <mutex>
#include <thread>
#include <QtWidgets>
// manually added types (normally provided by glib)
typedef unsigned guint;
typedef unsigned char guint8;
// the fluffy-cat image sample
struct Image {
guint width;
guint height;
guint bytes_per_pixel; /* 3:RGB, 4:RGBA */
guint8 pixel_data[1];
};
extern "C" const Image fluffyCat;
class ImageLoader {
private:
const Image &_img;
std::atomic<bool> _exit;
std::mutex _lock;
QImage _qImg;
std::thread _thread;
public: // main thread API
ImageLoader(const Image &img = fluffyCat):
_img(img),
_qImg(img.width, img.height, QImage::Format_RGB888),
_exit(false), _thread(&ImageLoader::loadImage, std::ref(*this))
{ }
~ImageLoader()
{
_exit = true;
_thread.join();
}
ImageLoader(const ImageLoader&) = delete;
void applyImage(QLabel &qLblImg)
{
std::lock_guard<std::mutex> lock(_lock);
qLblImg.setPixmap(QPixmap::fromImage(_qImg));
}
private: // thread private
void loadImage()
{
for (;;) {
{ std::lock_guard<std::mutex> lock(_lock);
_qImg.fill(0);
}
size_t i = 0;
for (int y = 0; y < (int)_img.height; ++y) {
for (int x = 0; x < (int)_img.width; ++x) {
const quint32 value
= _img.pixel_data[i + 2]
| (_img.pixel_data[i + 1] << 8)
| (_img.pixel_data[i + 0] << 16)
| (0xff << 24);
i += _img.bytes_per_pixel;
{ std::lock_guard<std::mutex> lock(_lock);
_qImg.setPixel(x, y, value);
}
if (_exit) return; // important: make thread co-operative
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // slow down CPU cooler
}
}
}
};
int main(int argc, char **argv)
{
// settings:
enum { N = 3 }; // number of images loaded/displayed
enum { Interval = 50 }; // update rate for GUI 50 ms -> 20 Hz (round about)
// build appl.
qDebug() << "Qt Version: " << QT_VERSION_STR;
QApplication app(argc, argv);
// build GUI
QWidget qMainWin;
QVBoxLayout qVBox;
QLabel *pQLblImgs[N];
for (int i = 0; i < N; ++i) {
qVBox.addWidget(
new QLabel(QString::fromUtf8("Image %1").arg(i + 1)));
qVBox.addWidget(
pQLblImgs[i] = new QLabel());
}
qMainWin.setLayout(&qVBox);
qMainWin.show();
// build image loaders
ImageLoader imgLoader[N];
// install timer
QTimer qTimer;
qTimer.setInterval(Interval); // ms
QObject::connect(&qTimer, &QTimer::timeout,
[&imgLoader, &pQLblImgs]() {
for (int i = 0; i < N; ++i) {
imgLoader[i].applyImage(*pQLblImgs[i]);
}
});
qTimer.start();
// exec. application
return app.exec();
}
Sorry, I used std::thread instead of boost::thread as I've no experience with the latter, nor a working installation. I believe (hope) the differences will be marginal. QThread would have been the "Qt native" alternative but again – no experiences.
To keep things simple, I just copied data out of a linked binary image (instead of loading one from file or from anywhere else). Hence, a second file has to be compiled and linked to make this an MCVE – fluffyCat.cc:
/* GIMP RGB C-Source image dump (fluffyCat.cc) */
// manually added types (normally provided by glib)
typedef unsigned guint;
typedef unsigned char guint8;
extern "C" const struct {
guint width;
guint height;
guint bytes_per_pixel; /* 3:RGB, 4:RGBA */
guint8 pixel_data[16 * 16 * 3 + 1];
} fluffyCat = {
16, 16, 3,
"x\211s\215\232\200gw`fx`at[cx^cw^fu\\itZerWn|ap~cv\204jnzedq^fr^kzfhv^Ra"
"GRbMWdR\\jXer^qw_\311\256\226\271\253\235\275\264\252\315\277\260\304\255"
"\231u~i\213\225\207l{fly`jx\\^nRlz_z\206nlx`t~i\221\211s\372\276\243\375"
"\336\275\376\352\340\356\312\301\235\216\212judgwcl~f\212\226u}\206h\212"
"\224q\231\237z\232\236{\216\225v\225\230\200\306\274\244\376\360\327\376"
"\361\331\376\360\341\326\275\272\253\240\244{\203p\202\220xp~e{\204^\222"
"\230n\212\217g\240\242{\234\236z\214\222r\270\271\247\360\353\340\376\370"
"\336\376\363\334\375\357\336\310\254\262\232\223\234\\gRfrX\204\220z\212"
"\225g\225\232j\254\255\177\252\250{\225\226u\304\302\265\374\365\351\376"
"\375\366\376\367\341\376\361\320\374\346\324\306\241\242\237\232\235n{fj"
"xckyfu~fUX#VZCfnT\231\231\207\374\374\371\377\372\354\376\376\374\376\376"
"\372\376\362\332\375\340\301\341\300\264\260\253\262jvdbq\\XkVJTDNTCCG8O"
"TE\322\321\313\377\377\375\376\376\373\376\377\376\376\376\375\376\374\362"
"\376\360\342\344\311\306\250\244\254R_PL^HXkT<#2OP#`dP\217\220\177\374\374"
"\370\377\377\374\376\375\371\377\377\376\376\374\360\377\367\336\376\350"
"\316\342\303\274\246\236\245jtbXdQTdNQYGU\\KchV\317\315\302\377\376\372\377"
"\376\367\376\373\360\377\376\367\376\366\337\376\355\312\374\331\271\323"
"\263\251\216\214\214\\hTP^HL\\FR[LMXI^dW\355\352\342\376\375\366\377\374"
"\360\376\374\361\376\374\361\376\356\321\374\331\264\374\330\266\330\270"
"\260\200||Y`SLVE>K9BJ<CN?VYP\347\330\322\376\366\345\376\363\330\376\367"
"\337\377\372\350\374\342\314\326\243\210\375\350\314\352\317\304shc^`TV`"
"RVbT>B4IS?PTD\244\232\216\374\355\320\376\354\311\376\351\306\376\362\332"
"\374\344\321\267\206u\375\362\337\326\274\272\\POMNBT]LNZH:<*<A*TV>OI;\242"
"\222\207\340\304\243\375\335\262\372\336\272\376\361\334\320\241\212\374"
"\352\322\266\233\237c\\WFH;MR>\\`F~xP\220\214[pqE\211\202\\g]=\230\214`\313"
"\266\207\344\303\240\362\336\274\323\257\201\333\304\240\305\252\204\254"
"\232p\216\206\\\206\203U\232\224b\234\244b\246\257m\220\232`\224\227h~\202"
"W\206\213]\204\210W\227\227i|\177RvzNlsGrtJwtLz}N{\204RlxF",
};
I compiled and tested in VS2013, with Qt 5.9.2 on Windows 10 (64 bit). This is how it looks:
I solved using signal/slot: the "non-GUI" thread emits a signal instead of displaying the images and the called slot paints the QLabel inside the GUI thread!

make a QGraphicsItemGroup with two QGraphicsSimpleTextItem-s?

Assume I want to display in some QGraphicsScene a simple genealogy tree. Each person there has exactly a first name and a last name (and nothing else displayed).
For each person there, I want to build a QGraphicsItemGroup made of two QGraphicsSimpleTextItem-s vertically aligned and centered. One for first names of persons, one for their family names. Each having its own font & color. I don't want to use a heavy QGraphicsTextItem because it is too heavy (a person name with first and last names only don't deserve a full-fledged QTextDocument).
So I was thinking of
struct Person {
QGraphicsItemGroup _group;
QGraphicsLinearLayout _lay;
QGraphicsSimpleTextItem _firstname;
QGraphicsSimpleTextItem _lastname;
public:
Person(QGraphicsScene*scene,
const std::string& first, const std::string& last)
: group(), _lay(Qt::Vertical),
_firstname(first.c_str()), _lastname(last.c_str()) {
_lay.addItem(&_firstname);
_lay.setSpacing(0, 5);
_lay.addItem(&_lastname);
_group.addToGroup(&_firstname);
_group.addToGroup(&_lastname);
scene->addItem(&_group);
};
};
but this don't work because _lay.addItem(&_firstname); don't compile, since a QGraphicsSimpleTextItem is not a QGraphicsLayoutItem
Any hints? Or is my entire approach wrong?
Should I define a class inheriting both from QGraphicsSimpleTextItem and from QGraphicsLayoutItem ?
NB: the actual code (GPlv3 licensed) is in my basixmo project on github, file guiqt.cc, commit 99fd6d7c1ff261. It is not a genealogy project, but an abstract syntax tree editor like thing for an interpreter with persistence. The concerned class is BxoCommentedObjrefShow; instead of a first name, I am showing an id like _7rH2hUnmW63o78UGC, instead of the last name, I am showing a short comment like body of test1_ptrb_get. The basixmo project itself is a tentative rewrite of melt-monitor-2015 in C++11 & Qt5
It seem you supposed a purpose to the QGraphicsLinearLayout which is not its real purspose:
From Official QT 5.7 documentation
The QGraphicsLinearLayout class provides a horizontal or vertical layout for managing widgets in Graphics View
It is not supposed to layout normal drawing items in you scene, but QWidgets. Unfortunately, the documentation and examples about it seem not to work.
How to implement vertical layout
Fortunately, the QGraphicsItem is not difficult to extends, so you may implement your pair of text in few lines:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsSimpleTextItem>
#include <QGraphicsItemGroup>
class QGraphicsPerson: public QGraphicsItemGroup
{
public:
QGraphicsPerson( const QString& firstStr,
const QString& secondStr,
QGraphicsItem *parent=nullptr)
{
first.setText(firstStr);
second.setText(secondStr);
addToGroup(&first);
addToGroup(&second);
second.setPos(first.pos().x(), first.pos().y()+first.boundingRect().height());
}
protected:
QGraphicsSimpleTextItem first, second;
};
int main(int n, char **args)
{
QApplication app(n, args);
QGraphicsScene scene;
QGraphicsView window(&scene);
QGraphicsPerson person( "Adrian", "Maire");
scene.addItem(&person);
person.setPos(30,30);
QGraphicsPerson person2( "Another", "Name");
scene.addItem(&person2);
window.show();
return app.exec();
}
Note: Scene space extends to the infinite in both direction, so space management is very simplified (no minimum/maximum sizes, stretching, etc.)

QPainter painter object outside paintevent

I am trying to draw a simple two dimensional figure in a QWidget window.
There is a paintEvent defined and a painter object of the QPainter class is also defined.
My drawing contains elements which I need to draw more than once at various locations, such as lines, text etc. For this purpose, I am using functions to draw these lines with varying positions. Similarly for text. In order to make the program shorter, also modular.
The paintEvent function is calling functions which are used to calculate and draw.
How do I pass the QPainter painter object defined in the paintEvent into the functions.
for e.g.
void Classname::drawText(QString text, int PosX, int PosY, QPainter painter)
{
QSize size=this->size();
QFont times("Helvetica [Cronyx]", 10);
QFontMetrics box(times);
int boxWidth = box.width(text);
int boxHeight = box.height();
painter.setFont(times);
painter.setPen(Qt::white);
painter.drawText(PosX,PosY,text);
}
then I get an error where the vc++ environment is telling me that the typename is not allowed for the painter object of QPainter class.
If I define QPainter painter1 object as shown below:
void Classname::drawText(QString text, int PosX, int PosY, QPainter painter)
{
QPainter painter1;
QSize size=this->size();
QFont times("Helvetica [Cronyx]", 10);
QFontMetrics box(times);
int boxWidth = box.width(text);
int boxHeight = box.height();
painter.setFont(times);
painter.setPen(Qt::white);
painter.drawText(PosX,PosY,text);
}
the program compiles but there is no output.
This is a part of the code, I am defining objects of the QPainter class in all the functions.
I read this thread, but the instructions are not clear. Must the begin() and end() function be called at all instances of drawing or just once in the paintEvent function?
As you mentioned you shall implement those functions in your class.
In your header:
class Class
{
// ...
protected:
virtual void paintEvent( QPaintEvent* aEvent ) override;
private:
void drawText( QPainter* aPainter, const QString& aText, int aPosX, int aPosY );
// void drawLine( ... );
};
In your source:
void Class::paintEvent( QPaintEvent* aEvent )
{
QPainter painter( this );
// ...
drawText( &painter/*, ... */ );
drawLine( &painter/*, ... */ );
}
void Class::drawText( QPainter* aPainter, const QString& aText, int aPosX, int aPosY )
{
// Your drawing ...
}

Resources