QtCharts - Background, foreground display - c++

I want to display a QGraphicsRectItem in my QChartView. But the rectangle is displayed behind the lines series in the chart.
I've tried to do a setZValue(10), for example, on my QGraphicsRectItem and setZValue(0) on my QChart but it is still displayed behind.
Obviously I want the informations in the rectangle to be displayed in front of the series of the chart.
Constructor
StatisticsChartView::StatisticsChartView(QWidget *parent, QChart *chart)
: QChartView(chart, parent)
{
/* Create new chart */
_chart = new QChart();
chart = _chart;
_chart->setAnimationOptions(QChart::AllAnimations);
/* Default granularity */
m_iGranularity = DEFAULT_GRANULARITY;
/* Creating ellipse item which will display a circle when the mouse goes over the series */
m_ellipse = new QGraphicsEllipseItem(_chart);
penEllipse.setColor(QColor(0, 0, 0));
penBorder.setWidth(1);
m_ellipse->setPen(penEllipse);
/* Creating text item which will display the x and y value of the mouse position */
m_coordX = new QGraphicsSimpleTextItem(_chart);
m_coordY = new QGraphicsSimpleTextItem(_chart);
penBorder.setColor(QColor(0, 0, 0));
penBorder.setWidth(1);
m_coordX->setPen(penBorder);
m_coordY->setPen(penBorder);
m_rectHovered = new QGraphicsRectItem(_chart);
m_rectHovered->setBrush(QBrush(Qt::yellow));
m_coordHoveredX = new QGraphicsSimpleTextItem(m_rectHovered);
m_coordHoveredY = new QGraphicsSimpleTextItem(m_rectHovered);
penBorder.setColor(QColor(0, 0, 0));
penBorder.setWidth(1);
m_coordHoveredX->setPen(penBorder);
m_coordHoveredY->setPen(penBorder);
m_lineItemX = new QGraphicsLineItem(_chart);
m_lineItemY = new QGraphicsLineItem(_chart);
penLine.setColor(QColor(0, 0, 0));
penLine.setStyle(Qt::DotLine);
m_lineItemX->setPen(penLine);
m_lineItemY->setPen(penLine);
/* Enable zooming in the rectangle drawn with the left click of the mouse, zoom out with right click */
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
mousePressed = 0;
seriesHovered = false;
setMouseTracking(true);
_chart->setAcceptHoverEvents(true);
_chart->setZValue(50);
m_ellipse->setZValue(10); //so it is displayed over the series
m_coordHoveredX->setZValue(20); //so it is displayed over others
setRenderHint(QPainter::Antialiasing);
setChart(_chart);
}
Creation of series
void StatisticsChartView::drawCurve(bool bDrawScale)
{
int w = WIDTH;
int h = HEIGHT;
/* Creating series */
QLineSeries *lineFalse = new QLineSeries();
QLineSeries *lineAutomatic = new QLineSeries();
QLineSeries *lineOk = new QLineSeries();
QLineSeries *lineFalsePositive = new QLineSeries();
QLineSeries *lineManualTreatement = new QLineSeries();
QLineSeries *lineFalseNegative = new QLineSeries();
QList<QLineSeries*> lineSeriesList;
lineSeriesList << lineFalse << lineAutomatic << lineOk << lineFalsePositive << lineManualTreatement << lineFalseNegative;
QList<QString> nameSeriesList;
nameSeriesList << "False" << "Automatic" << "Ok" << "FalsePositive" << "ManualTreatement" << "FalseNegative";
QList<QVector<GraphPoint>> graphPointList;
graphPointList << gpFalse << gpDetected << gpOk << gpDetectedNotOk << gpManualTreatement << gpFalseNegative;
double graphX = 100.0 / (m_iGranularity);
bool pointsVisible = true;
for (int n = 0; n < lineSeriesList.count(); ++n)
{
/* Adding points to line series */
for (int i = 0; i < m_iGranularity + 1; ++i)
{
lineSeriesList[n]->append(i * graphX, (float)(graphPointList[n][i]).fValue * 100);
lineSeriesList[n]->setPointsVisible(pointsVisible);
lineSeriesList[n]->setName(nameSeriesList[n]);
}
}
_chart->legend()->setVisible(true);
_chart->legend()->setAlignment(Qt::AlignBottom);
/* Setting axis X and Y */
axisX = new QValueAxis();
axisY = new QValueAxis();
axisX->setRange(0, 100);
axisY->setRange(0, 100);
/* Adding line series to the chart and attaching them to the same axis */
for (int j = 0; j < lineSeriesList.count(); ++j)
{
_chart->addSeries(lineSeriesList[j]);
_chart->setAxisX(axisX, lineSeriesList[j]);
_chart->setAxisY(axisY, lineSeriesList[j]);
connect(lineSeriesList[j], SIGNAL(hovered(QPointF, bool)), this, SLOT(onSeriesHovered(QPointF, bool)));
}
_chart->resize(w, h);
return;
}
Drawing rectangle on chart
void StatisticsChartView::onSeriesHovered(QPointF point, bool state)
{
seriesHovered = state;
/* Updating the size of the rectangle */
if (mousePressed == 0 && seriesHovered == true)
{
/* x and y position on the graph */
qreal x = _chart->mapToPosition(point).x();
qreal y = _chart->mapToPosition(point).y();
/* x and y value on the graph from 0 to 100 for ou graph */
qreal xVal = point.x();
qreal yVal = point.y();
qreal maxX = axisX->max();
qreal minX = axisX->min();
qreal maxY = axisY->max();
qreal minY = axisY->min();
/* We don't want to display value outside of the axis range */
if (xVal <= maxX && xVal >= minX && yVal <= maxY && yVal >= minY)
{
m_coordHoveredX->setVisible(true);
m_coordHoveredY->setVisible(true);
m_rectHovered->setVisible(true);
m_ellipse->setVisible(true);
m_rectHovered->setRect(x - 31, y - 31, 30, 30);
qreal rectX = m_rectHovered->rect().x();
qreal rectY = m_rectHovered->rect().y();
qreal rectW = m_rectHovered->rect().width();
qreal rectH = m_rectHovered->rect().height();
/* We're setting the labels and nicely adjusting to chart axis labels (adjusting so the dot lines are centered on the label) */
m_coordHoveredX->setPos(rectX + rectW / 4 - 3, rectY + 1);
m_coordHoveredY->setPos(rectX + rectW / 4 - 3, rectY + rectH / 2 + 1);
/* Setting value to displayed with four digit max, float, 1 decimal */
m_coordHoveredX->setText(QString("%1").arg(xVal, 4, 'f', 1, '0'));
m_coordHoveredY->setText(QString("%1").arg(yVal, 4, 'f', 1, '0'));
m_ellipse->setRect(QRectF::QRectF(x, y, 10, 10));
m_ellipse->setPos(x, y);
m_ellipse->setBrush(QBrush(Qt::red));
}
else
{
/* We're not displaying information if out of the chart */
m_coordHoveredX->setVisible(false);
m_coordHoveredY->setVisible(false);
m_rectHovered->setVisible(false);
m_ellipse->setVisible(false);
}
}
else
{
/* We're not displaying information if series aren't hovered */
m_coordHoveredX->setVisible(false);
m_coordHoveredY->setVisible(false);
m_rectHovered->setVisible(false);
m_ellipse->setVisible(false);
}
}

You should try using a series especially for your rectangle.
Setting it as the last series, on your chart, to be above the other lines. And adding a legend or a callout for the text.

Related

QGroupBox's child restricts shrink the form

I have a multiple screen video player, and I just want to keep 16:9 ratio. There is a qgroupbox as a container of a qwidget which plays video in it. I also use qgroupbox to show selected frame by painting border to green. I can't do this on qwidget because rendered video overlaps that. When I have done with resize, I emit a signal with mouseup event to be able to informed about the resize operation completed. Then I calculate new bounds for qwidget to keep 16:9 ratio and apply this values for qwidget. Here is the image to show you how my app looks like:
And here is the code that I use to resize qwidgets:
void playBack::OnWindowResized()
{
float ratio = 16.0f / 9.0f;
float w = playBackplayer_contList.at(0)->size().width(); //qgroupbox's width
float h = playBackplayer_contList.at(0)->size().height();//qgroupbox's height
float currentRatio = w / h;
float newW = 0;
float newH = 0;
if (currentRatio > ratio)
{
newH = h;
newW = h*ratio;
}
else if (currentRatio < ratio)
{
newW = w;
newH = w / ratio;
}
qDebug() << "NEW WIDGET SIZE: " << (int)newW << " x " << (int)newH;
for (int i = 0; i < playBackplayer_widgtList.count(); i++)
{
playBackplayer_widgtList.at(i)->setMinimumSize(newW, newH);
//playBackplayer_widgtList.at(i)->resize(newW, newH);
}
}
This code works perfectly when I enlarge form, but When I want to shrink, It doesn't allow me to do that. Because I set a minimum value for qwidgets. If I don't use setMinimumSize, use resize(w,h) instead, than orientation problems occur. And here is a example for this issue:
This code below shows ctor and this is where I set the layout:
playBack::playBack()
{
playback_player_1_widget = new QWidget;
playback_player_2_widget = new QWidget;
playback_player_3_widget = new QWidget;
playback_player_4_widget = new QWidget;
playback_player_1_widget_cont = new QGroupBox;
playback_player_2_widget_cont = new QGroupBox;
playback_player_3_widget_cont = new QGroupBox;
playback_player_4_widget_cont = new QGroupBox;
playBackplayer_widgtList.append(playback_player_1_widget);
playBackplayer_widgtList.append(playback_player_2_widget);
playBackplayer_widgtList.append(playback_player_3_widget);
playBackplayer_widgtList.append(playback_player_4_widget);
playBackplayer_contList.append(playback_player_1_widget_cont);
playBackplayer_contList.append(playback_player_2_widget_cont);
playBackplayer_contList.append(playback_player_3_widget_cont);
playBackplayer_contList.append(playback_player_4_widget_cont);
int rowcnt = 0;
int colcnt = 0;
for (int i = 0; i < 4; i++)
{
playBackplayer_contList.at(i)->setStyleSheet(QString("border:1px solid #000;background-color:#000;"));
playBackplayer_widgtList.at(i)->setStyleSheet(QString("background-color:#f00;"));
QGridLayout* layout = new QGridLayout;
layout->setRowStretch(0, 1);
layout->setColumnStretch(0, 1);
layout->setRowStretch(2, 1);
layout->setColumnStretch(2, 1);
playBackplayer_widgtList.at(i)->setMinimumWidth(100);
playBackplayer_widgtList.at(i)->setMinimumHeight(100);
playBackplayer_widgtList.at(i)->setMaximumWidth(10000);
playBackplayer_widgtList.at(i)->setMaximumHeight(10000);
layout->addWidget(playBackplayer_widgtList.at(i),1,1);
layout->setMargin(0);
layout->setSpacing(0);
playBackplayer_contList.at(i)->setLayout(layout);
mainLayout->addWidget(playBackplayer_contList.at(i), colcnt, rowcnt);
rowcnt++;
if (rowcnt % 2 == 0)
{
rowcnt = 0;
colcnt++;
}
playBackplayer_widgtList.at(i)->setAcceptDrops(true);
}
}
I have tried various things, I have tried to set size 0 for qwidget before resize, (in mousedownevent) that didn't work, I have tried deleting layout for qgroupbox, after resize happens, create new layout and set it for groupbox, that didn't work, I have tried layout()->adjustSize(), update(), repaint(), all that stuff didn't work. What am I missing? I need helps from you. Any help would be appreciated. Thank you in advance.
Do away with the grid layout inside the container group boxes. Instead, align and resize the video widget with setGeometry
Here is a simple subclass of QGroupBox I made that keeps your desired ratio and always stays in the center:
class RatioGroupBox : public QGroupBox{
Q_OBJECT
public:
RatioGroupBox(QWidget *parent = nullptr) : QGroupBox (parent){
setFlat(true);
setStyleSheet("border:1px solid #000;background-color:#000;");
setMinimumSize(100, 100);
setMaximumSize(10000, 10000);
ratio = 16.0f/9.0f;
ratioWidget = new QWidget(this);
ratioWidget->setStyleSheet("background: #f00;");
ratioWidget->setAcceptDrops(true);
}
protected:
void resizeEvent(QResizeEvent *){//or you can use your own resize slot
float w = width();
float h = height();
float currentRatio = w/h;
float newW(0);
float newH(0);
if (currentRatio > ratio){
newH = h;
newW = h*ratio;
}
else if (currentRatio < ratio){
newW = w;
newH = w / ratio;
}
ratioWidget->setGeometry((w-newW)/2, (h-newH)/2, newW, newH);
}
private:
QWidget *ratioWidget;
float ratio;
};
Your entire ctor will become something like:
playBack::playBack()
{
for(int r=0; r<2; r++){
for(int c=0; c<2; c++){
RatioGroupBox* playback_player_cont = new RatioGroupBox;
mainLayout->addWidget(playback_player_cont, c, r);
playBackplayer_contList.append(playback_player_cont);
}
}
}
You can of course access your video widgets by exposing ratioWidget any way you like. Either by making it public or creating a getter function.

QChart log axis data not shown

I have a 2Y Axis plo with a QValueAxison the y1 and QLogValueAxis on y2.
The linear plot is shown, the log plot is not shown. The data for both is the same.
I wonder how to set up the ticks and limits for the log axis?
This is the code:
chart = new QChart();
chart->legend()->hide();
chart->setTitle("Histogramm");
axisX = new QValueAxis;
axisX->setLabelFormat("%g");
chart->addAxis(axisX, Qt::AlignBottom);
series = new QLineSeries;
chart->addSeries(series);
axisY = new QValueAxis;
axisY->setTitleText("linear scale");
axisY->setLinePenColor(series->pen().color());
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisX);
series->attachAxis(axisY);
serieslog = new QLineSeries;
chart->addSeries(serieslog);
axisY3 = new QLogValueAxis();
axisY3->setTitleText("logarithmic scale");
axisY3->setBase(10.0);
axisY3->setLinePenColor(serieslog->pen().color());
chart->addAxis(axisY3, Qt::AlignRight);
serieslog->attachAxis(axisX);
serieslog->attachAxis(axisY3);
chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
// Create a layout and add Chart
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(chartView);
void WidgetHistogramm::setData(const std::vector<int> data)
{
if (data.size() <= 0)
{
LOG_DEBUG() << "Histogram Data empty";
return;
}
auto max = *max_element(std::begin(data), std::end(data));
QVector<QPointF> points(data.size());
for(std::vector<int>::size_type i = 0; i != data.size(); ++i) {
points[i] = QPointF(i, data[i]*100/max);
}
series->replace(points);
serieslog->replace(points);
chart->axisX(series)->setRange(0, data.size());
chart->axisY(series)->setRange(0, 100);
chart->axisX(serieslog)->setRange(0, data.size());
chart->axisY(serieslog)->setRange(-1000, 1);
}
The range does not refer to the scaled values, but to the actual values, for example in your case it should be from epsilon to 100. On the other hand the values that are shown in the logarithmic scale must be positive, in your case I see that there is zeros so a possible solution is to add an epsilon:
void WidgetHistogramm::setData(const std::vector<int> data)
{
if (data.size() <= 0){
LOG_DEBUG() << "Histogram Data empty";
return;
}
auto max = *max_element(std::begin(data), std::end(data));
QVector<QPointF> points(data.size());
for(std::vector<int>::size_type i = 0; i != data.size(); ++i) {
points[i] = QPointF(i, data[i]*100.0/max + std::numeric_limits<qreal>::epsilon());
}
series->replace(points);
serieslog->replace(points);
chart->axisX(series)->setRange(0, points.size());
chart->axisY(series)->setRange(0, 100);
chart->axisX(serieslog)->setRange(0, points.size());
chart->axisY(serieslog)->setRange( std::numeric_limits<qreal>::epsilon(), 100);
}

How do I set text within pie chart slices done with QPainter?

I am just starting out qt and have attempted to make a pie chart and I have the following code
QPainter painter(this);
QPen pen;
QRectF size;
pen.setColor(Qt::white);
pen.setWidth(0);
painter.setPen(pen);
if(this->height()>this->width()){
size = QRectF(5,5,this->width()-10, this->width()-10);
}
else{
size = QRectF(5,5,this->height()-10, this->height()-10);
}
double sum = 0.0, startAngle = 0.0;
double angle = 0.0, endAngle, percent;
for(auto i = 0; i < qvValues.size(); i++){
sum += qvValues[i];
}
for(auto i = 0; i < qvValues.size(); i++){
percent = qvValues[i] / sum;
angle = percent *360.0;
endAngle = startAngle + angle;
painter.setBrush(qvColors[i]);
painter.drawPie(size, startAngle*16, angle*16);
painter.drawText(size,"text");
startAngle = endAngle;
}
And am getting the following result
pie chart
My question is, how would one be able to use the drawText() method in the QPainter class to add text within each of the pie slices? Specifically would I need to get the coordinates of the slices pass them into drawText()? If so how would you access its x and y?

Qt Window incorrect size until user event

I'm creating a screen where users can add certain tiles to use in an editor, but when adding a tile the window does not correctly resize to fit the content. Except that when I drag the window or resize it even just a little then it snaps to the correct size immediately.
And when just dragging the window it snaps to the correct size.
I tried using resize(sizeHint()); which gave me an incorrect size and the following error, but the snapping to correct size still happens when resizing/dragging.
QWindowsWindow::setGeometry: Unable to set geometry 299x329+991+536 on QWidgetWindow/'TileSetterWindow'. Resulting geometry: 299x399+991+536 (frame: 8, 31, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 259x329, maximum size: 16777215x16777215).
I also tried using updateGeometry() and update(), but it didn't seem to do much if anything.
When setting the window to fixedSize it will immediately resize, but then the user cannot resize the window anymore. What am I doing wrong here and where do I start to solve it?
Edit
Minimal verifiable example and the .ui file.
selected_layout is of type Flowlayout
The flowlayout_placeholder_1 is only there because I can't place a flowlayout directly into the designer.
Edit2
Here is a minimal Visual Studio example. I use Visual Studio for Qt development. I tried creating a project in Qt Creator, but I didn't get that to work.
Edit3
Added a little video (80 KB).
Edit4
Here is the updated Visual Studio example. It has the new changes proposed by jpo38. It fixes the issue of the bad resizing. Though now trying to downsize the windows causes issues. They don't correctly fill up vertical space anymore if you try to reduce the horizontal space even though there is room for more rows.
Great MCVE, exactly what's needed to easily investigate the issue.
Looks like this FlowLayout class was not designed to have it's minimum size change on user action. Layout gets updated 'by chance' by QWidget kernel when the window is moved.
I could make it work smartly by modifying FlowLayout::minimumSize() behaviour, here are the changes I did:
Added QSize minSize; attribute to FlowLayout class
Modifed FlowLayout::minimumSize() to simply return this attribute
Added a third parameter QSize* pMinSize to doLayout function. This will be used to update this minSize attribute
Modified doLayout to save computed size to pMinSize parameter if specified
Had FlowLayout::setGeometry pass minSize attribute to doLayout and invalidate the layout if min size changed
The layout then behaves as expected.
int FlowLayout::heightForWidth(int width) const {
const int height = doLayout(QRect(0, 0, width, 0), true,NULL); // jpo38: set added parameter to NULL here
return height;
}
void FlowLayout::setGeometry(const QRect &rect) {
QLayout::setGeometry(rect);
// jpo38: update minSize from here, force layout to consider it if it changed
QSize oldSize = minSize;
doLayout(rect, false,&minSize);
if ( oldSize != minSize )
{
// force layout to consider new minimum size!
invalidate();
}
}
QSize FlowLayout::minimumSize() const {
// jpo38: Simply return computed min size
return minSize;
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly,QSize* pMinSize) const {
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
// jpo38: store max X
int maxX = 0;
for (auto&& item : itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
// jpo38: update max X based on current position
maxX = qMax( maxX, x + item->sizeHint().width() - rect.x() + left );
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
// jpo38: save height/width as max height/xidth in pMinSize is specified
int height = y + lineHeight - rect.y() + bottom;
if ( pMinSize )
{
pMinSize->setHeight( height );
pMinSize->setWidth( maxX );
}
return height;
}
I was having the same exact issue (albeit on PySide2 rather than C++).
#jpo38's answer above did not work directly, but it un-stuck me by giving me a new approach.
What worked was storing the last geometry, and using that geometry's width to calculate the minimum height.
Here is an untested C++ implementation based on the code in jpo38's answer (I don't code much in C++ so apologies in advance if some syntax is wrong):
int FlowLayout::heightForWidth(int width) const {
const int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
void FlowLayout::setGeometry(const QRect &rect) {
QLayout::setGeometry(rect);
// e-l: update lastSize from here
lastSize = rect.size();
doLayout(rect, false);
}
QSize FlowLayout::minimumSize() const {
// e-l: Call heightForWidth from here, my doLayout is doing things a bit differently with regards to margins, so might have to add or not add the margins here to the height
QSize size;
for (const QLayoutItem *item : qAsConst(itemList))
size = size.expandedTo(item->minimumSize());
const QMargins margins = contentsMargins();
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
size.setHeight(heightForWidth(qMax(lastSize.width(), size.width())));
return size;
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const {
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
for (auto&& item : itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
int height = y + lineHeight - rect.y() + bottom;
return height;
}

Qt, QTransform rotation

I'm trying to draw text "on cylinder". It means, that I have five rows of text. Top row is rotated by the X axis for 10 degrees. The second for 5 degrees. The middle is not rotated at all. The four's row is rotated for -5 degrees. The five's is rotated for -10 degrees.
Rows 1, 2, 3 draws OK, but something is wrong with 4,5 rows. What am I doing wrong ?
I provides an image for understanding a problem and code snippet:
for( int i = 0; i < iterationsCount; ++i )
{
const QRect r( x2, y2, textWidth, itemHeight );
const QString text = sections.at( section ).values.at( index );
int rsc = 0;
p->save();
rsc = widgetHeight / 2 - y;
p->setTransform(QTransform().rotate(rsc, Qt::XAxis));
if( type == Section::DaySectionShort ||
type == Section::DaySectionLong )
{
QStringList values = text.split( QLatin1Char( ' ' ) );
p->setPen(
lighterColor( opt.palette.color( QPalette::WindowText ), 75 ) );
p->drawText( r, Qt::AlignLeft | Qt::TextSingleLine, values.at( 0 ) );
p->setPen( opt.palette.color( QPalette::WindowText ) );
p->drawText( r, Qt::AlignLeft | Qt::TextSingleLine, values.at( 1 ) );
}
else
{
p->drawText( r, Qt::AlignLeft | Qt::TextSingleLine, text );
}
p->setTransform(QTransform().rotate(-rsc, Qt::XAxis));
index = nextIndex( index, sections.at( section ).values.size() );
y += itemHeight + itemTopMargin;
p->restore();
}
My problem
As you have not provided a minimal complete code that reproduces the problem, I cannot guess what is wrong there. But the most probable reason is incorrect rsc calculation. At least the following draft works:
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class MyWidget: public QWidget
{
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
QFont f = font();
f.setPointSize(15);
setFont(f);
}
protected:
void paintEvent(QPaintEvent *event) override
{
QWidget::paintEvent(event);
QPainter p(this);
const int itemHeight = fontMetrics().height();
const int itemTopMargin = 15;
const int xOffset = 15;
int y = itemHeight;
for (size_t i = 0; i < 5; ++i) {
// The angle is in range [-40, 40]. Remove " * 4" for [-10, 10].
const int rsc = (10 - 5 * i) * 4;
qDebug() << i << ":\t" << rsc << "\t" << y;
/*
Rotation must be performed relative to central point of the
drawn item. Transformations below are applied in reverse order.
At first translate item to make it's center in (0, 0). At
second rotate it relative to X axis. At third move the item to
desired position.
*/
QTransform transform;
transform.translate(xOffset, y + itemHeight / 2);
transform.rotate(rsc, Qt::XAxis);
transform.translate(0, - itemHeight / 2);
p.setTransform(transform);
p.drawText(QPoint(), QString("(Item no. %1)").arg(i + 1));
y += itemHeight + itemTopMargin;
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MyWidget widget;
widget.setMaximumSize(200, 250);
widget.show();
return app.exec();
}
The transformation used here is complicated because of need to rotate each item relative to it's central y, not y = 0. This also may be the case.
Font and angles are increased to see considered effects better.