aspnet校友录网站开发,d网站建设的目的,wordpress单页增加分页,wordpress 更换空间阿里云背景#xff1a;
我的最初应用场景#xff0c;就是要在表格上用进度条显示数据#xff0c;以及放一个按钮。
qt-creator中有自带的delegate示例可以参考#xff0c;但终归自己动手还是需要理解细节#xff0c;否则不能随心所欲。
自认没那个天赋#xff0c;于是记录下…背景
我的最初应用场景就是要在表格上用进度条显示数据以及放一个按钮。
qt-creator中有自带的delegate示例可以参考但终归自己动手还是需要理解细节否则不能随心所欲。
自认没那个天赋于是记录下来以便日后参考。
qt自带示例
打开qt-creator首页点击左侧示例搜索那里输入delegate就列出来几个常见的也就是spinbox输入数值。 上图中的第一个例子是个意外我觉得挺好玩的关于QItemEditorFactory和QItemEditorCreatorBase的用法可以设置item的编辑方式和代理有异曲同工之妙。也许本质上有某种联系但我没有深究本次只想记录代理的用法。
用法宗旨
按照资料以及网上的说法无非就是继承QStyledItemDelegate重写几个函数然后设置为QTablev/QTableWidget的代理即可。但关键是QStyledItemDelegate咋用。
我认为还是始终贯彻MVC模式的应用比如一个view绑定了一个model则model的数据是和view同步的。前后端分离的本质是view是躯壳model是灵魂。肉体感受可以影响灵魂灵魂变化也会反应到肉体。打住再分析就可以感悟人性了。
主要是继承QStyledItemDelegate之后重写那几个函数的意义。我认为常用的几个paintcreateEditoreditorEventsetEditorDatasetModelDataupdateEditorGeometry。看名字就知道凡是带editor的都是需要编辑view时才用。
paint函数
绘制代理区域如果希望表格一显示马上就要看到代理控件的时候用比如我要显示进度条或者按钮我希望界面一出来它就有。而不是编辑这个单元格的时候才出来。
至于paint调用的时机如果要代码控制就控制model好了view同步显示数据时会重绘界面。
延伸题外话
我试过其它方式修改数据然后总想别的方法来调用主动调用paint函数没戏的。比如this-repaint()不行的。除非把界面最小化再恢复或者点一下单元格。反正这个paint别想着自己控制它就用model的item间接更新就行了。所以都说tableview比widget优化显示速度之类的说法实际上是mvc内部优化的意思。
很早以前大约十多年了我用vs做了一个超大的电子表格因为单元格太多业务上又必须这样所以ui更新效率很低。最后也是让grid绑定了datatable然后通过更新datatable来间接更新grid才解决。其实所谓的mvc别说多先进早期vb6.0时代就有这样的雏形了。隐约记得那时候叫控件绑定数据源道理类似。
另外现在的扁平化风格有时候看起来特别别扭直接绘制出来的控件就是死的例如按钮看不到按下去的感觉。也许通过style能实现但我没尝试。
通常paint函数重写的内容主要是定义QStyleOption和drawControl。比如
void Debug_Delegate_Button::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const
{Q_UNUSED(index);QStyleOptionButton btnStyle;btnStyle.text exec;btnStyle.rect option.rect;btnStyle.state QStyle::State_Active;QPushButton btn;btn.setEnabled(m_bEnabled);btn.style()-drawControl(QStyle::CE_PushButton, btnStyle, painter, btn);
}
我亲测的感受是QStyleOption是用于定义显示效果的drawControl只是画出来。
上面的代码定义了一个按钮下面我写了个setEnable但我感觉意义不大主要看上面的Style。
createEditor函数
qt手册说是返回用于编辑item的控件指针。也就是编辑单元格时显示的控件实例需要在这个函数里new出来并return。亦即你得告诉代理要用什么控件来编辑。
我的理解是其它凡是带有editor入参的函数都依赖这个函数否则editor就是空指针会报错的。这一点手册里我没看到哪里有提到但亲测就是如此。
editorEvent函数
相当于一个eventFilter。被代理的那个区域如果发生事件从这里写响应。
比如我做了一个发送命令的界面 首先那个exec按钮是使用paint函数画上去的但就是死的一匹。然后使用editorEvent函数捕获了鼠标点击事件。cpp代码如下 void Debug_Delegate_Button::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const
{Q_UNUSED(index);QStyleOptionButton btnStyle;btnStyle.text exec;btnStyle.rect option.rect;btnStyle.state QStyle::State_Active;QPushButton btn;btn.setEnabled(m_bEnabled);btn.style()-drawControl(QStyle::CE_PushButton, btnStyle, painter, btn);
}
bool Debug_Delegate_Button::editorEvent(QEvent *event, QAbstractItemModel *model,const QStyleOptionViewItem option, const QModelIndex index)
{Q_UNUSED(model);QMouseEvent *mouseEvent static_castQMouseEvent *(event);if(option.rect.contains(mouseEvent-pos())){//Two signals will be send. Select one of them.//event-type() QEvent::MouseButtonPress//event-type() QEvent::MouseButtonReleaseif (event-type() QEvent::MouseButtonPress m_bEnabled){emit sigClicked(index.row());}}return true;
}当捕获鼠标点击事件时发送一个信号给外界外界再处理这个信号就知道是点击了哪一行的按钮。
setEditorData函数
还是要提MVC模式的应用比如一个view绑定了一个model则model的数据是和view同步的。当编辑某个view对应的item时如果使用了代理控件编辑它控件就叫editor用户操作editoreditor会把数据写入item。反之用户更新itemeditor也会同步更新。
所以这个函数就是通过item更新editor。因为有editor入参需要配合重写createEditor函数并返回editor。
setModelData函数
通过操作editor更新item。因为有editor入参需要配合重写createEditor函数并返回editor。
updateEditorGeometry函数
手册提到
Updates the geometry of the editor for the item with the given index, according to the rectangle specified in the option. If the item has an internal layout, the editor will be laid out accordingly. Note that the index contains information about the model being used.
我认为就是字面意思用于更新区域显示布局效果通常写一句editor-setGeometry(option.rect);即可。
官方实例spinbox
如果要通过编辑view来更新model也就是示例中spinbox用法则看一段官方实例
delegate.h
#ifndef DELEGATE_H
#define DELEGATE_H#include QStyledItemDelegate//! [0]
class SpinBoxDelegate : public QStyledItemDelegate
{Q_OBJECTpublic:SpinBoxDelegate(QObject *parent nullptr);QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem option,const QModelIndex index) const override;void setEditorData(QWidget *editor, const QModelIndex index) const override;void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex index) const override;void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem option,const QModelIndex index) const override;
};
//! [0]
delegate.cpp
#include delegate.h#include QSpinBox//! [0]
SpinBoxDelegate::SpinBoxDelegate(QObject *parent): QStyledItemDelegate(parent)
{
}
//! [0]//! [1]
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem /* option */,const QModelIndex /* index */) const
{QSpinBox *editor new QSpinBox(parent);editor-setFrame(false);editor-setMinimum(0);editor-setMaximum(100);return editor;
}
//! [1]//! [2]
void SpinBoxDelegate::setEditorData(QWidget *editor,const QModelIndex index) const
{int value index.model()-data(index, Qt::EditRole).toInt();QSpinBox *spinBox static_castQSpinBox*(editor);spinBox-setValue(value);
}
//! [2]//! [3]
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex index) const
{QSpinBox *spinBox static_castQSpinBox*(editor);spinBox-interpretText();int value spinBox-value();model-setData(index, value, Qt::EditRole);
}
//! [3]//! [4]
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem option,const QModelIndex /* index */) const
{editor-setGeometry(option.rect);
}
//! [4]
createEditor函数定义了用于编辑单元格的spinbox控件并返回editor控件指针因为其它函数用到。
setEditorData函数把model中的item数据更新到spinbox。
setModelData函数把spinbox的操作结果更新到model的item。
updateEditorGeometry函数负责更新显示区域。
实践
上面提到过放置button并可以响应点击的用法只用到了paint和editorEvent函数。下面再记录进度条的用法。
因为进度条用于显示而不是编辑需要界面一打开就能看到进度条所以需要paint函数。而进度条又不像按钮那样需要用户操作然后就没有然后了。
delegate_progressbar.h
#ifndef DELEGATE_PROGRESSBAR_H
#define DELEGATE_PROGRESSBAR_H#include QStyledItemDelegate
#include QProgressBarclass Delegate_ProgressBar : public QStyledItemDelegate
{Q_OBJECT
public:Delegate_ProgressBar(QObject *parent nullptr);enum EItemValue { eIV_Maxinum, eIV_Value };void paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const;
};#endif // DELEGATE_PROGRESSBAR_Hdelegate_progressbar.cpp
#include delegate_progressbar.hDelegate_ProgressBar::Delegate_ProgressBar(QObject *parent): QStyledItemDelegate(parent)
{}
void Delegate_ProgressBar::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const
{//这里控制显示效果QStyleOptionProgressBar pbarStyle;pbarStyle.rect option.rect;pbarStyle.state QStyle::State_Active;pbarStyle.maximum index.data(Qt::UserRole eIV_Maxinum).toInt();//设置进度条最大值pbarStyle.progress index.data(Qt::UserRole eIV_Value).toInt();//设置当前进度//这里把它绘制出来QProgressBar pbar;pbar.style()-drawControl(QStyle::CE_ProgressBar, pbarStyle, painter, pbar);
}mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include QMainWindow
#include QStandardItemModel
#include QTimer
#include delegate_progressbar.hnamespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent nullptr);~MainWindow();private slots:void on_pushButton_clicked();void onTimerOut();private:Ui::MainWindow *ui;QStandardItemModel *m_model nullptr;Delegate_ProgressBar *m_delegate nullptr;QTimer *m_timer nullptr;int m_i 0;
};#endif // MAINWINDOW_Hmainwindow.cpp
#include mainwindow.h
#include ui_mainwindow.h
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui-setupUi(this);m_model new QStandardItemModel(2, 3, this);ui-tableView-setModel(m_model);m_delegate new Delegate_ProgressBar;ui-tableView-setItemDelegateForColumn(2, m_delegate);QStandardItem *item new QStandardItem;item-setData(100, Qt::UserRole Delegate_ProgressBar::eIV_Maxinum);//这里给进度条设置最大值相当于setMaximum。m_model-setItem(0, 2, item);//这是model的另一种用法与直接操作item一样。
// QModelIndex index m_model-index(0, 2, QModelIndex());
// m_model-setData(index, QVariant(50));m_timer new QTimer(this);m_timer-setInterval(100);connect(m_timer, QTimer::timeout, this, MainWindow::onTimerOut);
}MainWindow::~MainWindow()
{delete ui;
}
void MainWindow::onTimerOut()
{m_model-item(0, 2)-setData(m_i, Qt::UserRole Delegate_ProgressBar::eIV_Value);//这里给进度条设置当前值相当于setValue。if (m_i 100){m_timer-stop();}
}
void MainWindow::on_pushButton_clicked()
{m_timer-start();
}再提MVC模式。这里用到的tableview当绑定model之后即使model没有任何item也可以设置行数和列数运行效果依然能看到表格有行有列但只能看不能操作。
给model添加item之后每个item对应一个单元格才能实现数据同步显示。
我这里需要单元格显示进度条而进度条本质是max和value两个int数据所以只要用代码控制响应item让它存储的数据发生变化则进度条就会响应变化了。
妥善起见显示进度条的单元格设置为readonly。
item是个好东西就像结构体一样可以按角色字段划分存储很多数据。而这些数据是QVariant类型也就是任意类型我还用它存过对象指针基本上可以无限扩展非常好用。
所以如果要显示进度条的那个单元格能保存max和value两个int数据非常简单setData时指定Qt:UserRole即可。比如
item-setData(100, Qt:UserRole);
item-setData(50, Qt:UserRole 1);
Qt:UserRole是枚举后面加几都可以那就根据需要定义成枚举见名知意比如
item-setData(100, Qt:UserRole eEnum_Max);
item-setData(50, Qt:UserRole eEnum Value);
就可以随便怎么玩了。
本文完。