QT中最常用的控件QPushButton(按钮)、QLineEdit(文本框)、QRadioButton(单选框)、QCheckBox(复选框)、QFrame(一般用作容器控件,配合布局)、QProgressBar(进度条控件)这些控件的使用方法都非常简单,查一下帮助文档就可以搞定,下面的章节中,我们会讲解另外的一些控件的常用但是却不是很容易找到的功能。
QVariant 类型
再讲解其他控件之前,我们需要先了解Qt中的QVariant类型,为什么呢,因为需要为控件绑定数据,就离不开对QVariant类型的了解,下面章节中我们要说到的一些控件,在绑定数据的时候就会使用QVariant类型。他除了可以包裹Qt中常见的QString,int等类型之外,还可以包裹自定义的类对象。该类型提供了一系列的构造函数以及转换函数来携带常见类型的数据,和转换到常见类型数据的方法:
QVariant(int val)
QVariant(uint val)
QVariant(qlonglong val)
QVariant(qulonglong val)
QVariant(bool val)
QVariant(double val)
QVariant(float val)
QVariant(const char * val)
QVariant(const QString & val)
QVariant(const QDate & val)
QVariant(const QTime & val)
QVariant(const QDateTime & val)
bool toBool() const
QByteArray toByteArray() const
QChar toChar() const
QDate toDate() const
QDateTime toDateTime() const
double toDouble(bool * ok = 0) const
float toFloat(bool * ok = 0) const
int toInt(bool * ok = 0) const
QJsonArray toJsonArray() const
qlonglong toLongLong(bool * ok = 0) const
QString toString() const
QTime toTime() const
uint toUInt(bool * ok = 0) const
qulonglong toULongLong(bool * ok = 0) const
这只是其中的一部分,其实还包括了一些画图相关的类型的封装,例如QPoint,QRect等,当然Qt提供的是使用频率很高的常见的类型,有时候我们需要绑定自己定义的类对象,例如实体类:
//设置
MyClass myclass;
QVariant courseModelVariant=QVariant::fromValue(myclass);
//获取
myclass = courseModelVariant.value<MyClass>();
这样我们就可以使用QVariant携带任意数据类型了
QComboBox控件
下拉列表框控件最常见的功能需求就是为该控件添加下拉项目,并且为每个下拉项目添加对应的自定义隐藏数据,例如在下拉列表中每一项上面显示的文字描述是给用户看的,然而在程序中,我们可能需要该项目对应的隐藏数据,例如ID甚至是自定义的对象。
QComboBox类使用QComboBox::addItem(const QString &atext, const QVariant &auserData)成员函数为下拉列表添加项目,第一个参数text表示显示在下拉项中的文字,而第二个参数我们可以利用来为该项绑定自定义的数据,其类型为QVariant类型。我们可以通过QVariant类型方便的为该下拉项关联任意自定义的数据类型。
在获取数据的时候,通过QComboBox:: currentData(int role = Qt::UserRole)函数获取当前选中下拉项关联的QVariant类型的数据,也可以通过QComboBox:: itemData(int index, int role = Qt::UserRole)获取指定下拉项的关联数据。通过currentText()、itemText(int index)可以获取下拉项上显示的文本。
QTableWidget控件
QTableWidget是Qt中的表格显示控件,与C#中的Grid、GridView类似,主要是用来绑定数据。在UI设计界面中选中该控件之后可以在属性栏对控件的属性进行设置,最常用的属性有如下:
focusPolicy 焦点策略,如果设置为NoFocus可以去掉单击时候现实的单元格的虚线框
contextMenuPolicy 可以设置右键菜单
frameShape 设置外边框,一般设置为NoFrame去掉边框
editTriggers触发单元格的编辑状态,值NoEditTriggers表示不触发编辑状态
selectionMode选择模式,值ExtendedSelection表示多选
selectionBehavior选择行为,值SelectRows按行选择
showGrid是否显示网格线
rowCount行数
columnCount列数
horizontalHeaderVisible是否显示水平表头
verticalHeaderVIsible是否显示垂直表头
verticalScrollBarPolicy设置垂直滚动条策略
horizontalScrollBarPolicy设置水平滚动条策略
另外的一些比较实用的功能代码:
在单元格中添加控件:
QComboBox *comBox = new QComboBox();
comBox->addItem("F");
comBox->addItem("M");
ui->qtablewidget->setCellWidget(0,3,comBox);//这里不是setItem而是setCellWidget
为单元格添加checkBox:
QTableWidgetItem *item = new QTableWidgetItem();
//设置item的check状态的时候,item会自动变成QCheckBox的样子,
//不必通过setCellWidget专门插入QCheckBox控件
//通过item->checkState()可以获取该item是否勾选
item->setCheckState(Qt::Unchecked);
ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);
单元格中显示字符串:
QTableWidgetItem *item = new QTableWidgetItem(QString("xx"));
ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);
设置单元格关联的自定义数据:
QTableWidgetItem *item = new QTableWidgetItem(QString(""));
QVariant courseModelVariant=QVariant::fromValue(MyClass("xx"));
item->setData(USER_DEFINE_ROLE,courseModelVariant);
this->ui->tableWidgetCourseList->setItem(rowIndex, columnIndex, item);
获取单元格关联的自定义数据:
QTableWidgetItem * item = this->ui->tableWidgetCourseList->item(row,col);
Myclass model = item->data(USER_DEFINE_ROLE).value<MyClass>();
设置单元格中的文本对齐方式:
ui->tableWidgetCourseList->item(rowIndex, columnIndex)->setTextAlignment(Qt::AlignCenter);
通过x,y坐标获取所在的item对象:
QModelIndex index = ui->tableWidgetCourseList->indexAt(QPoint(x,y));
int row = index.row();
int col = index.column();
QTableWidgetItem * item = ui->tableWidgetCourseList->item(row,col);
设置表头的列宽:
ui->tableWidgetCourseList->horizontalHeader()->resizeSection(colIndex,20);//宽20
设置列宽自适应:
ui->tableWidgetCourseList->horizontalHeader()->setSectionResizeMode(colIndex,QHeaderView::Stretch);
初始化表头文本:
QStringList headerText;
headerText.append("列1");
headerText.append("列2");
headerText.append("列3");
ui->tableWidgetCourseList->setHorizontalHeaderLabels(headerText);
为表头添加复选框按钮:
在表头上添加复选框不能通过在表头单元格中添加QCheckBox的方式实现,必须进行重绘,下面的代码是我们自定义的表头类
myqheaderview.h的内容:
//该类实现自定义的表头,主要是为了在表头中加入CheckBox控件
class MyQHeaderView : public QHeaderView
{
Q_OBJECT
public:
explicit MyQHeaderView(Qt::Orientation orientation, QWidget *parent = 0);
void setChecked(bool checked);
signals:
void headCheckBoxToggled(bool checked);
protected:
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
void mousePressEvent(QMouseEvent *event);
private:
QRect checkBoxRect(const QRect &sourceRect) const;
bool m_isOn;
};
myqheadview.cpp的内容:
MyQHeaderView::MyQHeaderView(Qt::Orientation orientation, QWidget *parent)
: QHeaderView(orientation, parent)
, m_isOn(false)
{
// set clickable by default
setChecked(false);
}
void MyQHeaderView::setChecked(bool checked)
{
if (isEnabled() && m_isOn != checked)
{
m_isOn = checked;
updateSection(0);
emit headCheckBoxToggled(m_isOn);
}
}
void MyQHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
painter->save();
QHeaderView::paintSection(painter, rect, logicalIndex);
painter->restore();
if (logicalIndex == 0)
{
QStyleOptionButton option;
if (isEnabled())
option.state |= QStyle::State_Enabled;
option.rect = checkBoxRect(rect);
if (m_isOn)
option.state |= QStyle::State_On;
else
option.state |= QStyle::State_Off;
style()->drawControl(QStyle::CE_CheckBox, &option, painter);
}
}
void MyQHeaderView::mousePressEvent(QMouseEvent *event)
{
if (isEnabled() && logicalIndexAt(event->pos()) == 0)
{
m_isOn = !m_isOn;
updateSection(0);
emit headCheckBoxToggled(m_isOn);
}
else QHeaderView::mousePressEvent(event);
}
QRect MyQHeaderView::checkBoxRect(const QRect &sourceRect) const
{
QStyleOptionButton checkBoxStyleOption;
QRect checkBoxRect = style()->subElementRect(QStyle::SE_CheckBoxIndicator,
&checkBoxStyleOption);
QPoint checkBoxPoint(sourceRect.x()+5,
sourceRect.y() +
sourceRect.height() / 2 -
checkBoxRect.height() / 2);
return QRect(checkBoxPoint, checkBoxRect.size());
}
使用自定义表头:
MyQHeaderView*myHeader=new MyQHeaderView(Qt::Horizontal, ui->tableWidgetCourseList);
ui->tableWidgetCourseList->setHorizontalHeader(myHeader);
为QTableWidget添加一行数据实际上是根据行数和列数,循环QTableWidget的所有单元格,对每个单元格item设置数据来实现的。
QTabWidget控件
该控件类就是一个选项卡控件,有多个tab页,下面是一些实用的方法:
切换到tab:
ui->tabWidgetExportEdit->setCurrentIndex(tabIndex);
移除选项卡:
ui->tabWidgetExportEdit->removeTab(tabIndex);
关于选项卡控件的操作不多,重要的是怎么美化控件的显示,QSS将会作为单独的一篇文章来讲解如何美化Qt中的各种控件。
QWebview控件
该控件是用于在Qt中显示网页的控件,一般而言会将contextMenuPolicy属性设置为NoContextMenu隐藏系统为其提供的默认右键菜单
<1>. 加载网页:
ui->webViewCut->load(QUrl("http://www.baidu.com"));
//如果是本地网页,必须使用file:///的前缀作为网页地址
ui->webViewCut->load(QUrl("file:///c:/test.html "));
<2>. Qt代码中调用QWebview加载的网页中的js函数:
/先作如下设置
ui->webViewCut->page()->setForwardUnsupportedContent(true);
ui->webViewCut->page()->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
ui->webViewCut->page()->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
ui->webViewCut->page()->settings()->setAttribute(QWebSettings::JavaEnabled, true);
ui->webViewCut->page()->settings()->setAttribute(QWebSettings::AutoLoadImages, true);
//然后在QWebview的loadFinished槽函数中调用js,该槽函数表示网页已经加载完毕
QString js = QString("alert(\'hello Qt!\')");
ui->webViewCut->page()->mainFrame()->evaluateJavaScript(js);
<3>. 在QWebview加载的html的js代码中调用Qt的函数:
默认情况下在QwebViewCut中的网页里面的js不能直接调用Qt中的相关功能,这涉及到安全性问题。要满足js中调用Qt的功能必须满足下面的条件:
在Qt中暴露一个对象给js,然后js就可以在网页中直接使用这个对象以及该对象的[特定]函数,要求是被暴露Qt对象必须继承自QObject类,并且在js中调用这个暴露的对象的成员函数的定义是有要求的,该对象的满足下面的要求的成员函数都可以直接被js调用:
1.必须是该对象的公共函数,并且在函数声明前面添加Q_INVOKABLE修饰,例如:
public :
Q_INVOKABLE int TestQt();
2.如果该函数被声明成一个public slot 也可以不添加Q_INVOKABLE修饰:
public slots:
void TestQt();
个人认为第一种方法更好,因为可以设置返回值,而Qt的槽函数是没有返回值的,都是返回void,只需要调用this->ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject(“QtObj”, this); 就可以将一个Qt对象,也就是这里传递的this代表的对象,当然也可以直接传递其他对象指针,暴露给网页中的javascript,网页中的javascript在调用的时候可以直接使用 QtObj 去引用我们的Qt对象,以及通过QtObj去直接调用符合条件的Qt对象的成员函数。
那么this->ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject(“QtObj”, this);代码在什么时候执行呢? 推荐是在QWebFrame的信号javaScriptWindowObjectCleared发出的时候执行,所以我们可以在当前UI界面类的构造函数中添加下面的代码:
onnect(ui->webViewCut->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
this, SLOT(populateJavaScriptWindowObject()));
然后在处理javaScriptWindowObjectCleared()信号的槽函数中实现上述暴露功能:
void MainWindow::populateJavaScriptWindowObject()
{
ui->webViewCut->page()->mainFrame()->addToJavaScriptWindowObject("QtObj", this);
}
根据Qt文档上对该信号的描述javaScriptWindowObjectCleared()这个信号会在我们调用QwebViewCut::load()加载新的url之前就触发,我们在这个时候去处理这个信号,将我们需要暴露的Qt对象暴露给即将载入的网页
<4>. 将Qt的属性暴露出去供js调用,使用如下方法:
Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)
将上面的语句加入到类的声明中,在private块下面就可以,最后不需要以分号结尾,例如:
private:
Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)
这一行的作用是将属性 Qtvalue 注册到Qt的元对象系统中,在js中可以通过名字Qtvalue来访问该属性,但在js中访问该属性的时候假设Qt暴露给js的对象为QtObj,那么在js中可以这样访问该属性:
QtObj.Qtvalue = 10; //设置该属性的时候会调用void setTestValue(int)
alert(QtObj.Qtvalue) //获取该属性的时候会调用 int testValue()
Q_PROPERTY(int Qtvalue READ testValue WRITE setTestValue)的结构如下:
Q_PROPERTY( 类型 属性名 READ 返回属性值的函数 WRITE 设置属性值的函数 )
int Qtvalue int testValue() void setTestValue(int)
也就是说在js中我们可以直接使用Qtvalue,当获取Qtvalue的值的时候会自动调用暴露对象的 int testValue() 函数 ,Qt规定其返回值必须与Q_PROPERTY语句中指定的类型相同,并且必须没有参数。当我们为Qtvalue设置值的时候会调用暴露对象的void setTestValue(int)函数,该函数必须有一个int类型的参数(类型也必须与前面Q_PROPERTY语句中指定的类型相同),并且不能有返回值。
经过实验int testValue()与void setTestValue(int)函数的声明在private区域也可以,好像无所谓。其实这两个函数的名字是可以随意定的,对js暴露的属性名是Qtvalue,当访问Qtvalue属性的时候,会自动调用Q_PROPERTY声明中READ后面指定的函数去获取值,并且调用WRITE后面指定的函数去设置值,而不在乎这两个函数的名字。
另外这两个函数获取的值或者设置的值从哪里得来呢,我们可以在Qt对象中定义一个私有变量来保存这个值,而这个私有变量的名字是无所谓的,甚至如果需要的话,我们也不必保存这个值,直接在函数testValue里面返回一个常量值,也就是说是否应该定义一个私有变量来保存Qtvalue相关联的属性值,这个也不是必须的。
更多Qt QWidget与js的交互可以在Qt文档中搜索 The Qt WebKit Bridge关键字,其实Q_PROPERTY并不是专用于暴露属性给js的,Q_PROPERTY是Qt元对象系统的一部分。
<5>. 如果在QWebview加载的网页中有Flex应用程序,并且Qt中调用该QWebview加载的网页中的js函数中需要调用flex程序暴露给js的接口,那么还需要作如下设置:
在"%appdata%\Macromedia\Flash Player#Security\FlashPlayerTrust"路径下新建xxx.cfg文件,将当前flex应用程序所在位置(也就是swf文件所在的目录)填写到该文件中即可,该xxx.cfg的名字是无所谓的,随便什么名字,在xxx.cfg文件中指定的目录路径中的swf文件的运行是被信任的。xxx.cfg文件中可以指定多个目录,每行一个。实际上%appdata%\Macromedia\Flash Player#Security\FlashPlayerTrust\路径下也可以有多个文件名不同的cfg文件。xxx.cfg文件中指定的目录实际上可以直接指定为根目录,例如swf文件的路径是F:/xxx/yyy/zzz/test.swf,那么我们新建的xxx.cfg中的内容的第一行可以直接指定为F:/即可。
其实FlexBuilder在建立项目的时候,其生成的swf所在的目录都被添加到了%appdata%\Macromedia\Flash Player#Security\FlashPlayerTrust\下面的flashbuilder.cfg中了,所以使用FlexBuilder调试项目的时候,运行的swf都是被信任的。