I currently have a QTabWidget and some layout.
Now I want all the tabs of my QtabWidget to occupy the width of the QtabWidget so I have something like this. The following method expands the two tabs in my Qtabwidget so they are the same size as the QtabWidget
void Contacts::AdjustTabs( int pos, int index )
{
std::string orig_sheet = ui.tabWidget->styleSheet().toStdString();
QString new_style = QString("QTabBar::tab { width: %1px; } ").arg((ui.tabWidget->size().width() /ui.tabWidget->count()));
std::string t = new_style.toStdString();
orig_sheet = orig_sheet + t;
ui.tabWidget->setStyleSheet(orig_sheet.c_str());
}
The above method works fine. Now the problem starts when the QTabwidget is set in a Qsplitter layout along with some other layout.At startup construction the above method does not seem to work for instance I have something like this
Someclass::SomeConstructor()
{
ui.setupUi(this);
.........
QObject::connect(ui.splitter,SIGNAL(splitterMoved(int,int)),this,SLOT(AdjustTabs(int,int)));
AdjustTabs(0,0);
}
Now when the form appears the horizontal size of the tabs remain unchanged (they do not expand - which is wrong). However when i move the splitter the tabs expand and everything is fine. My question is how can I make my tabs expand during form load ? Why arent they expanding ?
I think trying to explicitly adjust the tabs' sizes whenever a resize event occurs isn't the best approach. If things are set up properly, the appropriate size adjustments should naturally occur due to the default behavior of the QSplitter and QTabWidget's internal layout managers. Here is a very simple example of what I mean; note that there is no code in this program to explicitly handle resizing of anything, and yet all the tabs resize correctly as the splitter is moved and/or the window is resized:
#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QSplitter>
#include <QTabWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow mw;
QSplitter * splitter = new QSplitter(&mw);
QTabWidget * tab = new QTabWidget;
tab->addTab(new QPushButton("Content of tab #1"), "tab #1");
tab->addTab(new QPushButton("Content of tab #2"), "tab #2");
tab->addTab(new QPushButton("Content of tab #3"), "tab #3");
splitter->addWidget(tab);
splitter->addWidget(new QPushButton("Some other content"));
mw.setCentralWidget(splitter);
mw.show();
return a.exec();
}
So the question becomes -- why aren't your tabs resizing themselves automatically? Perhaps there you have their sizePolicy property set to Fixed, rather than Preferred or Ignored?
Related
I have label created and want tooltip over it, I want to set tooltip's maximum and minimum width but somehow its not working.
I am not expert to QT, not able to figure out the reason.
Code:
#include "widget.h"
#include <QApplication>
#include <QListWidget>
#include <QListWidgetItem>
#include <QLabel>
#include <QHBoxLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget listWidget;
listWidget.setContentsMargins(QMargins(0, 0, 0, 0));
for (int i = 0; i < 5; ++i)
{
QListWidgetItem* item = new QListWidgetItem();
auto *itemWidget = new QWidget();
auto *textLabel = new QLabel(QString("Item %1").arg(i + 1), itemWidget);
textLabel->setMinimumWidth(100); //Not working whatever value I set
textLabel->setMaximumWidth(400); //Not working whatever value I set
textLabel->setToolTip("<p>This is the looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooonggggggggggggggg name </p>");
listWidget.addItem(item);
listWidget.setItemWidget(item, itemWidget);
}
listWidget.show();
return a.exec();
}
Tooltip:
can someone please help.
You cant directly set max and min on tooltip , hence you should indrectly do that(for this usecase):
static const QString FORMATTER = "<p>%1</p>";
QString tooltip =
"This is the "
"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooonggggggggggggggg name.";
static constexpr auto maximum = 10;
textLabel->setToolTip(FORMATTER.arg(tooltip.mid(0, maximum)));
Update:
if you want exactly have your widget with any properties for tooltip , you can override your events and show the ToolTipWidget that reimplemented.
i.e: Qt WIdget inside ToolTip
textLabel->setMinimumWidth(100);
textLabel->setMaximumWidth(400);
This will set the min/max width of the label itself not the tooltip.
If you want to customize tooltip behavior, you'll have to override the event() function to catch QEvent::ToolTip (and probably QEvent::ToolTipChange) and draw it yourself using QToolTip::showText()
please use :textLabel->setToolTip
Having a QLineEdit with a plain vanilla QStringList QCompleter. I wonder if I can change the appearance of the dropdown (I want to have either a min. size or smaller scrollbar).
Clarification: I want to set it in a stylesheet, not in the code.
Summary of my findings so far:
Pretty good summary here: https://forum.qt.io/topic/26703/solved-stylize-using-css-and-editable-qcombobox-s-completions-list-view/12
I have to use QStyledItemDelegate and
give the popup a name for the qss selector
I have tried that and it does not work for me, but seems to work for others
A simple straight forward solution is to set the stylesheet of the QScrollBar used by the popup of the QCompleter. My knowledge of qss is little, so I don't know if you can set a minimum size that way, but you can always have a look at verticalScrollBar().
Here is some code for the qss way:
#include <QAbstractItemView>
#include <QCompleter>
#include <QLineEdit>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLineEdit edit;
edit.show();
QStringList completionList;
for (int a = 0 ; a < 10 ; ++a) {
completionList << QString("test%1").arg(a);
}
QCompleter completer(completionList);
edit.setCompleter(&completer);
QAbstractItemView *popup = completer.popup();
popup->setStyleSheet("QScrollBar{ width: 50px;}");
return a.exec();
}
This question already has answers here:
Unable to delete the widgets in sub-layout of a layout in Qt
(3 answers)
Closed 6 years ago.
I am using Qt5 (beginner) on Windows7.
In the main window of my app I want to display and remove some push-buttons.
widget = new ButtonWidget(ui->frame); // frame is a QScrollArea
connect(ui->addBtns, SIGNAL(clicked()), widget, SLOT(addButtons()));
connect(ui->deleteBtns, SIGNAL(clicked()), widget, SLOT(deleteButtons()));
And the ButtonWidget class is here:
ButtonWidget::ButtonWidget(QWidget * parent) : QWidget(parent)
{
//addButtons();
}
void ButtonWidget::addButtons()
{
QStringList texts{"1\nok", "2\nok", "3\nok", "4\nok", "5\nok", "6\nok"};
gridLayout = new QGridLayout;
for(int i = 0; i < texts.size(); i++)
{
QPushButton * button = new QPushButton(texts[i]);
gridLayout->addWidget(button, i / 5, i % 5);
}
setLayout(gridLayout);
}
// I'm not sure this method/function is ok... :(
void ButtonWidget::deleteButtons()
{
QLayoutItem * child;
while((child = gridLayout->takeAt(0)) != 0)
{
gridLayout->removeWidget(child->widget());
delete child->widget();
delete child;
}
delete gridLayout;
}
Problem is: when I click on add_buttons, I get all buttons displayed, but they are shrunk, tiny or something... :
OTOH... if I remove the comment from addButtons() call in the constructor (hence calling from within the constructor), the result is ok:
So, finally I have 2 questions:
1) How to fix the code to be able to add those buttons properly (when add_buttons is clicked)?
2) Is the deleteButtons() method ok?
First, I would recommend to do
gridLayout = new QGridLayout(this);
in the constructor. You don't need to delete all the grid, create it again and set in as a layout when you can just remove its content (with delete button for example) and fill it again afterwards.
EDIT : comment of Werner Erasmus : No need to set the layout. The fact that it has a parent widget implies that it sets itself up.
The problem is that is you do not modify addButtons() you will substitute the previous buttons without knowing where they go.
Also, try to give the QPushButton a parent :
new QPushButton(texts[i],this);
For your second point : Removing widgets from QGridLayout
EDIT:
After some more testing (without looking at the source code, but suspecting that it must be safe to delete button, else things would be brittle), I've implemented removeButtons as follows:
void ButtonWidget::deleteButtons()
{
while(myLayout->count())
{
delete myLayout->itemAt(0)->widget();
}
}
This works, and it proves that deleting a widget also removes the widget from it's parent, and layouts associated with the parent (by slots hooked up to when a child widget is deleted). The above code confirms this, as count(), which refers to number of layout items, decrease to zero (and those layout items are managed by the Layout). takeAt(x) is never called, and doesn't need to be - simply delete the widgets (buttons). Wholla!
ORIGINAL ANSWER
As mentioned in my other post, you only need to delete the buttons (it is removed from its parent automatically). However, if a widget was added to a layout, the parent of that layout becomes the parent of the widget, and an associated QLayoutItem is created that is managed by the layout itself. To the delete the buttons, the safest way is to take all the layout items (ownership the taker's responsibility), delete each items associated widget, and the delete each item. I'll try and find relevant references apart from the sources...
The following code works:
//ButtonWidget.h
#include <QWidget>
#include <QScrollArea>
#include <QHBoxLayout>
class ButtonWidget : public QScrollArea
{
Q_OBJECT
public:
ButtonWidget(QWidget *parent = 0);
~ButtonWidget();
void addButtons();
void deleteButtons();
private:
QHBoxLayout* myLayout;
};
//ButtonWidget.cpp
#include "ButtonWidget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QLayoutItem>
ButtonWidget::ButtonWidget(QWidget * parent) :
QScrollArea(parent),
myLayout(new QHBoxLayout(this))
{
}
ButtonWidget::~ButtonWidget()
{
}
void ButtonWidget::addButtons()
{
QStringList texts{"1\nok", "2\nok", "3\nok", "4\nok", "5\nok", "6\nok"};
for(int i = 0; i < texts.size(); i++)
{
myLayout->addWidget(new QPushButton(texts[i]));
}
}
void ButtonWidget::deleteButtons()
{
QLayoutItem * child;
while((child = myLayout->takeAt(0)) != 0)
{
delete child->widget();
delete child;
}
}
#include "ButtonWidget.h"
#include <QApplication>
#include <QScrollArea>
#include <QPushButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <memory>
std::unique_ptr<QScrollArea> makeArea()
{
std::unique_ptr<QScrollArea> area(new QScrollArea);
auto layout = new QGridLayout(area.get());
auto addButton = new QPushButton("Add");
auto removeButton = new QPushButton("Remove");
layout->addWidget(addButton, 0, 0);
layout->addWidget(removeButton, 0, 1);
auto btnWidget = new ButtonWidget;
layout->addWidget(btnWidget,1,0,1,2);
QObject::connect(addButton, &QPushButton::clicked, [=]()
{
btnWidget->addButtons();
});
QObject::connect(removeButton, &QPushButton::clicked, [=]()
{
btnWidget->deleteButtons();
});
return move(area);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
auto area = makeArea();
area->show();
return a.exec();
}
You need to enable c++11 in your config (.pro) to get the lambdas working.
CONFIG += c++11
I've used QHBoxLayout for your buttons, as it better models what you want. Although strictly not necessary, I'm returning unique_ptr from the makeArea in main, as it has not parent, I'm not sure whether it gets some parent because it is the first widget created, but unique_ptr shows intent.
NOTE:
Apparently the layout item is not the parent of the widget, but the widget associated with the layout itself is the parent of widgets belonging to its layout item.
In my GUI, I would like to add a QComboBox to a verticalLayout programmatically based on a a signal triggered by specific action. The following code works fine and the widget is added:
QComboBox* userOptions = new QComboBox();
ui->verticalLayout_13->addWidget(userOptions);
However, this way the widget is always added to the end of the layout.
My question is: how to position the added QComboBox to the verticalLayout in alignment to another widget in the same layout ? (i.e.: above the "Go" push button for example)
There doesn't seem to be a way to explicitly insert an item in a layout where you want it to be.
You have a few choices to achieve that the "hard" way:
use QLayout::takeAt(int index) to take all items after the index you want to insert at, insert your item, then insert back the taken items.
create a placeholder widget which you can use to reserve an index in the layout, then you don't insert the item in the layout, but in a layout nested inside the placeholder widget. Without an item, the placeholder widget takes no space, and expands to accommodate whatever is put into it.
implement your own QLayout subclass which supports inserting at a specific index. There are several functions you will have to implement.
EDIT: An omission on my end, as Kuba Ober noted, most of the concrete layout implementations support inserting at a specific index, for example QBoxLayout derived have insert methods which pass an index as a parameter.
First, iterate a layout to find the index of the reference item you're inserting relative to. Then use the concrete layout's specific widget insertion/addition functionality.
Since you presumably use a QBoxLayout, you'd use its insertWidget method to insert a widget.
// https://github.com/KubaO/stackoverflown/tree/master/questions/insert-widget-36746949
#include <QtWidgets>
namespace SO { enum InsertPosition { InsertBefore, InsertAfter }; }
bool insertWidget(QBoxLayout * layout, QWidget * reference, QWidget * widget,
SO::InsertPosition pos = SO::InsertBefore, int stretch = 0,
Qt::Alignment alignment = 0) {
int index = -1;
for (int i = 0; i < layout->count(); ++i)
if (layout->itemAt(i)->widget() == reference) {
index = i;
break;
}
if (index < 0) return false;
if (pos == SO::InsertAfter) index++;
layout->insertWidget(index, widget, stretch, alignment);
return true;
}
Similar functions can be easily devised for QFormLayout, QGridLayout and QStackedLayout.
And a test harness:
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
QVBoxLayout l{&w};
QLabel first{"First"};
QLabel second{"Second"};
l.addWidget(&first);
l.addWidget(&second);
insertWidget(&l, &first, new QLabel{"Before First"}, SO::InsertBefore);
insertWidget(&l, &second, new QLabel{"After Second"}, SO::InsertAfter);
w.show();
return app.exec();
}
I want to be able to stop a user from moving a QSplitter at runtime. Calling setEnabled(false) does this, but it also disables all child widgets - which isn't what I want. Is there a way to achieve this? Do I have to disable the splitter, and then manually re-enable all child widgets? That seems rather cumbersome, for something that must be a reasonably common practise.
Can anyone suggest anything?
Do this:
for (int i = 0; i < splitter->count(); i++)
{
QSplitterHandle *hndl = splitter->handle(i);
hndl->setEnabled(false);
}
Actually, I've never seen anyone ever disable a splitter: They are there so the user can layout the UI as she needs, so why would anyone want to disable this? Either you need a splitter or you can use one of the normal layouts (which the user can't resize).
If you still want to try, I think you should look at closestLegalPosition() or getRange(). If you just return the width of the widget, then resizing should stop working.
You have to do two things. Set the widgets (that shouldn't be resizeable) inside the splitter to FixedSize and change the cursor of the correspondent splitter handles to Qt::ArrowCursor. The handles start with zero (left and not used), so the first handle between two widgets is by index 1.
Here's a sample (put the code in main.cpp):
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.resize(800, 300);
window.setWindowTitle("Splitter Test");
window.show();
QSplitter *splitter = new QSplitter(&window);
QListView *listview = new QListView;
QTreeView *treeview = new QTreeView;
QTextEdit *textedit = new QTextEdit;
splitter->addWidget(listview);
splitter->addWidget(treeview);
splitter->addWidget(textedit);
splitter->setChildrenCollapsible(false);
splitter->show();
listview->show();
treeview->show();
textedit->show();
//make the lisview 'fix'
listview->setFixedSize(listview->width(), listview->height());
//change the cursor over the splitter handle between listview and
//treeview to ArrowCursor
splitter->handle(1)->setCursor(Qt::ArrowCursor);;
return app.exec();
}
Now the first splitter handle is disabled and the second works.