QProgressBar with StyleSheet - c++

I would like to have a vertical progressbar with vertical (top-to-bottom) text inside it. I wonder if and how can this be achieved with style sheets. I can`t change style of the whole application or completely change it for the widget ( no "apply plastic style solution available). Could someone provide me with some example? If you know any other way to achieve this it will be helpful as well.

Proposed answer is to complicated. It can be done in much more simple way (less code).
First of all you should subclass QProxyStyle.
This should be done more or less like that (override drawControl):
class MyProxyStyle : public QProxyStyle
{
public:
void QStyle::drawControl(ControlElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget = 0) const
{
if (element == CE_ProgressBarLabel) {
// create coppy of style option:
QStyleOptionProgressBar op(*static_cast<QStyleOptionProgressBar*>(option));
// prepare style option for rotation
op.rect = QTransform().rotate(-90).mapRect(op.rect);
// optional: op.orientation = (op.orientation==Qt::Horizontal)?Qt::Vertical:Qt::Horizontal;
painter->rotate(90); // rotate painter
QProxyStyle::drawControl(element, &op, painter, widget);
painter->rotate(-90); // restore painter state - its a must
return;
}
QProxyStyle::drawControl(element, option, painter, widget);
}
};
It is possible that I've messed up with angles, but general concept should be clear.
Note that this is much better approach since:
code is simple
you do not break style, if style will be changed all should look an fill right (you are not using hard coded painting of label).

I think the best way to do it is just to subclass QProgressBar, not using stylesheet.
You have a similar example here with QScrollbar, hope it will help ;)
Draw text on scrollbar
A small parenthesis, if you want to apply a stylesheet to only one component, you have to write something like this:
widgetType#widgetName
{
...
}

Related

How to expand tabs in QTabWidget Qt

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();
}
};

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);
}
}
};

Change Expand/Collapse icon in wxDataViewCtrl

I want to replace the Expand/Collapse icon with my icons in wxDataViewCtrl.
I am not able to find a way to set my expand/collapse icons.
Is there any API available for the same?
or
Do I need to modify the wxDataViewCtrl source directly? your prior experience can help me.
The main point of having wxDataViewCtrl in the first place is that it wraps the corresponding native control, so changing its look and feel is not supported.
This being said, if you're using its generic implementation, as is always the case under MSW, you can actually change it by defining a custom wxRendererNative-derived class and overriding its DrawTreeItemButton() method. But this would affect all the other controls drawing tree-like buttons, including, obviously, generic wxTreeCtrl itself but also wxPropGrid. Generally speaking I wouldn't recommend doing this.
I do not know whether it is right approach or wrong, Here I found a way to achieve my thing based on VZ.suggestion.
class MyRenderer : public wxDelegateRendererNative
{
public:
MyRenderer() : wxDelegateRendererNative(wxRendererNative::GetDefault()) { }
virtual void DrawTreeItemButton(wxWindow *win,
wxDC& dc,
const wxRect& rect,
int flags = 0)
{
MyCustomCtrl* pWin = dynamic_cast<MyCustomCtrl*>(win->GetParent());
if (pWin != nullptr)
{
// Draw my choice of expand/collapse button.
}
else
{ // Do not affect other controls that are using this drawing.
wxRendererNative::GetDefault().DrawTreeItemButton(win, dc, rect, flags);
}
}
};
This way I can override the
virtual void DrawItemSelectionRect(wxWindow *win,
wxDC& dc,
const wxRect& rect,
int flags)
and many more native drawings.

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()).