ディレクトリ
オリジナルリンク:QTableViewテーブル領域選択制御-自己描かれた選択領域
幸せな瞬間
自宅クライアント、暗がりで終了し、私の母は床を掃討して見て、私は200の手に私の母を取った、言った:ママは、あなたが私の妻に知らせていない、お金をポケット与えます。
私の母は、お金を取っ叫んだ:あなたは、彼らは飲んでいないですか?
I:ハッシュ、どのようにあなたが知っているのですか?
ママ:あなたが見る、私も、あなたの妻です。200ドル、と言うところです!私:ああ......
第二に、概要
最近、私たちが主に得意の動作を模倣するために関連した小さな機能を、最適化された、私はここで、連結を必要としている友人と共有して、かなり良い感じ。メインエリア選択は本日、Qtは、グリッドコントロールが付属して、この機能についての話である私たちは、カスタムの境界線の色といくつかの詳細の調整をサポートすることはできません、選択した領域の関数であるが、彼は美しいではありません。
ブロガーは自分の条件に離陸した今日では、この領域選択機能をカスタマイズする方法です。
主な方法で使用またはから引き出され、効果で次の第一印象は、あなたが望むものではありません。
第三に、結果は
以下に示すように、それは自己塗ら選択した領域の効果を示すためには、デモに加えて、他の効果がありますが、この記事では、コンテンツれようとしていません。
この記事の焦点は、地域選択ボックス描画を実現する方法についてです
第四に、アイデアの実現
レンダリングを見た後、その後、選択矩形を描画する方法を分析し始めます。ここでは、分析すべき質問の形で、そのためには、理解するために、より助長しています。
以下の質問について考えてその後、非常に最初の
- 描画領域を確認する方法
- 描かれた境界を確認する方法
- より良い誰描画します
上記の3つの質問を知って取得し、その後、今日の主な内容もほぼ同じです。
1、描画領域
最初のステップは、Qtのヘルプドキュメントを参照してくださいすることを学ぶことです、非常に完全な、非常によくやっているQtのヘルプドキュメントを言わなければなりません。それでは、直接開いた後、ケースをされていることをQt 助手
いくつかのクラスを以下のものを信号見るためには、する必要があります。
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グリッドコントロールは、フォームも、このコントロールの開発に基づいており、基底クラスです。信号を見て(彼の親ウィンドウ信号である)、この要約の冒頭で提起した3つの質問には、特に大きな役割を思われないために、このクラスに含まれています。だから我々はダウン見て、彼のデータ・ストレージ・クラスを見続けます。
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)
いくつかの信号QStandardItemModelはQTableViewデータモデルであり、かつスイープは、モデルデータの変更と思われます。MとVは、私たちが必要なものを見つけるように見えることはありません今回は、Qtは本当に今ではイライラすることはありません。もちろん、答えは慎重にQtのヘルプドキュメントを読んで、「いいえ」であるQAbstractItemViewクラスは、その名前が、我々は必要なものであるように思わ参照、selectionModelのを返すことができるでしょう。
QItemSelectionModel * selectionModel() const
ヘルプ・ドキュメントを読むことを続けて、私たちは、以下の情報を得ます
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
void currentColumnChanged(const QModelIndex ¤t, const QModelIndex &previous)
void currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous)
void modelChanged(QAbstractItemModel *model)
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
ハッハッハ、彼女はその後、我々はアイテムが選択されている統計に行くことができ、我々は、信号の名前が現在の項目が変更されたときにトリガーされる知って見る必要がある信号を発見しました。
ここで、我々は、セルの境界線を描画する必要があるかもしれませんselectionChanged selectionModelのことで信号を数えることができるとしても、場合、最初の質問に答えます。
//连接信号
connect(m_pVew->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ExcTableWidget::SelectionChanged);
2、境界線を描きます
接続上の信号の後の信号の処理を開始します。
アイデアは大体これです:
- すべてのセルを記録するグリッドセルを使用してください
- 選択したセルをループ
- 現在のセルの側が描画するのに必要とされているかを決定します
- 結果は構造体に格納されgridPosints
ロジックを分析することは比較的簡単である、比較的単純なロジックを直接コードを見てもよいです。ここで私は、このような左の境界線を描画する必要があるとして、例を挙げれば、このセルは左のセルを持っているかどうかを確認する必要がある、または彼らは最初の列である必要があります。
gridPosints是QMap <QModelIndex、QVector
>型メモリセルのインデックスキー値が格納されている状態四辺(図面が必要)
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();
}
ここでは、私たちも、我々は最終的に計算され、セルの境界線を描画する必要があり、2つ目の質問に答えます。
3.ドロー
データはこれまでの背後にあることがプロットされていますか?
その後、見下すのQtが提供する論理メカニズムはまだ非常に強力な滴で描画し続け、我々は次の方法を再描画することができます
1、書き換えQStyledItemDelegate
QStyledItemDelegate機関が、最終的な描画操作のほとんどを描いているが、ここで実行され、各セルは、ここで描画される際に知っているパラメータを参照してください。
virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
しかし、国境上にプロットされた場合には、この機能の地域問題は、セル内に描画されるだけで描画することができないという問題が上書きされますがあり、以下のスタックを見て信じていません。
paintメソッドQStyledItemDelegateエージェントを描画すると、QTableView paintEventコールバック関数です。
外に出セルに描画描かれたセルのプロキシアイテムを描画することができないときので、ちょうどので、ここで選択した領域を塗りつぶすことができます
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();
}
選択された領域を充填した後、次のステップは、描画境界領域を選択することです。
2、paintEventを書き換える
我々はすべてのQTableViewもそれを描画する方法を明確である必要があります知っている、関数呼び出しスタックを読んだ後。ドローエージェントは要求を完了することはできませんので、我々は唯一の山でpaintEventに描くことができます。
ここで注意すべき点は、私たちが、再び行くために、画面上の情報がいっぱいであることを保証するために、そして私たち自身のカスタムコードの実装では、元の引き分けにpaintEvent QTableView自分自身を試してみる必要があるということです。
以下に示すように、paintEventの親クラス実装した後、我々は境界エッジを描きます
selectionChanged信号selectionModelの前に、我々はあなたがセルの境界線を描画するために必要な情報を取得する必要が、あなただけの、以下の描かれたデータに基づいて、キャッシュを描き、このコードを見てする必要がありますすることは非常に長いですが、スピードレバレッジ低下。
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();
}
これらのコアコードと、塗装機能選択領域は、基本的に実現されるからです。
V.関連記事
QTグリッド制御を実現 - サポートするマルチレベルリストヘッド、マルチレベルの行ヘッダ、セルのマージ、フォント設定など
プロパティブラウザはQtTreePropertyBrowserコンパイルしたDLLをコントロールします(デザイナープラグイン)
優れた記事を見て価値があります:
あなたは記事が良いと思われる場合は、それが与えるかもしれない 報酬を 、書き込みは、あなたのサポートへの感謝は容易ではありません。あなたのサポートが私の最大の動機である、ありがとう!!!
それは重要である - 復刻声明
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。