Here is my problem, I display Buttons and Labels manually with setGeometry()
method.
MyClass which inherits from QWidget
Here is my code :
void MyClass::function() {
QLabel *imgP;
QLabel *name;
QPushButton *newConv;
QPixmap *profilPic;
std::stringstream ss;
int j = 0;
int i = 0;
for (int tmp = 0; tmp < 15; tmp++)
{
if (i % 7 == 0 && i != 0)
{
i = 0;
j++;
}
ss << "Username " << tmp;
name = new QLabel(tr(ss.str().c_str()), this);
name->setAlignment(Qt::AlignCenter);
name->setGeometry((30 * (i + 1) + 240 * i), (30 + 390 * j),
240, 60);
profilPic = new QPixmap("./gui/img/avatar1.png");
imgP = new QLabel(this);
imgP->setPixmap(profilPic->scaled(240, 240));
imgP->setGeometry((30 * (i + 1) + 240 * i), (90 + 390 * j),
240, 240);
newConv = new QPushButton(tr("Chat"), this);
newConv->setGeometry((30 * (i + 1) + 240 * i), (330 + 390 * j),
240, 60);
newConv->setFocusPolicy(Qt::NoFocus);
connect(newConv, SIGNAL(released()), this, SLOT(addTab()));
ss.str("");
ss.clear();
i++;
}
}
It may be a bit more complicated than it should, but it works just the way I wanted ..
it looks like this :
As you can see, the result is good, I have my contacts displayed, but the 15th element is hidden because the window is too small. So my question is :
How can I make a ScrollView when this happens ?
I already know QScrollArea
, but I would have to work with QBoxLayout
, and I don't think this will do the job.
Thanks for your help !
EDIT
This is my MainWidget class which displays the window :
class QTabBar;
MainWidget::MainWidget(QWidget *parent) : QWidget(parent)
{
setFixedSize(1920, 1200);
setWindowTitle(tr("Babel"));
QVBoxLayout *mainLayout = new QVBoxLayout;
QTabBar *tb;
UiContact *contact = new UiContact(this);
QScrollArea *contactScrollArea = new QScrollArea();
contactScrollArea->setWidget(contact);
_tabWidget = new QTabWidget;
tb = _tabWidget->tabBar();
_tabWidget->addTab(new Home(), tr("Home"));
_tabWidget->addTab(contactScrollArea, tr("Contact"));
std::ostringstream oss;
_tabWidget->setTabsClosable(true);
connect(_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
tb->tabButton(0, QTabBar::RightSide)->hide();
tb->tabButton(1, QTabBar::RightSide)->hide();
_tabWidget->setFocusPolicy(Qt::NoFocus);
mainLayout->addWidget(_tabWidget);
setLayout(mainLayout);
}
QScrollArea
is certainly the way to go. It is not necessary to use a layout, it's only necessary to force the widget to be the size it needs to be. A QScrollArea
can handle any QWidget
derived class, and it will display its scrollbars as needed.
Practically: if you can calculate how much space you need (which I think you can do), and set the size constraints of the containing widget accordingly, the QScrollArea will show the scrollbars automatically. You can use QWidget::setMinimumSize
for that purpose, a simple setMinimumSize(itemWidth*7,itemHeight*(count%7))
should suffice.
This method does allow the widget to grow as large as to fill the QScrollArea
. Also, it's possible to disable the frame around the QScrollArea
if preferred.
edit:
You probably have something like this:
MyClass *myClass = new MyClass();
...
tabs->insertTab(1,myClass,"Contact");
An example implementation in that situation would be:
MyClass *myClass = new MyClass();
...
QScrollArea* contactScrollArea = new QScrollArea();
contactScrollArea->setWidget(myClass);
tabs->insertTab(1,contactScrollArea,"Contact");
It will place a scroll area inside the tab widget and put the instance of MyClass inside it
A QScrollArea
is used as a relatively high container class. Think of a QScrollArea
as a widget which can embed another widget inside it. By default it creates an empty widget, which can get a layout. But by using setWidget
, you can literally place anything you want in it. A possible "combination" is a QLabel
with a large licence text or any other widget that can potentially grow too large (like your widget).
Now, just some extra information:
When you draw stuff yourself (not creating several widgets and code your own layout stuff), you may use QAbstractScrollArea
. It would give you full control. It gives scrollbars and a separate middle widget to which you can draw during paintEvent
. But I think that's beyond the scope of this.
Now, for sake of clean coding. I would actually suggest you create a separate class ContactItem
. It would consist of that label, image and pushbutton, held together with a QVBoxLayout
(which makes them neatly packed above eachother). This "item" can be put inside a QGridLayout
. This way, you don't need to concern yourself with arranging the items. If you set a minimum size on the picture, it will make sure the items are your preferred width/height. The label size constraints make sure that font differences don't affect the presentation (same goes for the buttons). Last but not least, your list is suddenly resizable. (By using setRowStretch
and setColumnStretch
you can make sure your list is centered, or top aligned, in case the window is larger than your list takes up.
Here I have a possible implementation (it isn't my best code) from what I got from the screenshot and given code.
It consists of a 'Widget' which is responsible for the tab layout:
#include <QWidget>
#include "uicontact.h"
class QTabWidget;
class MainWidget : public QWidget
{
Q_OBJECT
QTabWidget *_tabWidget;
public:
MainWidget(QWidget *parent = 0);
~MainWidget();
public slots:
void addChatTab();
};
#include "mainwidget.h"
#include <QScrollArea>
#include <QTabWidget>
#include <QTabBar>
#include <QVBoxLayout>
#include "home.h"
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(1920,1200);
setWindowTitle(tr("Babel"));
QVBoxLayout *mainLayout = new QVBoxLayout;
QTabBar *tb;
UiContact *contact = new UiContact(this);
QScrollArea *contactScrollArea = new QScrollArea();
contactScrollArea->setWidget(contact);
_tabWidget = new QTabWidget;
tb = _tabWidget->tabBar();
_tabWidget->addTab(new Home(), tr("Home"));
_tabWidget->addTab(contactScrollArea, tr("Contact"));
_tabWidget->setTabsClosable(true);
connect(_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
tb->tabButton(0, QTabBar::RightSide)->hide();
tb->tabButton(1, QTabBar::RightSide)->hide();
_tabWidget->setFocusPolicy(Qt::NoFocus);
mainLayout->addWidget(_tabWidget);
setLayout(mainLayout);
}
MainWidget::~MainWidget()
{
}
void MainWidget::addChatTab()
{
_tabWidget->addTab(new QWidget, tr("Number %1").arg(_tabWidget->count()-2));
}
The class MyClass
is responsible for creating the 'contact' tab:
#include "uicontact.h"
#include "mainwidget.h"
#include <QLabel>
#include <QPushButton>
UiContact::UiContact(MainWidget *owner,QWidget *parent) : QWidget(parent)
{
QLabel *imgP;
QLabel *name;
QPushButton *newConv;
QPixmap *profilPic;
int j = 0;
int i = 0;
for (int tmp = 0; tmp < 15; tmp++)
{
if (i % 7 == 0 && i != 0)
{
i = 0;
j++;
}
name = new QLabel(tr("Username %1").arg(tmp), this);
name->setAlignment(Qt::AlignCenter);
name->setGeometry((30 * (i + 1) + 240 * i), (30 + 390 * j),
240, 60);
profilPic = new QPixmap("./gui/img/avatar1.png");
imgP = new QLabel(this);
imgP->setPixmap(profilPic->scaled(240, 240));
imgP->setGeometry((30 * (i + 1) + 240 * i), (90 + 390 * j),
240, 240);
newConv = new QPushButton(tr("Chat"), this);
newConv->setGeometry((30 * (i + 1) + 240 * i), (330 + 390 * j),
240, 60);
newConv->setFocusPolicy(Qt::NoFocus);
connect(newConv, SIGNAL(clicked(bool)), owner, SLOT(addChatTab()));
i++;
}
setMinimumSize(270*7,420*(15/7+1));
}
The uicontact.h
file is very trivial so omitted.
A few things to note: MyClass
receives an 'owner' pointer, this allows it to talk directly to the top level widget responsible for adding tabs. Probably you want to look at QSignalMapper
to be able to map the individual QPushButton
s to a more known value. With QSignalMapper
, you can map the button to an integer, string, QWidget*
or QObject*
.
Also note the tr("Contact %1").arg(tmp)
which is the correct way to make your program locale aware.