How to expand tabs in QTabWidget Qt - c++

I have a QTabWidget like this one:
But I want to expand the tabs to "fill" the entire widget width, like this:
How can I do that?
I am using Qt 5.3.2 and Qt Creator 3.2.1
Update:
I tried to use the setExpanding function:
ui->myTabWidget->tabBar()->setExpanding(true);
But it didn't work.

I found that QTabBar has a setExpanding method, which appears to do exactly what you want, but I tried it (on Windows), and it doesn't work. This is the code:
ui->tabWidget->tabBar()->setExpanding (true);
Then I found the following post:
https://forum.qt.io/topic/47404/qtabbar-will-not-expand-its-tabs
I find the answer provided in the above post to be debatable. He says it's respecting the operating system style whether or not the expanding property is set to true and that it's a feature, not a bug, and that you have to subclass QTabBar to get the desired behavior. If I write code to do a specific thing, I feel like my instructions should override the OS style. If I just wanted the OS style, I would leave that special code out. However much I disagree with the implementation, that appears to be what we're stuck with.
So if it's important to you to have this look, then you'll need to subclass QTabBar, override the tabSizeHint--I suspect that will take some experimentation--and use QTabWidget::setTabBar to replace the default with your own. According to the documentation, you have to do that before adding any tabs, so this mechanism is not workable if you want to create your tab widget in Qt Designer. (Another argument in favor of implementing setExpanding as an override to the OS style rather than the way it's been done.)

Just set both expanding and document mode to true.
ui->tabWidget->tabBar()->setDocumentMode(true);
ui->tabWidget->tabBar()->setExpanding(true);
Works for me :)

As mostefa answered here, I can set a fixed width for the tabs using styleSheet.
I am calculating the width based on the QTabWidget width.
To get the QTabWidget width correctly I need to get it in the showEvent function:
void LogListForm::showEvent(QShowEvent *ev)
{
/*
* Divide by 2 because we have 2 tabs.
* I need to decrease 24 pixels to fill the width correctly.
*/
int tabWidth = (ui->myTabWidget->width()/2)-24;
/*
* Then, I set this tabWidth to the styleSheet.
* Note: I need to set the previously styleSheet to not lose it
*/
ui->myTabWidget->setStyleSheet( ui->myTabWidget->styleSheet() +
"QTabBar::tab {"
"width: " + QString::number(tabWidth) + "px; }" );
}

This works as well:
QTabWidget::tab-bar
{
min-width:1000;
}

// Qt 5.12.2
// just use TabWidget in place of QTabWidget, nothing else
class TabWidget : public QTabWidget
{
class TabBar : public QTabBar
{
QSize _size;
public:
TabBar(QWidget* a_parent) : QTabBar(a_parent)
{
setWidth(size().width());
}
QSize tabSizeHint(int index) const
{
return QSize(_size.width()/(count()?count():1), size().height());
}
void setWidth(int a_width)
{
_size = QSize(a_width, size().height());
QTabBar::resize(_size);
}
};
TabBar* _tabBar = new TabBar(this);
public:
TabWidget(QWidget* a_parent) : QTabWidget(a_parent)
{
setTabBar(_tabBar);
}
void resizeEvent(QResizeEvent *e) override
{
_tabBar->setWidth(size().width());
QTabWidget::resizeEvent(e);
}
};

this worked for me:
ui->myTabWidget->tabBar()->setDocumentMode(true);

Suggestion from Mitak worked. Assuming the tabwiget is in the centralwidget of the mainwindow, one can replace a tabwidget created with the designer as follow in the code :
QTabWidget* newTabWidget = new GeneralTabWidget(ui->tabWidget_ToReplace->parentWidget());
ui->centralWidget->layout()->replaceWidget(ui->tabWidget_ToReplace, newTabWidget );
delete ui->tabWidget_ToReplace;
ui->tabWidget_ToReplace= newTabWidget ;
if the tabwidget is situated in another location or in a dialog, one need to replace it in the appropriate layout.

Not pretty, but what worked for me was to forced the value setExpending in the minimumSizeHint override.
class A: public QTabWidget
{
A(QWidget *p = nullptr): QTabWidget(p)
{
setDocumentMode(true);
}
virtual QSize minimumSizeHint() const override
{
tabBar()->setExpanding(true);
return QTabWidget::minimumSizeHint();
}
};

Related

QToolTip Style Sheet not working if using QToolTip::showText

I'm trying to use QSS to customize the look of the tool-tip, it works. However, if I try showing the tooltip using QToolTip::showText function, it won't work as intended which means that the QToolTip style sheet doesn't apply to it probably?
My purpose: When changing a slider value I want to show a rectangle somewhere near the slider, so I thought the tooltip was the easiest way to do it?
If you don't understand what I mean, I'm trying to make sliders in Qt feel like Windows 10 UWP so if you want to understand what I mean look for example any slider in Windows like go to Settings->System->Sound and see how their slider works. I've done everything they have except that rectangle which shows when value get changed.
What I'm trying to do:
In the style sheet
QToolTip
{
color: red;
}
In my custom Slider class (inherited from QSlider)
class FSlider : public QSlider
{
Q_OBJECT
public:
FSlider(QWidget *parent = 0) : QSlider(parent) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(notifyValueChanged(int))); }
protected:
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
bool event(QEvent* event);
private slots:
void notifyValueChanged(int Value);
};
void FSlider::notifyValueChanged(int Value)
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sliderHandle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle);
QToolTip::setFont(QFont("Segoe UI", 13, 400));
QToolTip::showText(mapToGlobal(QPoint(sliderHandle.x() - sliderHandle.width() - 2, sliderHandle.y() - sliderHandle.height() - 30)), QString::asprintf("%i", Value));
}
Thanks for reading !
I had the same issue at first with Qt 5.12.2. I was missing the third argument to QToolTip::showText. I had to add the parent widget. In that case, the CSS that I had applied to the tooltip in mainwindow.ui applied to the QToolTip . Without the parent, it is a separate window shown on the position specified and has no knowledge of the css.

Double Click on QTreeView Expand Item Arrow

In my Qt 5.6.2 project, I noticed that if you double click on a QTreeView item (the actual arrow part, not the item text) the first click toggles the expanded state, and the second click does nothing.
I would instead like the second click to again toggle the expanded state.
I tried treeView->setExpandsOnDoubleClick(false); but the behaviour is still the same since it appears to not affect the arrow part of the item at all. It looks like Qt is deciding for me how the arrow should react to a double click regardless of the property expandsOnDoubleClick. How can I resolve this?
(Note: this behaviour didn't exist in Qt 5.0.2. Unsure about intermediate Qt versions.)
I was able to solve this by subclassing QProxyStyle and checking for the style hint SH_ListViewExpand_SelectMouseType and returning a value of 3 instead of the default 2.
class MyProxyStyle : public QProxyStyle
{
public:
int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const
{
if(hint == QStyle::SH_ListViewExpand_SelectMouseType)
return 3;
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
}

Why do I not see the drop indicator in a QTableView?

I use drag and drop in my QTableView (works). However, I do not see any drop indicator. I should see a line where the drop is supposed to be inserted, shouldn't I? At least here they say so.
My init is pretty much standard.
// see model for implementing logic of drag
this->viewport()->setAcceptDrops(allowDrop);
this->setDragEnabled(allowDrag);
this->setDropIndicatorShown(true);
this->m_model->allowDrop(allowDrop);
I have no idea why I do not see the indicator. A style sheet is used with the views, could that be the reason. However, I have disabled the stylesheet and still do not see it.
The view uses entire rows for selection, not sure if this causes an issue. So any hint is appreciated.
-- Edit --
As of the comment below, tried all selection modes: single, multi or extended, no visual effect. Also tried cell instead of row selection, again no improvement.
-- Edit 2 --
Currently evaluating another style proxy example, similar to the one below, originally referenced here
-- Related --
QTreeView draw drop indicator
How to highlight the entire row on mouse hover in QTableWidget: Qt5
https://forum.qt.io/topic/12794/mousehover-entire-row-selection-in-qtableview/7
https://stackoverflow.com/a/23111484/356726
I faced the same problem, I tried two options which both worked for me. IIRC, the help came from an answer on SO.
if you are subclassing QTreeView, you can override its paintEvent() method. It is calling by default the drawTree() method and the paintDropIndicator() one (the latter being part of QAbstractItemView private class).
You can call drawTree() from your paintEvent(), and it should override the default drag and drop indicator as well :
class MyTreeView : public QTreeView
{
public:
explicit MyTreeView(QWidget* parent = 0) : QTreeView(parent) {}
void paintEvent(QPaintEvent * event)
{
QPainter painter(viewport());
drawTree(&painter, event->region());
}
};
the other method is to subclass QProxyStyle and overriding the drawPrimitive() method. When you get the element QStyle::PE_IndicatorItemViewItemDrop as a parameter, you can paint it your own way.
The code will look like this:
class MyOwnStyle : public QProxyStyle
{
public:
MyOwnStyle(QStyle* style = 0) : QProxyStyle(style) {}
void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
if (element == QStyle::PE_IndicatorItemViewItemDrop)
{
//custom paint here, you can do nothing as well
QColor c(Qt::white);
QPen pen(c);
pen.setWidth(1);
painter->setPen(pen);
if (!option->rect.isNull())
painter->drawLine(option->rect.topLeft(), option->rect.topRight());
}
else
{
// the default style is applied
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
};

QToolbar force expand on too many QActions

Hi all is there any way to automatically expand a QToolbar if there is too many QActions in?
Using Qt version 5.4.1 C++11
Ive tried :ui->mainToolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred)
But this only expands it horizontally. I need it to expand vertically like the Expand button does.
Always expanding a toolbar vertically is not possible as far as I know (never seen it). A solution would be to add multiple toolbars. This way, you can arrange them one under the other.
What you can try is to add a custom widget to the toolbar that grows horizontally. This was proposed here by using a QScrollArea... Not sure whether this is exactly what you want, but it may work good enough.
This is how you can make a function to expand/retract a QToolbar. Firstly using a Forloop get all the child widgets from the QToolbar. You can use a Bool to lock to only get the first Widget which is the Expanding button/Action.
bool get_first_action{true};
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(get_first_action)
{
get_first_action = false;
// This is the expanding action!
m_action_expand = widget;
}
}
Or you can do this which is probably a bit safer.
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(widget->objectName() == "qt_toolbar_ext_button")
{
// This is the expanding action!
m_action_expand = widget;
}
}
Once you have the sneaky expanding action assign it to a member varible
// Make sure to initialize this in the constructor!
// m_action_expand = new QWidget(this // parent)
QWidget* m_action_expand;
Now create a handy function with a good name;
void MainWindow::forceToolbarExpand()
{
// Grab the position of the expanding action/widget
QPointF pos(m_action_expand->pos());
// Create a fake/dummy event that replicates the mouse button press
QMouseEvent event_press(QEvent::MouseButtonPress, pos, Qt::LeftButton,0, 0);
// Create a fake/dummy event that replicates the mouse button release
QMouseEvent event_release(QEvent::MouseButtonRelease, pos, Qt::LeftButton,0, 0);
// These two events together will fire the QAction::Toggled signal.
// Make sure to send the events!
QApplication::sendEvent(m_action_expand, &event_press);
QApplication::sendEvent(m_action_expand, &event_release);
}
And there we have it your QToolbar, if it can be expanded/retracted now will when you call this function. I'm not too sure if you can directly Moc/fake the toggled event but you can try it. I know this method works so yeah.

PyQt: how to handle auto-resize of widgets when their content changes

I am having some issues with the size of qt4 widgets when their content changes.
I will illustrate my problems with two simple scenarios:
Scenario 1:
I have a QLineEdit widget. Sometimes, when I'm changing its content using QLineEdit.setText(), the one-line string doesn't fit into the widget at its current size anymore. I must select the widget and use the arrow keys to scroll the string in both directions in order to see it all.
Scenario 2:
I have a QTextEdit widget. Sometimes, when I'm changing its content using QTextEdit.setHtml(), the rendered HTML content doesn't fit into the widget at its current size anymore. The widget starts displaying horizontal and/or vertical scroll bars and I can use them to scroll the HTML content.
What I would want in such scenarios is to have some logic that decides if after a content change, the new content won't fit anymore into the widget and automatically increase the widget size so everything would fit.
How are these scenarios handled?
I'm using PyQt4.
Edit: after reading both the comment and the first answer (which mentions typing content into the widget), I went over the question one more time. I was unpleasantly surprised to find out a horrible typo. I meant QTextBrowser when I wrote QTextEdit, my apologies for misleading you. That is: I have a widget which renders HTML code that I'm changing and I would want the widget to grow enough to display everything without having scrollbars.
As for QLineEdit instead of QLabel - I went for QLineEdit since I've noticed I can't select text from a QLabel with the mouse for copying it. With QLineEdit it is possible.
I'm answering in C++ here, since that's what I'm most familiar with, and your problem isn't specific to PyQt.
Normally, you just need to call QWidget::updateGeometry() when the sizeHint() may have changed, just like you need to call QWidget::update() when the contents may have changed.
Your problem, however, is that the sizeHint() doesn't change when text is added to QLineEdit and QTextEdit. For a reason: People don't expect their dialogs to grow-as-they-type :)
That said, if you really want grow-as-you-type behaviour in those widgets you need to inherit from them and reimplement sizeHint() and minimumSizeHint() to return the larger size, and potentially setText(), append() etc. to call updateGeometry() so the sizehint change is noticed.
The sizehint calculation won't be entirely trivial, and will be way easier for QLineEdit than for QTextEdit (which is secretly a QAbstractScrollArea), but you can look at the sizeHint() and minimumSizeHint() implementations for inspiration (also the one for QComboBox, which has a mode to do exactly what you want: QComboBox::AdjustToContents.
EDIT: Your two usecases (QTextBrowser w/o scrollbars and QLineEdit instead of QLabel just for selecting the text in there) can be solved by using a QLabel and a recent enough Qt. QLabel has gained both link-clicking notification and so-called "text-interaction flags" (one of which is TextSelectableByMouse) in Qt 4.2. The only difference that I was able to make out is that loading new content isn't automatic, there's no history, and there's no micro focus hinting (ie. tabbing from link to link) in QLabel.
For the QTextBrowser case you should be able to get the size of the document using
QTextBrowser::document()->size();
after setting the html, and then resizing it the QTextBrowser afterwards.
i achieve a similar effect by using the following C++ class:
textedit.h
#ifndef TEXTEDIT_H
#define TEXTEDIT_H
#include <QTextEdit>
class TextEdit : public QTextEdit
{
Q_DISABLE_COPY( TextEdit )
public:
TextEdit( QWidget* parent = NULL );
TextEdit( const QString& text, QWidget* parent = NULL );
virtual ~TextEdit();
void fitToDocument( Qt::Orientations orientations );
virtual QSize sizeHint() const;
private:
int fittedHeight_;
Qt::Orientations fittedOrientations_;
int fittedWidth_;
};
#include "textedit-inl.h"
#endif // TEXTEDIT_H
textedit-inl.h
#ifndef TEXTEDITINL_H
#define TEXTEDITINL_H
#include "textedit.h"
inline TextEdit::TextEdit( QWidget* parent ) :
QTextEdit( parent ), fittedOrientations_( 0 )
{ }
inline TextEdit::TextEdit( const QString& text, QWidget* parent ) :
QTextEdit( text, parent ), fittedOrientations_( 0 )
{ }
inline TextEdit::~TextEdit()
{ }
inline QSize TextEdit::sizeHint() const
{
QSize sizeHint = QTextEdit::sizeHint();
if( fittedOrientations_ & Qt::Horizontal )
sizeHint.setWidth( fittedWidth_ );
if( fittedOrientations_ & Qt::Vertical )
sizeHint.setHeight( fittedHeight_ );
return sizeHint;
}
#endif // TEXTEDITINL_H
textedit.cpp
#include "textedit.h"
void TextEdit::fitToDocument( Qt::Orientations orientations )
{
QSize documentSize( document()->size().toSize() );
QSizePolicy sizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
if( orientations & Qt::Horizontal ) {
fittedWidth_ = documentSize.width() + (width() - viewport()->width());
sizePolicy.setHorizontalPolicy( QSizePolicy::Fixed );
}
if( orientations & Qt::Vertical ) {
fittedHeight_ = documentSize.height() + (width() - viewport()->width());
sizePolicy.setVerticalPolicy( QSizePolicy::Fixed );
}
fittedOrientations_ = orientations;
setSizePolicy( sizePolicy );
updateGeometry();
}
for example, calling TextEdit::fitToDocument( Qt::Horizontal ) will set the widget's width to a fixed width exactly large enough to fit the document and its surroundings (e.g. a vertical scrollbar, if there is one). if your goal is to have this happen whenever the contents change, connect the QTextEdit::textChanged() signal to a slot which calls TextEdit::fitToDocument().
as for your issue with QLabel, the solution is simple: call QLabel::setTextInteractionFlags( Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse ).
Maybe take a look at Python QT Automatic Widget Resizer. It's written in python though but it may give you some ideas on how to go about doing what you need.
Ok implement sizeHint() method. And every time your content change size call updateGeometry()
When content change without changing size use update(). (updateGeometry() automatically call update()).