PDA

View Full Version : Creating diagonal table headers



kubark42
8th September 2013, 12:58
Hi--

I'm trying to create diagonal table headers. So far, it's kind of working, but I'm running into what might be deep problems. My goal is to create a table that looks like this:

9541

So far, I have subclassed the header and added a custom delegate, getting me here:

9540

The diagonal and text elements are in the right spot, but they're being drawn on top of by other paint commands or are being stopped by the widgets boundaries.

Questions:
1) How can I expand the header boundaries so that my diagonal lines don't get truncated?
2) How can I draw the diagonal lines on top of the table, instead of having them be draw on top of?


diagonal_table.cpp


#include "diagonal_table.h"
#include <qnamespace.h>

#include <Qt>

DiagonalTable::DiagonalTable(QWidget *parent) : QTableView(parent)
{
DiagonalHeaderView *horizontalDiagonalHeader = new DiagonalHeaderView(Qt::Horizontal, this);
DiagonalHeaderView *verticalDiagonalHeader = new DiagonalHeaderView(Qt::Vertical, this);
setHorizontalHeader(horizontalDiagonalHeader);
setVerticalHeader(verticalDiagonalHeader);

setItemDelegate(new MyDelegate(this));
setShowGrid(false);

}

DiagonalHeaderView::DiagonalHeaderView(Qt::Orienta tion orientation, QWidget *parent) : QHeaderView(orientation, parent)
{

}

void DiagonalHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
if (!rect.isValid())
return;

QStyleOptionHeader opt;
initStyleOption(&opt);

opt.text = model()->headerData(logicalIndex, orientation(), Qt::DisplayRole).toString();


// the section position
int visual = visualIndex(logicalIndex);
Q_ASSERT(visual != -1);
if (count() == 1)
opt.position = QStyleOptionHeader::OnlyOneSection;
else if (visual == 0)
opt.position = QStyleOptionHeader::Beginning;
else if (visual == count() - 1)
opt.position = QStyleOptionHeader::End;
else
opt.position = QStyleOptionHeader::Middle;
opt.orientation = orientation();

// Get rotation, and wrap to (-180, 180]
double rotation = model()->headerData(logicalIndex, orientation(), DiagonalTable::RotationRole).toFloat();
while (rotation > 180)
rotation -=360;
while (rotation <= -180)
rotation +=360;

int separatorLength;
int sumLinePositionX;
int sumLinePositionY;
int textOffsetX;
int textOffsetY;
int textWidth = opt.fontMetrics.width(opt.text);
int textHeight = opt.fontMetrics.height();

switch (orientation()) {
case Qt::Horizontal:
{
// Cast parent so that we can use its methods
DiagonalTable * diagonalTable = dynamic_cast<DiagonalTable *>(this->parent());
if (diagonalTable == NULL)
Q_ASSERT(0);

// Calculate the rectangle for the box in which we will draw
int firstRow = model()->headerData(logicalIndex, orientation(),DiagonalTable::RowRole).toInt();

sumLinePositionX = rect.bottomLeft().x();
sumLinePositionY = rect.bottomLeft().y();

for (int i=0; i<firstRow; i++) {
int rowHeight = diagonalTable->rowHeight(i+1);
sumLinePositionY += rowHeight;
}

textOffsetX = rect.width()/2 + textHeight/2;
textOffsetY = 0;

}
break;
case Qt::Vertical:
{
if (rotation == 0) {
style()->drawControl(QStyle::CE_Header, &opt, painter, this);
} else {
// Cast parent so that we can use its methods
DiagonalTable * diagonalTable = dynamic_cast<DiagonalTable *>(this->parent());
if (diagonalTable == NULL)
Q_ASSERT(0);

// Calculate the rectangle for the box in which we will draw
int firstColumn = model()->headerData(logicalIndex, orientation(), DiagonalTable::ColumnRole).toInt();

sumLinePositionX = rect.bottomRight().x();
sumLinePositionY = rect.bottomRight().y();

for (int i=0; i<firstColumn; i++) {
int columnWidth = diagonalTable->columnWidth(i+1);
sumLinePositionX += columnWidth;
}

textOffsetX = 0;
textOffsetY = -rect.height()/2 + textHeight/2;
}
}
break;
}

// Paint the diagonal line
// Adjust for rotation
if (rotation > -90 && rotation < 90)
separatorLength = 30;
else
separatorLength = 90;
painter->setRenderHint(QPainter::Antialiasing);
painter->translate(sumLinePositionX, sumLinePositionY);
painter->rotate(rotation);
painter->drawLine(0, 0, separatorLength, 0);

// Paint text
painter->resetTransform();
// Adjust for rotation
if (rotation > -90 && rotation < 90) {
painter->translate(sumLinePositionX + textOffsetX, sumLinePositionY + textOffsetY);
painter->rotate(rotation);
painter->translate(2, 0); // Shift text up (along new axis)
} else {
painter->translate(sumLinePositionX + textOffsetX, sumLinePositionY + textOffsetY);
painter->rotate(rotation + 180); // Always draw text from left to right
painter->translate(-textWidth - 2, 0); // Back text up (along new axis)
}
painter->setFont(font()); // This font should be system dependent. Not sure how to get this.
painter->drawText(0, 0, opt.text);

}


void MyDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
int row = index.row();
int col = index.column();

int firstRow = index.model()->headerData(col, Qt::Horizontal,DiagonalTable::RowRole).toInt();
if (row < firstRow) {
} else {
QPen pen;
int shift = 18; // This makes no sense yet, but for whatever reason the colors are shifted when drawn in the table
pen.setColor(QColor(143+shift, 142+shift, 147+shift));
pen.setWidth(1);
painter->setPen( pen );

painter->drawRect( option.rect.x()-1, option.rect.y()-1, option.rect.width(), option.rect.height() );
QItemDelegate::paint( painter, option, index );
}

}

diagonal_table.h


#ifndef DIAGONALTABLE_H
#define DIAGONALTABLE_H
#include <QWidget>
#include <QHeaderView>
#include <QItemDelegate>
#include <QPainter>
#include <QTableView>

class DiagonalTable : public QTableView
{
Q_OBJECT

public:
enum {RotationRole = Qt::UserRole + 0,
RowRole = Qt::UserRole + 1,
ColumnRole = Qt::UserRole + 2};

public:
explicit DiagonalTable(QWidget *parent = 0);
~DiagonalTable();

};


class MyDelegate : public QItemDelegate {
public:
MyDelegate( QObject *parent ) : QItemDelegate( parent ) { }
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
};

class DiagonalHeaderView : public QHeaderView
{
Q_OBJECT

public:
DiagonalHeaderView(Qt::Orientation orientation, QWidget *parent = 0);
~DiagonalHeaderView();
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;

};

#endif // DIAGONALTABLE_H


mainwindow.cpp


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "diagonal_table.h"
#include <QStandardItemModel>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);


QStandardItemModel *sim = new QStandardItemModel(0,0,ui->tableView);
ui->tableView->setModel(sim);

QStringList horizontalHeaderLabels;
horizontalHeaderLabels << "Output channel" << "Channel type" << "Live view" << "Curve 1" << "Curve 2" << "Roll" << "Pitch" << "Yaw";
sim->setHorizontalHeaderLabels(horizontalHeaderLabels);

// Set horizontal header rotation
sim->setHeaderData(0, Qt::Horizontal, -150, DiagonalTable::RotationRole);
sim->setHeaderData(1, Qt::Horizontal, -150, DiagonalTable::RotationRole);
sim->setHeaderData(2, Qt::Horizontal, -150, DiagonalTable::RotationRole);
sim->setHeaderData(3, Qt::Horizontal, -45, DiagonalTable::RotationRole);
sim->setHeaderData(4, Qt::Horizontal, -45, DiagonalTable::RotationRole);
sim->setHeaderData(5, Qt::Horizontal, -45, DiagonalTable::RotationRole);
sim->setHeaderData(6, Qt::Horizontal, -45, DiagonalTable::RotationRole);
sim->setHeaderData(7, Qt::Horizontal, -45, DiagonalTable::RotationRole);

// Set row for horizontal header. All cella above this point will be empty
sim->setHeaderData(0, Qt::Horizontal, 1, DiagonalTable::RowRole);
sim->setHeaderData(1, Qt::Horizontal, 1, DiagonalTable::RowRole);
sim->setHeaderData(2, Qt::Horizontal, 1, DiagonalTable::RowRole);

QStringList verticalHeaderLabels;
verticalHeaderLabels << "Live view" << "Channel 1" << "Channel 2" << "Channel 3"
<< "Channel 4" << "Channel 5" << "Channel 6";
sim->setVerticalHeaderLabels(verticalHeaderLabels);

// Set vertical header rotation
sim->setHeaderData(0, Qt::Vertical, -150, DiagonalTable::RotationRole);

// Set column for vertical header. All cells to the left of this point will be empty
sim->setHeaderData(0, Qt::Vertical, 3, DiagonalTable::ColumnRole);

for (int j=0; j < sim->rowCount(); j++) {
for (int i=0; i < sim->columnCount(); i++) {
int firstRow = sim->headerData(i, Qt::Horizontal,DiagonalTable::RowRole).toInt();
if (j < firstRow)
continue;

sim->setItem(j,i,new QStandardItem("-1.00"));
}
}

ui->tableView->resizeColumnsToContents();
}

MainWindow::~MainWindow()
{
delete ui;
}

Rajesh.Rathod
11th September 2013, 13:19
See the below link which creates hierarchical headers, might be helpful to you.

http://qt-apps.org/content/show.php/HierarchicalHeaderView?content=103154