How to change text alignment in QTabWidget in C++? - c++

This is the same question as in:
How to change text alignment in QTabWidget?
I tried to port that python code into C++ but it doesn't seem to work.
Here is header file:
#include <QTabBar>
class HorizontalTabWidget : public QTabBar
{
Q_OBJECT
public:
explicit HorizontalTabWidget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *);
QSize sizeHint() const;
};
Here is source file:
void HorizontalTabWidget::paintEvent(QPaintEvent *)
{
for(int index = 0; index < count(); index++)
{
QPainter * painter = new QPainter(this);
painter->begin(this);
painter->setPen(Qt::blue);
painter->setFont(QFont("Arial", 10));
QRect tabrect = tabRect(index);
painter->drawText(tabrect, Qt::AlignVCenter | Qt::TextDontClip, tabText(index));
painter->end();
}
}
QSize HorizontalTabWidget::sizeHint() const
{
return QSize(130, 130);
}
I use it by creating NewTabWidget class that inherits QTabWidget. In the constructor of NewTabWidget I use:
setTabBar(new HorizontalTabWidget);
This is done just to be able to use that tabWidget because setTabBar is protected. Here is what I get:
What am I missing?
Edit:
What I want to create is this but with icons on the top and the labels under the icons (as in Qt Creator):

the problem should be in the paint method; check if an example below would work for you, it should draw tabs the same way QTCreator does. I've reused the original tab style QStyleOptionTabV3 to do majority of paint work and then just rendered icon and tab's text on top of the image it produced:
class TestTabBar : public QTabBar
{
public:
explicit TestTabBar(QWidget* parent=0) : QTabBar(parent)
{
setIconSize(QSize(80, 80));
}
protected:
QSize tabSizeHint(int) const
{
return QSize(80, 80);
}
void paintEvent(QPaintEvent *)
{
QStylePainter p(this);
for (int index = 0; index < count(); index++)
{
QStyleOptionTabV3 tab;
initStyleOption(&tab, index);
QIcon tempIcon = tab.icon;
QString tempText = tab.text;
tab.icon = QIcon();
tab.text = QString();
p.drawControl(QStyle::CE_TabBarTab, tab);
QPainter painter;
painter.begin(this);
QRect tabrect = tabRect(index);
tabrect.adjust(0, 8, 0, -8);
painter.drawText(tabrect, Qt::AlignBottom | Qt::AlignHCenter, tempText);
tempIcon.paint(&painter, 0, tabrect.top(), tab.iconSize.width(), tab.iconSize.height(), Qt::AlignTop | Qt::AlignHCenter);
painter.end();
}
}
};
class TestTabWidget : public QTabWidget
{
public:
explicit TestTabWidget(QWidget *parent = 0) : QTabWidget(parent)
{
setTabBar(new TestTabBar());
}
};
tabwidget init:
TestTabWidget* test = new TestTabWidget(this);
test->setGeometry(20, 20, 300, 200);
test->addTab(new QWidget(), QIcon("icon0.png"), "test0");
test->addTab(new QWidget(), QIcon("icon1.png"), "test1");
test->setTabPosition(QTabWidget::West);
this worked fine on my ubuntu, hope it gonna work for you,
regards

Vasiliy, thanks for fixing the double QPainter bug.
However, calling setTabIcon() and setTabText() from within paintEvent() leads to an infinite recursion. Remember that tab is a local object, so
tab.text = QString();
does not affect tabText().
So, the example can also be written without making temporary copies and do
p.drawText(tabrect, Qt::AlignBottom | Qt::AlignHCenter, tabText(index));
tabIcon(index).paint(&p, tabrect, Qt::AlignTop | Qt::AlignHCenter);

This example does not work. leads to a fall program.
bring your own example with minor edits
- my system qt 4.6.3 for Windows and VS2008
class TestTabBar : public QTabBar
{
public:
explicit TestTabBar(QWidget* parent=0) : QTabBar(parent)
{
setIconSize(QSize(58, 68));
}
protected:
QSize tabSizeHint(int) const
{
return QSize(58, 68);
}
void paintEvent(QPaintEvent *)
{
QStylePainter p(this);
for (int index = 0; index < count(); index++)
{
QStyleOptionTabV3 tab;
initStyleOption(&tab, index);
QIcon tempIcon = tabIcon(index);
QString tempText = this->tabText(index);
QRect tabrect = tabRect(index);
tab.icon = QIcon();
tab.text = QString();
p.drawControl(QStyle::CE_TabBarTab, tab);
tabrect.adjust(0, 3, 0, -3);
p.setPen(Qt::black);
p.setFont(QFont("Arial", 7));
p.drawText(tabrect, Qt::AlignBottom | Qt::AlignHCenter, tempText );
tempIcon.paint(&p, tabrect, Qt::AlignTop | Qt::AlignHCenter);
this->setTabIcon(index, tempIcon );
this->setTabText( index, tempText);
}
}
};
class TestTabWidget : public QTabWidget
{
public:
explicit TestTabWidget(QWidget *parent = 0) : QTabWidget(parent)
{
setTabBar(new TestTabBar());
}
};

Related

How to resize the icon of specific QTabBar icon?

I'm trying to create an effect similar to this gif:
While you are hovering over the tab button it shows a text and also resizes the icon.
I was reading the docs about the property icon-size it says it supports QTabBar, but adding the property into the tab stylesheet, has no effect when the button is being hovered.
I'm doing something wrong or it's a 'bug'?
QTabBar::tab:hover {
font-size: 12pt;
icon-size: 64px 64px;
color: rgb(255, 255, 255);
background: transparent;
}
As I couldn't get it working with a stylesheet, I tried using an event filter:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::HoverEnter)
{
QTabBar *tabbar = static_cast<QTabBar*>(obj);
tabbar->setIconSize(QSize(40, 40));
}
else if (event->type() == QEvent::HoverLeave)
{
QTabBar *tabbar = static_cast<QTabBar*>(obj);
tabbar->setIconSize(QSize(32,32));
}
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
Result:
Whenever any tab button has hovered, it resizes all icons.
How I could resize just the tab button under the mouse instead of the entire "QTabBar"?
Tab stylesheet options from the gif above:
QTabWidget::pane {
background: transparent;
}
QTabBar::tab {
font-size: 12pt;
color: rgba(255, 255, 255, 0);
background: transparent;
}
QTabBar::tab:hover {
font-size: 12pt;
color: rgb(255, 255, 255);
background: transparent;
}
QTabBar::tab:selected {
background: transparent;
}
I don't understand what's going on with the text, the tab text is "Tab" but it's cropping the "T".
Why the tab pane background is not getting transparent?
The recommendation in my comment of yesterday:
The last resort might be to make a custom widget for the tab bar, and to use a QStackedWidget as container for the tab pages.
There are probably samples around. Instead of googling I didn't hesitate to add one on my own:
qIconBar.h:
#ifndef Q_ICON_BAR
#define Q_ICON_BAR
#include <vector>
#include <QIcon>
#include <QWidget>
class QIconBar: public QWidget {
Q_OBJECT
private:
Qt::Orientation _ori;
QSize _qSizeIcon = QSize(64, 64); // icon size
int _spc = 4; // size between
double _scl = 0.6; // scale factor for icons not under mouse
struct Tab {
QIcon qIcon;
QString qLabel;
};
std::vector<Tab> _tabs;
int _iTab = 0; // current tab
signals:
void currentChanged(int i);
void tabClicked(int i);
public:
/// constructor.
explicit QIconBar(
Qt::Orientation ori = Qt::Horizontal, QWidget* pQParent = nullptr);
/// destructor.
virtual ~QIconBar() = default;
// disabled:
QIconBar(const QIconBar&) = delete;
QIconBar& operator=(const QIconBar&) = delete;
public:
virtual QSize sizeHint() const;
QSize iconSize() const { return _qSizeIcon; }
void setIconSize(const QSize& qSizeIcon);
int addTab(const QIcon& qIcon, const QString& qText)
{
return insertTab(-1, qIcon, qText);
}
int insertTab(int i, const QIcon& qIcon, const QString& qText);
void removeTab(int i);
int tabAt(const QPoint& qPos) const;
QIcon tabIcon(int i) const
{
return (size_t)i < _tabs.size() ? _tabs[i].qIcon : QIcon();
}
void setTabIcon(int i, const QIcon& qIcon);
QString tabText(int i) const
{
return (size_t)i < _tabs.size() ? _tabs[i].qLabel : QString();
}
void setTabText(int i, const QString& qLabel);
int count() const { return (int)_tabs.size(); }
int currentIndex() const { return _iTab; }
void setCurrentIndex(int i);
protected:
virtual void paintEvent(QPaintEvent* pQEvent) override;
virtual void mouseMoveEvent(QMouseEvent* pQEvent) override;
virtual void mousePressEvent(QMouseEvent* pQEvent) override;
virtual void leaveEvent(QEvent* pQEvent) override;
};
#endif // Q_ICON_BAR
Concerning the API, I (shamelessly) borrowed from QTabBar.
qIconBar.cc:
#include <QMouseEvent>
#include <QPainter>
#include "qIconBar.h"
QIconBar::QIconBar(Qt::Orientation ori, QWidget* pQParent):
QWidget(pQParent),
_ori(ori)
{
setMouseTracking(true);
}
QSize QIconBar::sizeHint() const
{
const int n = (int)_tabs.size();
const int spc = std::max(n - 1, 0) * _spc;
return _ori == Qt::Vertical
? QSize(_qSizeIcon.width(), _qSizeIcon.height() * n + spc)
: QSize(_qSizeIcon.width() * n + spc, _qSizeIcon.height());
}
int QIconBar::insertTab(int i, const QIcon& qIcon, const QString& qText)
{
if ((size_t)i > _tabs.size()) i = (int)_tabs.size();
_tabs.insert(_tabs.begin() + i, { qIcon, qText });
update();
if (i <= _iTab && _iTab + 1 < (int)_tabs.size()) {
setCurrentIndex(_iTab + 1);
}
return i;
}
void QIconBar::removeTab(int i)
{
if ((size_t)i >= _tabs.size()) return;
_tabs.erase(_tabs.begin() + i);
update();
if ((size_t)_iTab >= _tabs.size()) {
setCurrentIndex(0);
}
}
int QIconBar::tabAt(const QPoint& qPos) const
{
if (!QRect(QPoint(), size()).contains(qPos)) return -1;
return _ori == Qt::Vertical
? qPos.y() % (_qSizeIcon.height() + _spc) < _qSizeIcon.height()
? qPos.y() / (_qSizeIcon.height() + _spc)
: -1
: qPos.x() % (_qSizeIcon.width() + _spc) < _qSizeIcon.width()
? qPos.x() / (_qSizeIcon.width() + _spc)
: -1;
}
void QIconBar::setCurrentIndex(int i)
{
_iTab = i;
emit currentChanged(_iTab);
update();
}
void QIconBar::paintEvent(QPaintEvent* pQEvent)
{
const QPoint qPosMouse = mapFromGlobal(QCursor::pos());
const int iTabMouse = tabAt(qPosMouse);
QPainter qPainter(this);
for (int i = 0, n = (int)_tabs.size(); i < n; ++i) {
const Tab& tab = _tabs[i];
const QPoint qPos(
(_ori == Qt::Horizontal) * i * (_qSizeIcon.width() + _spc)
+ _qSizeIcon.width() / 2,
(_ori == Qt::Vertical) * i *(_qSizeIcon.height() + _spc)
+ _qSizeIcon.height() / 2);
if (i == _iTab) {
QRect qRect(QPoint(), _qSizeIcon);
qRect.moveCenter(qPos);
qPainter.fillRect(qRect, palette().color(QPalette::Base));
}
if (!tab.qIcon.isNull()) {
const double scale = iTabMouse == i ? 1.0 : _scl;
const QSize qSizeIcon = scale * _qSizeIcon;
const QPixmap qPixmap = tab.qIcon.pixmap(qSizeIcon);
QRect qRect(QPoint(), qSizeIcon);
qRect.moveCenter(qPos);
qPainter.drawPixmap(qRect, qPixmap);
}
if (i == iTabMouse) {
QFont qFont = font();
qFont.setBold(true);
qPainter.setFont(qFont);
QRect qRect(QPoint(), _qSizeIcon);
qRect.moveCenter(qPos);
qPainter.drawText(qRect, Qt::AlignHCenter | Qt::AlignBottom,
tab.qLabel);
}
}
}
void QIconBar::mouseMoveEvent(QMouseEvent* pQEvent)
{
update();
QWidget::mouseMoveEvent(pQEvent);
}
void QIconBar::mousePressEvent(QMouseEvent* pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
const int iTab = tabAt(pQEvent->pos());
if ((size_t)iTab < _tabs.size()) {
setCurrentIndex(iTab);
emit tabClicked(iTab);
return;
}
}
QWidget::mousePressEvent(pQEvent);
}
void QIconBar::leaveEvent(QEvent* pQEvent)
{
update();
QWidget::leaveEvent(pQEvent);
}
A sample application testQIconBar.cc:
#include <QtWidgets>
#include "qIconBar.h"
const struct Item {
const char* filePathIcon;
const char* label;
} items[] = {
{ "icons/document-open.svg", "File" },
{ "icons/document-properties.svg", "Edit" },
{ "icons/edit-find.svg", "Find" },
{ "icons/help-help.svg", "Help" }
};
const size_t nItems = std::size(items);
int main(int argc, char** argv)
{
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("QIconBar Test");
qWinMain.resize(640, 480);
QHBoxLayout qHBox;
QIconBar qIconBar(Qt::Vertical);
qHBox.addWidget(&qIconBar);
QStackedLayout qStack;
qHBox.addLayout(&qStack, 1);
qWinMain.setLayout(&qHBox);
qWinMain.show();
// populate icon bar and stack
QFont qFont = qWinMain.font();
qFont.setPixelSize(30);
for (const Item& item : items) {
qIconBar.addTab(
QIcon(item.filePathIcon), QString::fromUtf8(item.label));
const QString qText
= QString("This is the page for item %1.").arg(item.label);
QLabel* pQLbl = new QLabel(qText);
pQLbl->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
pQLbl->setFont(qFont);
pQLbl->setAlignment(Qt::AlignCenter);
qStack.addWidget(pQLbl);
}
qStack.setCurrentIndex(qIconBar.currentIndex());
// install signal handlers
QObject::connect(&qIconBar, &QIconBar::currentChanged,
&qStack, &QStackedLayout::setCurrentIndex);
// runtime loop
return app.exec();
}
Additionally, I used the following CMakeLists.txt:
project(QIconBar)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5Widgets CONFIG REQUIRED)
include_directories("${CMAKE_SOURCE_DIR}")
qt5_wrap_cpp(moc_sources QIconBar.h
TARGET testQIconBar)
add_executable(testQIconBar
testQIconBar.cc qIconBar.cc qIconBar.h
${moc_sources})
target_link_libraries(testQIconBar Qt5::Widgets)
…and compiled this in Visual Studio 2019.
Output:
According to feedback, I tested my MCVE concerning transparency of the widgets — i.e. with a background image in the main window:
The QIconBar doesn't clear it's background — the contents is just drawn over the background of the parent window. (It's the same with the QLabels I used in the QStackedLayout.)

How to properly drop a widget on a QGraphicsView right under the mouse?

Following my previous post I have been trying to solve a positioning problem withing a QGraphicsView.
After a widget is dragged from a QListWidget and dropped inside a QGraphicsView, it jumps everywhere.
The problem I have been trying to solve with also the help of another user is the following:
As soon as the widget is dragged inside the QGraphicsView and dropped. It just goes in random locations and have to pay attention to retrieve it. This behavior is not user friendly and sometimes it takes a bit to locate the widget.
Below the code:
scene.h
#ifndef SCENE_H
#define SCENE_H
#include <QGraphicsScene>
class Scene : public QGraphicsScene
{
public:
Scene(QObject *parent = nullptr);
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
};
#endif // SCENE_H
scene.cpp
void Scene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
QByteArray encoded =
event->mimeData()->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
QStringList rosTables;
QString newString;
while (!stream.atEnd()) {
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
rosTables << roleDataMap[Qt::DisplayRole].toString();
}
for (const QString &tableType : rosTables) {
if (tableType == "Images") {
QPoint initPos(0, 0);
auto *wgt = new CustomTableWidget;
auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
QBrush(Qt::darkGreen));
auto *sizeGrip = new QSizeGrip(wgt);
auto *layout = new QHBoxLayout(wgt);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom);
connect(wgt, &CustomTableWidget::sizeChanged, [wgt, proxyControl](){
proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 10, 10));
});
wgt->setColumnCount(2);
wgt->setRowCount(2);
for (int ridx = 0; ridx < wgt->rowCount(); ridx++) {
for (int cidx = 0; cidx < wgt->columnCount(); cidx++) {
auto *item = new QTableWidgetItem();
item->setText(QString("%1").arg(ridx));
wgt->setItem(ridx,cidx,item);
}
}
auto *const proxy = addWidget(wgt);
proxy->setPos(initPos.x(), initPos.y()
+ proxyControl->rect().height());
proxy->setParentItem(proxyControl);
proxyControl->setPos(initPos.x(), initPos.y());
proxyControl->setFlag(QGraphicsItem::ItemIsMovable, true);
proxyControl->setFlag(QGraphicsItem::ItemIsSelectable, true);
proxyControl->setRect(wgt->geometry().adjusted(-10, -10, 10, 10));
}
}
}
What it was tried so far to solve the problem:
Now the QGraphicsScene have been subclassed to re-write the mouse events, in particular and in this case attention was given to the dropEvent(QGraphicsSceneDragDropEvent *event) as that is the responsible function to "see the widget"
Several trials and errors were conducted and in the same function it was tried to add the following part:
for (const QString &tableType : rosTables) {
if (tableType == "Images") {
QPoint initPos(event->scenePos()); // <-- Tried this but no change
auto *wgt = new CustomTableWidget;
auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
QBrush(Qt::darkGreen));
auto *sizeGrip = new QSizeGrip(wgt);
auto *layout = new QHBoxLayout(wgt);
}
An additional thing tried was to provide the QPoint the event->scenePos().toPoint() as deemed proper for the goal, but unfortunately that didn't solve the problem either:
for (const QString &tableType : rosTables) {
if (tableType == "Images") {
QPoint initPos(event->scenePos().toPoint()); // <-- Tried this too but no change
auto *wgt = new CustomTableWidget;
auto *proxyControl = addRect(0, 0, 0, 0, QPen(Qt::black),
QBrush(Qt::darkGreen));
auto *sizeGrip = new QSizeGrip(wgt);
auto *layout = new QHBoxLayout(wgt);
}
If anyone has an idea of what the problem might be please provide guidance on how to solve it.
Change proxy->setPos(initPos.x(), initPos.y() + proxyControl->rect().height()); to proxy->setPos(10, 10);.
Set scene rect:
Scene::Scene(QObject *parent) :
QGraphicsScene(parent)
{
setBackgroundBrush(Qt::lightGray);
setSceneRect(0, 0, 1000, 1000);
}
Set view alignment, e.g. in MainWindow.cpp:
ui->graphicsView->setAlignment(Qt::AlignLeft | Qt::AlignTop);

how to draw lines able to change color in Qt?

there is a question disturbing me, maybe it's just a simple question for you guys.
i want to draw tow lines in my Qt widget, when the QComboBox cbx is set at the item 1, the first line is set to red, second black, on the contrary, 2, fist black, second red.
i used *paintEvent* to draw my lines. (actually, if you want to draw something, you must draw in paintEvent function) But i dont know how to change the line's color based on the QComboBox item, follows are my codes.
#include "changecolor.h"
#include "ui_changecolor.h"
QString st;
QPainter painter;
ChangeColor::ChangeColor(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ChangeColor)
{
ui->setupUi(this);
setFixedSize(2000, 1000);
QComboBox * cbx = new QComboBox(this);
cbx->setSizeAdjustPolicy(QComboBox::AdjustToContents);
cbx->addItem("1", Qt::DisplayRole);
cbx->addItem("2", Qt::DisplayRole);
st = cbx->currentText();
connect(cbx, SIGNAL(currentTextChanged(QString)), this, SIGNAL(changeColorSlot(st, painter)));
}
void ChangeColor::paintEvent(QPaintEvent*)
{
// QPainter painter(this);
// painter.setPen(Qt::black);
// painter.drawLine(QPoint(100,100), QPoint(1100,100));
// painter.drawLine(QPoint(100,100), QPoint(100,600));
// changeColorSlot(painter, );
changeColorSlot(st, painter);
}
void ChangeColor::changeColorSlot(QString& st, QPainter& painter)
{
// QPainter painter(this);
if(st == tr("1"))
{
painter.setPen(Qt::black);
painter.drawLine(QPoint(100,100),QPoint(1100,100));
painter.setPen(Qt::red);
painter.drawLine(QPoint(100,100),QPoint(100,600));
}
else if(st == tr("2"))
{
painter.setPen(Qt::red);
painter.drawLine(QPoint(100,100),QPoint(1100,100));
painter.setPen(Qt::black);
painter.drawLine(QPoint(100,100),QPoint(100,600));
}
update();
}
ChangeColor::~ChangeColor()
{
delete ui;
}
these codes denote my painful coding life, i mean i have tried many times, but there is no right result. thank you guys.
you have to set the logic that controls the state(the color of the lines) outside the widget:
here an example with the relevant parts
.h:
#include
namespace Ui
{
class ChangeColor;
}
class ChangeColor : public QWidget
{
Q_OBJECT
public:
explicit ChangeColor(QWidget *parent = nullptr);
void paintEvent(QPaintEvent* pe);
~ChangeColor();
public slots:
void setColorState(int state);
private:
Ui::ChangeColor* ui{nullptr};
int state{};
};
.cpp
...
void ChangeColor::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter paint(this);
QColor RED_COLOR{255, 0, 0};
QColor BLACK_COLOR{0, 0, 0};
if(state == 0)
{
paint.setPen(QPen(RED_COLOR));
paint.setBrush(RED_COLOR);
}
else
{
paint.setPen(QPen(BLACK_COLOR));
paint.setBrush(BLACK_COLOR);
}
paint.drawLine(0, 0, width(), height());
}
void ChangeColor::setColorState(int state)
{
this->state = state;
update();
}
I suppose you are looking for this feature:
QComboBox* cb = ...;
QAbstractItemView* view = cb->view();
view->setAlternatingRowColors(true);

Custom QGraphicsItem That Contains Child QGraphicsItems

I am new at Qt and I want to write my custom QGraphicsItem which contains a rectangle and couple of buttons. I want to write a single custom component that could be easily added to QGraphicsScene and moved or resized with contents(buttons and rectangles) in it. In the end I want to add multiple customized QGraphicsItem to my QGraphicsScene. My question is how can I write this customized QGraphicsItem that contains buttons and rectangles which relative positions to each other are constant.
In this drawing green colored rectangles represent buttons and their relative position to each other always stays same (as if they are placed using qlayouts)
Thanks to #replete, from the example at http://doc.qt.io/qt-5/qtwidgets-graphicsview-dragdroprobot-example.html I was able to create a custom QGraphicsItem with clickable sub-parts in it. In code below BboxItem represents container QGraphicsItem and BboxItemContent represents childs of it. By emitting signals whith mause click events I was able to implement button like features. And I can move the BboxItem by setting its bounding rectangle.
BboxItem related source code:
BboxItemContent::BboxItemContent(QGraphicsItem *parent, int type, QColor color,QRectF *rect)
: QGraphicsObject(parent)
{
content_rectangle = rect;
content_type = type;
switch (type)
{
case 0:
rectangle_color = color;
icon = 0;
break;
case 1:
icon = new QImage(":/resource/assets/info_btn.png");
break;
case 2:
icon = new QImage(":/resource/assets/close_btn.png");
break;
}
}
BboxItemContent::~BboxItemContent()
{
delete icon;
}
QRectF BboxItemContent::boundingRect() const
{
return QRectF(content_rectangle->x(), content_rectangle->y(), content_rectangle->width(), content_rectangle->height());
}
void BboxItemContent::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if (icon == 0)
{
QPen pen(rectangle_color, 3);
painter->setPen(pen);
painter->drawRect(*content_rectangle);
}
else
{
painter->drawImage(*content_rectangle, *icon);
}
}
void BboxItemContent::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
emit bboxContentClickedSignal();
}
void BboxItemContent::setRect(QRectF *rect)
{
content_rectangle = rect;
update();
}
BboxItem::BboxItem(QGraphicsItem *parent,QRectF *itemRect) : BboxItemContent(parent,0,Qt::red, itemRect)
{
setFlag(ItemHasNoContents);
bbox_area = new BboxItemContent(this, 0, Qt::red, itemRect);
info_btn = new BboxItemContent(this, 1, Qt::red, new QRectF(itemRect->x() - 30, itemRect->y(), 30, 30));
connect(info_btn, &BboxItemContent::bboxContentClickedSignal, this, &BboxItem::onInfoClickedSlot);
delete_btn= new BboxItemContent(this, 2, Qt::red, new QRectF((itemRect->x()+itemRect->width()), itemRect->y(), 30, 30));
connect(delete_btn, &BboxItemContent::bboxContentClickedSignal, this, &BboxItem::onDeleteClickedSlot);
}
void BboxItem::onDeleteClickedSlot()
{
//delete clicked actions
}
void BboxItem::onInfoClickedSlot()
{
//info clicked actions
}
void BboxItem::setRect(QRectF *rect)
{
bbox_area->setRect(rect);
info_btn->setRect(new QRectF(rect->x() - 30, rect->y(), 30, 30));
delete_btn->setRect(new QRectF((rect->x() + rect->width()), rect->y(), 30, 30));
}
Related Headers:
class BboxItemContent : public QGraphicsObject
{
Q_OBJECT
public:
BboxItemContent(QGraphicsItem *parent = 0, int type = 0, QColor color = Qt::red, QRectF *rect=nullptr);
~BboxItemContent();
// Inherited from QGraphicsItem
QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
void setRect(QRectF *rect);
signals:
void bboxContentClickedSignal();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
private:
QImage *icon;
QColor rectangle_color;
QRectF *content_rectangle;
int content_type;
};
class BboxItem : public BboxItemContent {
Q_OBJECT
public:
BboxItem(QGraphicsItem *parent = 0,QRectF *itemRect=nullptr);
void setRect(QRectF *rect);
private slots:
void onDeleteClickedSlot();
void onInfoClickedSlot();
private:
BboxItemContent *delete_btn;
BboxItemContent *bbox_area;
BboxItemContent *info_btn;
};

How to create a vertical (rotated) button in Qt with C++

I'd like to create a vertical button in Qt (using C++, not Python), with text rotated 90º either clockwise or counterclockwise. It doesn't seem to be possible with a standard QPushButton.
How could I do it?
In order to create a vertical button in Qt, you can subclass QPushButton so that the dimensions reported by the widget are transposed, and also modify the drawing event to paint the button with the proper alignment.
Here's a class called OrientablePushButton that can be used as a drop-in replacement of the traditional QPushButton but also supports vertical orientation through the usage of setOrientation.
Aspect:
Sample usage:
auto anotherButton = new OrientablePushButton("Hello world world world world", this);
anotherButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
anotherButton->setOrientation(OrientablePushButton::VerticalTopToBottom);
Header file:
class OrientablePushButton : public QPushButton
{
Q_OBJECT
public:
enum Orientation {
Horizontal,
VerticalTopToBottom,
VerticalBottomToTop
};
OrientablePushButton(QWidget * parent = nullptr);
OrientablePushButton(const QString & text, QWidget *parent = nullptr);
OrientablePushButton(const QIcon & icon, const QString & text, QWidget *parent = nullptr);
QSize sizeHint() const;
OrientablePushButton::Orientation orientation() const;
void setOrientation(const OrientablePushButton::Orientation &orientation);
protected:
void paintEvent(QPaintEvent *event);
private:
Orientation mOrientation = Horizontal;
};
Source file:
#include <QPainter>
#include <QStyleOptionButton>
#include <QDebug>
#include <QStylePainter>
OrientablePushButton::OrientablePushButton(QWidget *parent)
: QPushButton(parent)
{ }
OrientablePushButton::OrientablePushButton(const QString &text, QWidget *parent)
: QPushButton(text, parent)
{ }
OrientablePushButton::OrientablePushButton(const QIcon &icon, const QString &text, QWidget *parent)
: QPushButton(icon, text, parent)
{ }
QSize OrientablePushButton::sizeHint() const
{
QSize sh = QPushButton::sizeHint();
if (mOrientation != OrientablePushButton::Horizontal)
{
sh.transpose();
}
return sh;
}
void OrientablePushButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QStylePainter painter(this);
QStyleOptionButton option;
initStyleOption(&option);
if (mOrientation == OrientablePushButton::VerticalTopToBottom)
{
painter.rotate(90);
painter.translate(0, -1 * width());
option.rect = option.rect.transposed();
}
else if (mOrientation == OrientablePushButton::VerticalBottomToTop)
{
painter.rotate(-90);
painter.translate(-1 * height(), 0);
option.rect = option.rect.transposed();
}
painter.drawControl(QStyle::CE_PushButton, option);
}
OrientablePushButton::Orientation OrientablePushButton::orientation() const
{
return mOrientation;
}
void OrientablePushButton::setOrientation(const OrientablePushButton::Orientation &orientation)
{
mOrientation = orientation;
}