QScrollArea missing Scrollbar - c++

I think it is the same problem as : QScrollArea resizing QWidget
but there are not solution. so let me expose the problem.
test 2 inherited from QWidget:
composed :
vector of QSpinBox
QScrollArea
QVBoxLayout
test2 (QWidget) <- QScrollArea <- QVBoxLayout <- Spinbox
Problems :
There are no scrollbar
[FIXED] The inside of the scrollbar is shrinked to fit so little space nothing can be read (the window can be resized during execution that will cause the inside to get bigger and be readable nevertheless no scrollbar will appear)
I Think problems come from a single source :: Size Hints and Layouts (http://qt-project.org/doc/qt-5.1/qtwidgets/qscrollarea.html#details)
The second problem (shrinked widget) can be solved by setting "c->setSizeConstraint(QLayout::SetMinimumSize);"
I am currently seeking a solution for the missing scrollbar
here is a code showing my problem :
<c++>
#include <QWidget>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QSpinBox>
class test2 : public QWidget
{
Q_OBJECT
public:
test2(QWidget *parent = 0) :QWidget(parent)
{
b = new QScrollArea(this);
c = new QVBoxLayout;
for (int i = 0; i < 10; i++)
{
a.push_back(new QSpinBox());
c->addWidget(a[i]);
}
c->setSizeConstraint(QLayout::SetMinimumSize);
b->setLayout(c);
b->resize(200, 200);
}
~test2()
{
for (int i = 0; i < 10; i++)
delete a[i];
}
protected:
QVector<QSpinBox*> a;
QScrollArea* b;
QVBoxLayout* c;
};
int main(int argc, char *argv[])
{
///*
QApplication app(argc, argv);
test2 a;
a.show();
return app.exec();//*/
}
EDIT :: found a Solution here:
http://qt-project.org/forums/viewthread/295
if you don't want to read huge amount of useless code here what he has done ::
he warped the layout inside a widget
Solution :: inherit the Object from ScrollBar <- Widget <- Layout
instead of widget <- ScrollBar <- Layout
but it a work around not really a solution...
I going to try on the example I gave.
it works. Does anyone have a better solution ??

You do not want to set the layout on the scroll area itself. The answer you cite stems from misunderstanding this.
You need to have a widget within a scrollarea, and you pass that widget to the area using QScrollArea::setWidget. If all you have inside the scroll area is one widget with no children, then you don't need additional layout.
You do not need to manually keep track of widgets that are owned by a layout. They'll be deleted automatically once the widget that has the layout is deleted.
The QScrollArea widget is not laid out within its enclosing widget.
Below is a working example of how to do it:
// https://github.com/KubaO/stackoverflown/tree/master/questions/scroll-18703286
#include <QScrollArea>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QApplication>
class Window : public QWidget
{
QVBoxLayout m_layout{this};
QScrollArea m_area;
QWidget m_contents;
QVBoxLayout m_contentsLayout{&m_contents};
QSpinBox m_spinBoxes[10];
public:
Window(QWidget *parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_area);
m_area.setWidget(&m_contents);
for (auto & spinbox : m_spinBoxes)
m_contentsLayout.addWidget(&spinbox);
m_contentsLayout.setSizeConstraint(QLayout::SetMinimumSize);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window w;
w.show();
return app.exec();
}

Related

Draw inside a QGraphicsScene inside a QWidget

I am trying to have a window (in the form of a QWidget) consisting of both a menu on the right and a graphics area on the left.
Despite the numerous websites explaining the many ways to use QGraphicsScene and QGraphicsView, I just couldn't figure out how to do it.
Here's main.cpp modified to work on its own :
#include <QApplication>
#include <QRectF>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPushButton>
#include <QVBoxLayout>
#include <QGraphicsRectItem>
#include <QPalette>
int main (int argc, char * argv []) {
QApplication app (argc, argv) ;
// Main window
QWidget frame ;
frame.setFixedSize(750, 550) ;
frame.show() ;
// Right side, works ok
QWidget menu (&frame) ;
menu.setFixedSize(200, 500) ;
menu.move(550, 10) ;
QPalette pal = menu.palette() ;
pal.setColor(QPalette::Background, Qt::red) ;
menu.setPalette(pal) ; // I expected this to color the whole area
// to show the extent of the zone devoted to the menu,
// but it only outlines the button
menu.show() ;
QPushButton button ("Menu", &menu) ;
button.show() ; // I didn't think this was necessary,
// but the button didn't appear without this line
// Left side, nothing displayed
QVBoxLayout layout ;
QGraphicsScene * scene = new QGraphicsScene () ;
QGraphicsView view (scene) ;
layout.addWidget(&view) ;
QWidget canvas (&frame) ;
canvas.setLayout(&layout) ;
// I found this trick to include a QGraphicsScene inside a QWidget,
// I haven't had the opportunity to see whether it really works.
scene->addItem(new QGraphicsRectItem(10, 10, 20, 20)) ;
// The above line has no visible effect
view.show() ;
return app.exec() ;
}
I would expect this to create a window, put a bunch of buttons on the right side (or in the case of the rediced code I provided, just a single button), and draw a rectangle on the left, but it leaves the whole left area blank.
Does the problem come from how I put the QGraphicsView inside the QWidget ? Or is it failing to draw because of something else ? Do I have to update the QGraphicsView to reflect the change ? Is it just out of visible range ?
Finally, is the failure to draw in any way related to the fact that the whole application crashes on line QWidget canvas (&frame) ; when closed ?
I just repaired you program a bit, so that it illustrates, what you can do with Qt and how you should likely use the framework.
I just moved the QPushButton to a QAction residing in a QMenuBar. The QMenuBar can be added to a QMainWindow, which is reasonable for a normal app.
The central widget of the QMainWindow contains the QGraphicsView. Now, you just forgot to connect the QGraphicsScene with the QGraphicsView. That was the reasons for not seeeing anything in your view.
QGraphicsView and QGraphicsScene are just a typical example for a MVC pattern. You can also add another QGraphicsView and connect it to the same QGraphicsScene.
You should also create all you objects with new, as Qt automatically disposes all its children of a QObject, if it is either deleted or leaves scope.
If you are realyl interesting into seriously learning Qt I suggest, that you are creating plenty small example programs like these. It really helped me a lot.
#include <QApplication>
#include <QMenuBar>
#include <QGraphicsView>
#include <QVBoxLayout>
#include <QGraphicsRectItem>
#include <QMainWindow>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
auto mainWindow = new QMainWindow;
auto menuBar = new QMenuBar;
auto menu = new QMenu("Menu");
auto action = new QAction("Action");
menu->addAction(action);
menuBar->addMenu(menu);
mainWindow->setMenuBar(menuBar);
auto frame = new QFrame;
frame->setLayout(new QVBoxLayout);
mainWindow->setCentralWidget(frame);
auto scene = new QGraphicsScene();
auto view=new QGraphicsView(scene);
view->setScene(scene); // That connects the view with the scene
frame->layout()->addWidget(view);
QObject::connect(action, &QAction::triggered, [&]() {
scene->addItem(new QGraphicsRectItem(10, 10, 20, 20));
});
mainWindow->show();
return app.exec();
}

QTableWidget's cell widget not displaying correctly when last row deleted

I have a QTableWidget that has QPushButtons as cell widgets. I use these push buttons as a means to delete a row from the table. I'm having a problem where the QPushButtons can be accidentally shifted from being centered in their cell. The image below shows what is happening.
This occurs in the specific case where the user has 1) made selected a cell in the last row of the table, 2) that cell contains a delete button, and 3) the vertical scroll bar is moved such that the last row is partially visible.
Below is a very minimal example that will create this problem. The user needs to use the scrollbar and scroll up by one tick, then press the last delete button.
mainwindow.h:
#pragma once
#include <QMainWindow>
class QSignalMapper;
class QTableWidget;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void DeletePressed(int row);
private:
QSignalMapper* signalMapper_;
QTableWidget* table_;
};
mainwindow.cpp:
#include "mainwindow.h"
#include <QGridLayout>
#include <QSignalMapper>
#include <QTableWidget>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
signalMapper_(new QSignalMapper(this)),
table_(new QTableWidget())
{
setCentralWidget( new QWidget );
this->setFixedSize(200,300);
QGridLayout *layout = new QGridLayout(centralWidget());
layout->addWidget(table_);
table_->setRowCount(0);
table_->setColumnCount(1);
// if you comment out the following line then the problem is not present
table_->setSelectionMode(QAbstractItemView::SingleSelection);
for (int i = 0; i < 20; i++) {
table_->insertRow(i);
QPushButton *button = new QPushButton("Delete");
connect(button, SIGNAL(clicked(bool)), signalMapper_, SLOT(map()));
signalMapper_->setMapping(button, i);
table_->setCellWidget(i, 0, button);
}
connect(signalMapper_, SIGNAL(mapped(int)), this, SLOT(DeletePressed(int)));
table_->setCurrentCell(19,0);
}
MainWindow::~MainWindow()
{
}
void MainWindow::DeletePressed(int row)
{
table_->clearSelection(); // <- added clear selection before remove row
table_->removeRow(row);
}
main.cpp:
#include <QApplication>
#include "mainwindow.h"
int main( int argc, char* argv[] )
{
QApplication app(argc, argv);
MainWindow wnd;
wnd.show();
return app.exec();
}
I think the problem stems from the QTableWidget trying to keep the selected cell visible when a new selection is made. I have tried both of the answers posted here but neither solved my problem.
If anyone could tell me how to solve this problem I would appreciate the help.
I had a similar problem a long time ago with a QtreeWidget and I solved it by callig protected method updateGeometries().
If you did not specialize QTableView, you can't simply call this function (as it is protected), so here's a trick to simply do it:
class MyQTableView : public QTableView
{
public:
inline void publicUpdateGeometries() { updateGeometries(); }
};
void MainWindow::DeletePressed(int row)
{
table_->clearSelection(); // <- added clear selection before remove row
table_->removeRow(row);
// call the protected method QTableView::updateGeometries()
((MyQTableView*) table_)->publicUpdateGeometries();
}
Hope this will solve your problem.
Try ui->tableWidget->clearSelection();
This happen only when scroll bar is added.to solve this you need to setvrticalscrollbarpolicy to scroll bar off then delete the row and add the scroll as needed by setverticalbarpolicy.
It will solve your problem.
I did the same way.

Creating and laying out widgets using a for loop in Qt

So i wanted to create 5 buttons in Qt but instead, create just one button and put it in a for loop so i don't have to create each of the 5 buttons manually. I tried different ways but all proved futile. I'm new to C++ and Qt.
Here are the codes;
show.h
#ifndef SHOW_H
#define SHOW_H
#include <QDialog>
#include <QPushButton>
#include <QVBoxLayout>
class Show : public QDialog {
Q_OBJECT
public:
explicit Show(QWidget *parent = 0);
~Show();
private:
QPushButton *button;
};
#endif // SHOW_H
show.cpp
#include "show.h"
#include "ui_show.h"
Show::Show(QWidget *parent) : QDialog(parent) {
int a = 5;
button = new QPushButton[a];
button->setText("Ok");
QVBoxLayout *layout = new QVBoxLayout[a];
for (int i = 0; i < sizeof(button)/4; i++) {
/*here, i wanted to do something like this;
'layout[i].addWidget(button[i]);' but didn't work*/
layout[i].addWidget(button);
}
setLayout(layout);
}
Show::~Show() {
}
main.cpp
#include "show.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Show *dialog = new Show;
dialog->show();
return a.exec();
}
After i run the code, i only see one button.
Your help is deeply appreciated. Thank you!!
QPushButton* pButton = new QPushButton("Ok");
This creates a single instance of a QPushButton.
You can add the button to a layout with a call to Layout::addWidget, which internally calls addItem.
As the documentation states for addItem: -
Note: The ownership of item is transferred to the layout, and it's the layout's responsibility to delete it
So your current code creates a single button and as it gets added to each successive layout, it is removed from the layout in which it was previously added.
You're creating one button and adding the same button 5 times. If you want 5 buttons in 5 layouts using a loop, then you need 5 separate instances of the button: -
for (int i=0; i<5; ++i)
{
QPushButton* pButton = new QPushButton("Ok");
layout[i].addWidget(pButton);
}
In your show.cpp change the lines
Show::Show(QWidget *parent) : QDialog(parent) {
int a = 5;
// You only need one layout for all buttons, not one per button.
QVBoxLayout *layout = new QVBoxLayout( this );
for (int i = 0; i < a; i++) {
QPushButton * newButton = new QPushButton( this );
newButton ->setText( "Ok" );
layout->addWidget( newButton );
}
setLayout(layout);
}
Show::~Show() {
}
The layout expects a pointer and you only need one layout instead one per button.

Qt custom widget not showing child widgets

I have a custom widget with some standard child widgets inside. If I make a separate test project and redefine my custom widget to inherit QMainWindow, everything is fine. However, if my custom widget inherits QWidget, the window opens, but there are no child widgets inside.
This is the code:
controls.h:
#include <QtGui>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QPushButton>
class Controls : public QWidget
{
Q_OBJECT
public:
Controls();
private slots:
void render();
private:
QWidget *frame;
QWidget *renderFrame;
QVBoxLayout *layout;
QLineEdit *rayleigh;
QLineEdit *mie;
QLineEdit *angle;
QPushButton *renderButton;
};
controls.cpp:
#include "controls.h"
Controls::Controls()
{
frame = new QWidget;
layout = new QVBoxLayout(frame);
rayleigh = new QLineEdit;
mie = new QLineEdit;
angle = new QLineEdit;
renderButton = new QPushButton(tr("Render"));
layout->addWidget(rayleigh);
layout->addWidget(mie);
layout->addWidget(angle);
layout->addWidget(renderButton);
frame->setLayout(layout);
setFixedSize(200, 400);
connect(renderButton, SIGNAL(clicked()), this, SLOT(render()));
}
main.cpp:
#include <QApplication>
#include "controls.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Controls *controls = new Controls();
controls->show();
return app.exec();
}
This opens up a window with correct dimensions, but with no content inside.
Bear in mind this is my first day using Qt. I need to make this work without inheriting QMainWindow because later on I need to put this on a QMainWindow.
You're missing a top level layout:
Controls::Controls()
{
... (yoour code)
QVBoxLayout* topLevel = new QVBoxLayout(this);
topLevel->addWidget( frame );
}
Or, if frame is not used anywhere else, directly:
Controls::Controls()
{
layout = new QVBoxLayout(this);
rayleigh = new QLineEdit;
mie = new QLineEdit;
angle = new QLineEdit;
renderButton = new QPushButton(tr("Render"));
layout->addWidget(rayleigh);
layout->addWidget(mie);
layout->addWidget(angle);
layout->addWidget(renderButton);
setFixedSize(200, 400);
connect(renderButton, SIGNAL(clicked()), this, SLOT(render()));
}
Note that setLayout is done automatically when QLayout is created (using parent widget)
You'll want to set a layout on your Controls class for managing its child sizes. I'd recommend removing your frame widget.
controls.cpp
Controls::Controls()
{
layout = new QVBoxLayout(this);
.
.
.
}
main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}

With QToolBox, which setting to have page be only its content size?

I'm trying to find the settings or size policy so that each page in my QToolBox instance only takes up the space needed by its content. I've tried everything I could see in the properties for both the instance and for each of the individual pages.
Am I misconstruing the functionality of QToolBox widget or just missing the right setting?
What I am going for is something similar to the accordion fold type widget in Qt Creator:
I can't seem to get this "Sort" page to take only the size needed to display the button and field.
Unfortunately you can't do that directly because it will span all the available space that the title widgets don't occupy. You can emulate what you want by setting a fixed height on the QToolBox if you know the exact height your page(s). But you do not want to do that in practise.
If you want the behavior you ask for then you need to write your own custom control. It doesn't have to be hard. Use a QVBoxLayout and fill into it items of a custom class, let's call it ToolItem, which is a QWidget with a title (perhaps a button to show/hide) and another QWidget for showing the contents that is either visible or not.
The following very simple example will toggle the visibility of the ToolItem when it is clicked. And only when visible will it occupy any space.
class ToolItem : public QWidget {
public:
ToolItem(const QString &title, QWidget *item) : item(item) {
QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(new QLabel(title));
layout->addWidget(item);
setLayout(layout);
item->setVisible(false);
}
protected:
void mousePressEvent(QMouseEvent *event) {
item->setVisible(!item->isVisible());
}
private:
QWidget *item;
};
class ToolBox : public QWidget {
public:
ToolBox() : layout(new QVBoxLayout) {
setLayout(layout);
}
void addItem(ToolItem *item) {
// Remove last spacer item if present.
int count = layout->count();
if (count > 1) {
layout->removeItem(layout->itemAt(count - 1));
}
// Add item and make sure it stretches the remaining space.
layout->addWidget(item);
layout->addStretch();
}
private:
QVBoxLayout *layout;
};
And simple usage of it:
QWidget *window = new QWidget;
window->setWindowTitle("QToolBox Example");
QListWidget *list = new QListWidget;
list->addItem("One");
list->addItem("Two");
list->addItem("Three");
ToolBox *toolBox = new ToolBox;
toolBox->addItem(new ToolItem("Title 1", new QLabel("Some text here")));
toolBox->addItem(new ToolItem("Title 2", list));
toolBox->addItem(new ToolItem("Title 3", new QLabel("Lorem Ipsum..")));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(toolBox);
window->setLayout(layout);
window->resize(500, 500);
window->show();
You can now tweak it to look like the QToolBox if needed.
Please don't hesitate to ask follow-up questions.
The example shown from Qt Designer may not be using a QToolBox, which behaves more like a stacked tab widget only displaying a single page at a time. The example in Qt Designer appears to be a QTreeWidget with custom drawing or styling.
This is not the complete answer.
I traced down the actual component, it can be included outside designer (kind of). Here is a minimal example showing how to do that (modified from https://github.com/zdenekzc/qtdesigner-integration).
form.h
#ifndef FORM_H
#define FORM_H
#include <QMainWindow>
class FormWindow : public QMainWindow {
Q_OBJECT
public:
explicit FormWindow (QWidget * parent = 0);
};
#endif // FORM_H
form.cc
#include "form.h"
#include <QApplication>
#include <QtDesigner/QtDesigner>
#include <QtDesigner/QDesignerComponents>
FormWindow::FormWindow (QWidget* parent) : QMainWindow (parent) {
QDesignerFormEditorInterface* core = QDesignerComponents::createFormEditor (this);
core->setWidgetBox (QDesignerComponents::createWidgetBox (core, 0));
this->setCentralWidget (core->widgetBox());
}
extern "C" int main (int argc, char * * argv) {
QApplication app (argc, argv);
FormWindow * win = new FormWindow ();
win->show ();
return app.exec();
}
qt-designer.pro
QT += designer
HEADERS = form.h
SOURCES = form.cc
LIBS += -lQt5DesignerComponents
Build it:
mkdir -p build
cd build
qmake-qt5 ../qt5-design.pro
make
./qt5-design
This is obviously not useful by itself unless you want to build a designer but another step towards isolating the actual component.