QTableView table area selection control - self-drawn selection area

Original link: QTableView table area selection control - self-drawn selection area

A happy moment

End with clients home, gloom and saw my mother was mopping the floor, I took my mother into the hands of 200, said: Mom, give you pocket money, do not let my wife know.

My mother took the money, yelled: You are not they drinking?

I: Hush, how do you know?

Mom: You see, I am your wife, as well. This is where the 200 dollars, say! me……

Second, an overview

Recently we optimized a small function, mainly related to mimic the operation excel, I feel pretty good, so here were consolidated, shared with a friend in need. The main area selection today is talk about this feature, Qt comes with Grid control is a function of the selected area, but he is not beautiful, we can not support the adjustment on a custom border color and some details.

Today bloggers took off on their own terms is how to customize this area selection feature.

The main way used or drawn from, the following first look at the effect is not what you want.

Third, the results show

As shown below, it is a self-painted to show the effect of the selected area, in addition to the demo, there are other effects, but this article is not about to be content.

The focus of this article is about how to achieve regional selection box draw

Fourth, the realization of ideas

After seeing renderings, then begin to analyze how to draw the selection rectangle. Here in the form of questions to be analyzed, so that is more conducive to understanding.

Then the very first to think about the following questions

  1. How to determine the drawing area
  2. How to determine the borders drawn
  3. Who better to draw

Get to know the three questions above, then the main content of today's also almost the same.

1, the drawing area

The first step is to learn to see the Qt help documentation, have to say Qt help documentation that is doing quite well, very complete. That being the case then so what, directly open Qt 助手to see what signal follows several classes have to.

QTableView

//QAbstractItemView
void activated(const QModelIndex &index)
void clicked(const QModelIndex &index)
void doubleClicked(const QModelIndex &index)
void entered(const QModelIndex &index)
void iconSizeChanged(const QSize &size)
void pressed(const QModelIndex &index)
void viewportEntered()

QTableView Grid control is the base class, our form is also based on this control development. Look signal (which is his parent window signal) included in this class, for the three questions posed at the beginning of this summary does not seem particularly large role. So we continue to look down and see his data storage class.

QStandardItemModel

void itemChanged(QStandardItem *item)

//parent QAbstractItemModel

void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn)
void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void columnsInserted(const QModelIndex &parent, int first, int last)
void columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column)
void columnsRemoved(const QModelIndex &parent, int first, int last)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int> ())
void headerDataChanged(Qt::Orientation orientation, int first, int last)
void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex> (), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint)
void layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex> (), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint)
void modelAboutToBeReset()
void modelReset()
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
void rowsRemoved(const QModelIndex &parent, int first, int last)

Some signals QStandardItemModel is QTableView data model, and a sweep seems that the change of model data. This time M and V does not seem to find what we need, Qt will not really be so frustrated now. Of course the answer is "No", carefully read the Qt help documentation will find QAbstractItemView class can return a selectionModel, see its name seems to be what we need.

QItemSelectionModel * selectionModel() const

Continue to read with the help documentation, we get the following information

void currentChanged(const QModelIndex &current, const QModelIndex &previous)
void currentColumnChanged(const QModelIndex &current, const QModelIndex &previous)
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
void modelChanged(QAbstractItemModel *model)
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)

Ha ha ha, she found a signal that we need to see to know the name of the signal is triggered when the current item is changed, then we can go to the statistics which item is selected.

Here, we answer the first question, even if, we can count signal by selectionChanged selectionModel may need to draw the cell border.

//连接信号
connect(m_pVew->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ExcTableWidget::SelectionChanged);

2, draw a border

After the signal on connection start processing signals.

The idea is roughly this:

  1. Use gridCell recording all cells
  2. Loop through selected cells
  3. Determining which side of the current cell is needed to draw
  4. The result is stored in the structure gridPosints

Analyzing logic is relatively simple, relatively simple logic may look at the code directly. Here I give an example, such as the need to draw the left border, it is the need to see whether this cell has a cell to the left, or they have is the first column.

gridPosints是QMap<QModelIndex, QVector > Type memory cell index key value stored state four sides (need drawing)

void ExcTableWidget::SelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
    QModelIndexList indexs = m_pVew->selectionModel()->selectedIndexes();

    qDebug() << indexs;

    int row = GetModel()->rowCount();
    int column = GetModel()->columnCount();

    QVector<QVector<bool>> gridCell(row, QVector<bool>(column));

    for each (const QModelIndex & index in indexs)
    {
        gridCell[index.row()][index.column()] = true;
    }

    QMap<QModelIndex, DrawTypes> datas;
    QMap<QModelIndex, QVector<GridPoint>> gridPosints;
    for each (const QModelIndex & index in indexs)
    {
        DrawTypes types;
        bool topLine = true, rightLine = true, bottomLine = true, leftLine = true;
        if (index.row() == 0)
        {
            types |= TOP;
        }
        else
        {
            int aboveCell = index.row() - 1;
            if (gridCell[aboveCell][index.column()] == false)
            {
                types |= TOP;
            }
            else
            {
                topLine = false;
            }
        }

        if (index.column() == GetModel()->columnCount() - 1)
        {
            types |= RIGHT;
        }
        else
        {
            int rightCell = index.column() + 1;
            if (gridCell[index.row()][rightCell] == false)
            {
                types |= RIGHT;
            }
            else
            {
                rightLine = false;
            }
        }

        if (index.row() == GetModel()->rowCount() - 1)
        {
            types |= BOTTOM;
        }
        else
        {
            int beloveCell = index.row() + 1;
            if (gridCell[beloveCell][index.column()] == false)
            {
                types |= BOTTOM;
            }
            else
            {
                bottomLine = false;
            }
        }

        if (index.column() == 0)
        {
            types |= LEFT;
        }
        else
        {
            int leftCell = index.column() - 1;
            if (gridCell[index.row()][leftCell] == false)
            {
                types |= LEFT;
            }
            else
            {
                leftLine = false;
            }
        }

        datas[index] = types;

        gridPosints[index].push_back({ TOP, topLine });
        gridPosints[index].push_back({ RIGHT, rightLine });
        gridPosints[index].push_back({ BOTTOM, bottomLine });
        gridPosints[index].push_back({ LEFT, leftLine });
    }

    m_pVew->SetCellDatas(gridPosints);
    SelectStyle * style = m_pVew->GetDelegate();
    style->SetCellDatas(datas);

    m_pVew->update();
}

Here, we even answer the second question, we need to draw the borders of the cell finally calculated.

3. Draw

The data are plotted be far behind?

Then continue to look down, draw the logical mechanism provided by Qt is still very powerful drops, we can redraw the following ways

1, rewrite QStyledItemDelegate

QStyledItemDelegate agency is drawing most of the final drawing operations will be executed here, see parameter to know when each cell will be drawn here.

virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;

But there is a problem that regional issues of this function can be drawn only be drawn inside the cell, if plotted on the border will be overwritten, do not believe to see the following stack.

Drawing paint method QStyledItemDelegate agent is QTableView paintEvent the callback function.

Since when can not draw drawing drawn cell proxy item to cell go outside, just so we can fill the selected area here

void SelectStyle::DrawSelected(QPainter * painter, const QRect & rect, const QModelIndex & index) const
{
    if (m_indexs.contains(index) == false)
    {
        return;
    }

    painter->save();

    QPen pen = painter->pen();
    pen.setWidth(1);
    pen.setColor(m_color);
    painter->setPen(pen);

    painter->fillRect(rect, QColor(100, 0, 0, 100));

    painter->restore();
}

After filling the selected area, the next step is to select the drawing border region.

2, rewrite paintEvent
after reading function call stack, we all know QTableView should also clear is how to draw it. Since the draw agent can not complete the requirements, then we can only draw in paintEvent in the mountain.

Point to note here is that we need to try paintEvent QTableView itself to the original draw to go again, to ensure that the information on the screen is full, and then in the implementation of our own custom code.

As shown below, the paintEvent parent class after the implementation, we draw the border edges






Before selectionChanged signal selectionModel, we have to get the information you need to draw the cell border, you only need to draw the cache based on data drawn below, look at this code is very long, but the speed Leverage drop.

void FreezeTableView::paintEvent(QPaintEvent * event)
{
    QTableView::paintEvent(event);

    //绘制网格线
    QPainter painter(viewport());
    painter.save();
    QPen pen = painter.pen();
    pen.setWidth(1);
    pen.setColor(m_pSelectBorder->GetLineColor());
    painter.setPen(pen);

    for (auto iter = m_indexs.begin(); iter != m_indexs.end(); ++iter)
    {
        QModelIndex index = iter.key();
        QVector<GridPoint> cellTyeps = iter.value();
        QRect rect = visualRect(index);
        QRect tmpRect = rect;
        tmpRect.adjust(-1, -1, 1, 1);
        if (index.column() == 0)
        {
            tmpRect.adjust(1, 0, 0, 0);
        }
        if (index.row() == 0)
        { 
            tmpRect.adjust(0, 1, 0, 0);
        }

        for (int i = 0; i < cellTyeps.size(); ++i)
        {
            const GridPoint & point = cellTyeps.at(i);

            if (point.type == TOP && point.line)
            {
                painter.drawLine(tmpRect.topLeft(), tmpRect.topRight());
            }
            if (point.type == RIGHT && point.line)
            {
                painter.drawLine(tmpRect.topRight(), tmpRect.bottomRight());
            }
            if (point.type == BOTTOM && point.line)
            {
                painter.drawLine(tmpRect.bottomLeft(), tmpRect.bottomRight());
            }
            if (point.type == LEFT && point.line)
            {
                painter.drawLine(tmpRect.topLeft(), tmpRect.bottomLeft());
            }
        }
    }

    for (auto iter = m_indexsBorder.begin(); iter != m_indexsBorder.end(); ++iter)
    {
        QModelIndexList indexs = iter.key();
        for each (const QModelIndex & index in indexs)
        {
            QRect rect = visualRect(index);
            rect.adjust(-1, -1, 0, 0);
            if (index.column() == 0)
            {
                rect.adjust(1, 0, 0, 0);
            }
            if (index.row() == 0)
            {
                rect.adjust(0, 1, 0, 0);
            }
            painter.setPen(iter.value());
            painter.drawRect(rect);
        }
    }

    painter.restore();
}

With these core code, since the painting function selection area will be basically realized.

V. Related Articles

  1. Qt realize Grid control - Supports multi-level list head, multi-level row header, cell merging, font settings, etc.

  2. Qt imitation Excel spreadsheet component - Freeze support columns, freeze rows, content adaptation and merging cells

  3. Property Browser Controls QtTreePropertyBrowser compiled DLL (Designer plug-in)

  4. Super practical properties browser control --QtTreePropertyBrowser

  5. Qt form of ant control line

  6. QRowTable Grid control - Supports hover entire line, checked the entire row, column sorting and other specified

  7. QRowTable form controls (two) - red rose green fall


Worth seeing excellent article:

  1. Choi Associated Press - Products
  2. Glodon - Products
  3. Qt custom control list
  4. Niubi Hong Hong Qt library





If you think the article is good, it may give a reward , writing is not easy, thanks for your support. Your support is my greatest motivation, thank you! ! !














It is important - reprint statement

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


Guess you like

Origin www.cnblogs.com/swarmbees/p/11280122.html