QT_学习笔记

来源:互联网 发布:js date yyyymmdd 编辑:程序博客网 时间:2024/06/03 18:18

1.       在堆上用new分配的内存,为什么没有用delete释放?

从HelloQt说起

#include <qapplication.h>
#include <qlabel.h>
int main( int argc, char **argv )
{
    QApplication app( argc, argv );
   QLabel *label=new QLabel(“Hello QT!”,0);
    app.setMainWidget( label );
    label->show();
    return a.exec();
}

这里label是new出来的一个指针变量,也就是在堆上申请的内存,在C++语言里,明确过,在堆上分配的内存是必须认为的用delete释放的,防止内存泄露。这里为什么没有呢。后来看到有的QT程序也有明确使用delete释放内存的。这里越发不解了。后来,查到原来是QT扩展了C++,如果指针对象有父对象时,可以不用显式释放内存。我们需要显式释放的是那些没有父对象的孤立的指针。但上面这个例子并没有继承父对象,注意第二个指针,指向父对象的指针参数为0,即没有父对象,但是setMainWidget把它设为了主窗口部件,它就和程序同步了,当程序退出的时候自动释放,孩子对象的释放也是如此,在QT中扩展了C++ ,如果父亲对象无效时,子对象自动释放分配的内存。

2.        关于前置声明的说明

前置声明是C++ 的一个特性,不过我是在学QT时才接触到。这里有一些说明。

         #include <qapplication.h>

     class QCheckBox;

     class myClass:public QDialog

         {

                   Q_OBJECT

          public:

                         --------

          signals:

---------

          private:

                   QCheckBox *myCheck;

};

这里定义了一个示例类,继承于QDialog,其中用Q_OBJECT宏是因为声明的类定义了自己的signals,其中第二行就是一种前置声明,因为私有成员里用到了此类,这种方法比#include <qcheckbox.h>编译速度要快,注意私有成员为一个指针,如果是个私有对象而不是指针的话就不能用这种前置声明。具体的理由可以参考C++关于前置声明的说明,这里只是一个提醒

 

 以上两点参考: 1,2两点源文引用,感谢原作者

 

3.   控件内存释放

   Qt中,如果申明了两个数组:

   QLabel        *label[8];
   QLabel        *sLabel[8];

   这两个都是QLabel类型的指针数组,希望在以后的使用中动态的分配其内存;
   那么就应该:
 
   在class MyLabel中...

   for(int i=0;i<8;i++)
   {
       label[i] = new QLabel("This is a Label",this,0);
       //申请空间.   
       sLabel[i] = new QLabel("",label[i],0);
       //第二个Label指针数组的父对象为上面申请过空间的Label.
   }

   QLabel的构造函数的原形为:

   QLabel ( const QString & text, QWidget * parent, const char * name = 0, WFlags f = 0 )

   text:       Label上要显示的字符串;
   parent:     Label的父对象的指针;
   name:       Label的名字;
   f:          Label的Flags;

   从上面的代码容易得知label数组每个元素是以当前MyLabel类对象为父对象的;而sLabel里的元素是以对应的label为父对象的;


  在MyLabel的析构函数中就有相应的释放空间的代码:
 
 1   for(int i=0;i<8;i++)
 2   {
 3       delete label[i];
 4       //delete sLabel[i];
 5   }

  这里要是不注释掉上面第4行的语句就会出现段错误,原因经过查找资料得知在Qt中如果在创建组件对象时设置了父对象时,由父对象负责释放其子对象的空间.所以上面第2行已经释放了label的空间,而其子对象的sLabel也随之被释放,所以如果第三行再进行释放就会出现之前所说的错误.

  Qt虽然提供了这种机制,自动回收部分的内存,但是自己写程序的时候还是要小心,不要造成内存泄漏,这对于嵌入式的应用来说更加是件重要的事.毕竟现在的嵌入式硬件还是内存较小,而且要求程序可以长期稳定,高效节能的运行.

 

第三点参考: 原文链接,感谢原作者

 

4. 关于QLineEdit输入为ip格式时的验证和错误提示

       (1).输入时需要验证是否符合ip的输入格式

        QRegExp regx("^(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)\\.(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)\\.(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)\\.(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)");        QValidator *validator = new QRegExpValidator(regx,displays_ledit[i]);//输入验证        displays_ledit[i]->setValidator(validator);

        (2).输入完后,点击保存或类似操作时,需要对输入的信息进行检查,因为如果没有输入完,或者其他

bool ConfigScreenUI::checkIP(){bool flag = true;//检测ip地址格式QRegExp rx2("^(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)\\.(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)\\.(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)\\.(2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d)");for (int i = 0; i < 10; i++){if ( displays_ledit[i]->text() == "")//可以允许不输入,这里看自己需求continue;else if ( !rx2.exactMatch(displays_ledit[i]->text().trimmed()) ) //判断ip是否符合表达式{warning_lab[i]->setText("IP格式错误");flag = false;}else{if(warning_lab[i]->text() == "IP格式错误"|| displays_ledit[i]->text() == "")warning_lab[i]->setText("");}}if (flag == false)return flag;//检测ip冲突,如果需要输入很多个ip(通过多个QLineEdit),检测是否重复,可以通过下面的方法QMap<QString, int> map;QMap<QString, int>::iterator i_IP;map.clear();for (int i = 0; i < 10; i++){i_IP = map.find( displays_ledit[i]->text().trimmed() );if ( i_IP == map.constEnd() ){if (warning_lab[i]->text() != "")map.insert(displays_ledit[i]->text().trimmed(), i+1);//没有找到就插入数据if(warning_lab[i]->text() == "IP地址冲突" || displays_ledit[i]->text() == "")warning_lab[i]->setText("");}else{warning_lab[i]->setText("IP地址冲突");warning_lab[i_IP.value()-1]->setText("IP地址冲突");return false;}}return true;}


 

5. 简单对话框输出信

QMessageBox::information(this, "标题提示文字","内容提示文字");

 

 6.信号量机制

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method,Qt::ConnectionType type = Qt::AutoConnection ) [static]

 

sender:发送方,表示是谁发出的这个信号(这个就是signals)

signal:发送的信号类型,发送的那种信号,这个信号肯定是从属于sender类对象的

receiver:接收方,表示这个信号应该由谁来接收

method:接收信号后要执行的方法(这个就是slots),这个槽也是从属于receiver的

 

(1) 在类中定义一个槽slots,来使用已经定义好的信号

例如:实现,当点击按钮时,弹出一个框,上面说"恭喜你打开了我"

//类中声明

class Say

{

protected slots:
 void sayWordSlot();    // 为一个按钮定义一个槽

}

//类外定义

void Say::sayWordSlot()

{

        QMessageBox::information(this, "这是Say类里面的","恭喜你打开了我");

}

 

//类外使用

QToolButton *say_btn = new QToolButton(this);

connect(say_btn, SIGNAL(clicked()), this, SLOT(sayWordSlot() ) );//当点击按钮,就触发本页面的sayWordSlot方法

 

(2)自定义处理信号和处理方法

class Number

{

public :

            int GetNumber();

private:

            int number;

protected slots:
              void changeNumberSlot();    // 为一个按钮定义一个槽

signals:
              void numberChangeSignal(); //自定义一个信号

};

//类外定义

int Number::GetNumber()

{

               return number;

}

 

void Number::changeNumberSlot()

{

        number = 0;  //只要大于100就置为0,随便确定的需求

}

 

Number *num = new Number();

connect(num, SIGNAL( numberChangeSignal()), num,  changeNumberSlot() );

 

//测试触发

if (num->GetNumber() > 100)

    emit  numberChangeSignal();

 

小帮助:

emit的理解参考: http://www.qkevin.com/archives/89

 

 7. 关于控制位置的几个基本方法的理解

  (1). voidmove ( int x, int y )

如果是窗体,(x,y)  坐标参数是屏幕坐标
如果是控件,(x,y)  坐标参数是窗体内坐标
这个方法,可以用于所有QWidget的移动,但是如果它属于某一个部件,那么它的位置就会有限制,不能移出它的控件位置

    (2).

void QWidget::setContentsMargins ( int left, int top, int right, int bottom )

left,top,right,bottom : 表示上下左右的间隔,这个方法使用的前提,一般为:界面大小,位置都固定,不会变动,可以考虑用,如果是自适应的,最好用设置比例的方法来控制位置

 

 8. 用于绘制的两个常用方法:

      void paintEvent(QPaintEvent *);//这个方法在对象创建,update···等,会被调用,可以用于一些划线,画矩形框等
      void showEvent(QShowEvent *); //这个在显示时候就会被调用,可以用于界面初始数据的提取时,在此方法中初始化一些数据

      //每一个QWidget都有这个方法

例如:

void A::paintEvent(QPaintEvent *)//A表示一个类
{
 if(this == NULL)
  return;

 QPainter painter(this);

 painter.setPen(QColor("#bfbfbf"));
 painter.setBrush(QBrush(QColor("#e6e6e6")));

 painter.drawRect(51, 32, 1455, 817);//画矩形框
}

 

9. 字体的设置

QFont ftt("微软雅黑", 12);

QLabel *m_lab = new QLabel();

m_lab->setFont(ftt);

//小提示:

m_lab->setStyleSheet("QLabel{color:red}");//也可以用qssl来实现

额外:

int m_count = 4;

QString q_str = QString("共 <font color = #bf0000>%1</font> 张床").arg(m_count);

m_lab->setText(q_str);

 

 10. 自定义按钮实现(点击是出现下划线)

(1).下划线按钮 

定义

//Underline.h#ifndef UNDERLINEBTN_H#define UNDERLINEBTN_H#include <QWidget>#include <QToolButton>#include <QPainter>#include <QPixmap>#include "ui_UnderlineBtn.h"class UnderlineBtn : public QToolButton{Q_OBJECTpublic:UnderlineBtn(QString text, int width, int hight, int font_size, QWidget *parent = 0);~UnderlineBtn();void changeState();private:Ui::UnderlineBtn ui;QString m_text;QString m_text_color;bool m_is_selected;int m_width;int m_hight;int m_font_size;private:void paintEvent(QPaintEvent *);};#endif // UNDERLINEBTN_H

实现

//Underline.cpp#include "UnderlineBtn.h"UnderlineBtn::UnderlineBtn(QString text, int width, int hight,int font_size, QWidget *parent): QToolButton(parent){ui.setupUi(this);setCursor(QCursor(Qt::PointingHandCursor));//鼠标移入时变手形//设置用户定义的属性m_text = text;m_width = width;m_font_size = font_size;m_hight = hight;setFixedSize(m_width,m_hight);m_is_selected = false;setEnabled(true);m_text_color = "#0e0e0e";}UnderlineBtn::~UnderlineBtn(){}void UnderlineBtn::paintEvent(QPaintEvent *){if(this == NULL)return;QPainter painter(this);painter.setFont(QFont("微软雅黑", m_font_size));painter.setPen(QColor(m_text_color));//设置文字颜色painter.drawText(0, 0, m_width, m_hight, Qt::AlignLeft, m_text);//绘制文字if(m_is_selected){painter.drawLine(0,m_hight-5,m_width,m_hight-5);//在文字的下方画一条线}}//改变状态void UnderlineBtn::changeState(){if(m_is_selected){m_is_selected = false;setEnabled(true);m_text_color = "#0000ff";update();}else{m_is_selected = true;setEnabled(false);m_text_color = "#0e0e0e";update();}}


  (2). 画图按钮

 定义

 

//PictureBtn.h#ifndef PICTUREBTN_H#define PICTUREBTN_H#include <QToolButton>#include "ui_PictureBtn.h"class PictureBtn : public QToolButton{Q_OBJECTpublic:PictureBtn(QString  picture_normal, QString picture_selected, QString file_dir, QString text, QWidget *parent = 0);~PictureBtn();void changeState();private:Ui::PictureBtn ui;QString m_normal_pic;QString m_picture_dir;QString m_icon_dir;QString m_text;QString m_text_color;bool m_is_selected;private:void paintEvent(QPaintEvent *);void enterEvent(QEvent *);void leaveEvent(QEvent *);};#endif // PICTUREBTN_H


实现

//PictureBtn.cpp#include "PictureBtn.h"#include <qpainter.h>#include <qpixmap.h>PictureBtn::PictureBtn(QString  picture_normal, QString picture_selected, QString file_dir, QString text,  QWidget *parent): QToolButton(parent){ui.setupUi(this);m_normal_pic = picture_normal;m_picture_dir = picture_selected;m_icon_dir = file_dir;m_text = text;setCursor(QCursor(Qt::PointingHandCursor));//鼠标移入时变手形QPixmap pic(picture_selected);setFixedSize(pic.size());m_is_selected = false;setEnabled(true);m_text_color = "#0e0e0e";}PictureBtn::~PictureBtn(){}void PictureBtn::paintEvent(QPaintEvent *){if(this == NULL)return;QPainter painter(this);QPixmap background;background.load(m_picture_dir);int x = 0, y = 0, w = 0, h =  background.height();if(m_is_selected){painter.drawPixmap(0, 0, background.width(), background.height(), background);} if(m_icon_dir != ""){QPixmap icon(m_icon_dir);painter.drawPixmap(18, (background.height() - icon.height())/2, icon.width(), icon.height(), icon);x = icon.width() + 8;w = background.width() - icon.width() - 5;} else{w = background.width();}painter.setPen(QColor(m_text_color));if(m_icon_dir == "")painter.setFont(QFont("微软雅黑", 13));elsepainter.setFont(QFont("微软雅黑", 15));//绘制文字painter.drawText(x, y, w, h, Qt::AlignCenter, m_text);}void PictureBtn::changeState(){if(m_is_selected){m_is_selected = false;setEnabled(true);update();}else{m_is_selected = true;setEnabled(false);m_text_color = "#0e0e0e";update();}}void PictureBtn::enterEvent(QEvent *){if(!m_is_selected){m_text_color = "#0000ff";update();}}void PictureBtn::leaveEvent(QEvent *){if(!m_is_selected){m_text_color = "#0e0e0e";update();}}


//使用实例

PictureBtn *m_btn = new PictureBtn("图片方式", "图片路径", “图标路径”, "按钮上面的字", "父部件");

 

11. 数据库操作

#define ret_type int#define RET_OK 0#define RET_FAIL 1ret_type DBBasic::connect(QSqlDatabase &db){db = QSqlDatabase::addDatabase("QMYSQL", QUuid::createUuid());//连接数据库的配置信息db.setHostName("ip地址");db.setUserName("数据库用户名");db.setPassword("数据库密码");db.setDatabaseName("数据库名称");if ( ! db.open() ) {handleError( db.lastError(), db );return RET_FAIL;}QSqlQuery query(db);if ( ! query.exec("set names 'utf8'") ) {handleError( query.lastError(), db );return RET_FAIL;}return RET_OK;}ret_type DBBasic::execute(const QString query_str, QSqlQuery &sql_query, QSqlDatabase &db){QSqlQuery query(db);if ( ! query.exec(query_str) ) {handleError(query.lastError(), db);return RET_FAIL;}sql_query = query;return RET_OK;}ret_type DBLogic::test(QSqlQuery &query){ret_type ret = RET_OK;QString query_str;QSqlDatabase m_conn;//初始化query.clear();query_str = QString("sql语句");DBBasic::connect(m_conn);ret = DBBasic::execute(query_str, query, m_conn);//query为执行得到的结果if(ret!= RET_OK) {return RET_FAIL;}//一条一条处理执行结果(当用select来执行的时候),这里看自己需求)while (query.next()){   //QString title = query.value(0).toString();    //int year = query.value(1).toInt();   //std::cerr << qPrintable(title) << ": " << year << std::endl;}return RET_OK;}


以上数据库代码,只是使用的基本方式,不一定能满足所有要求,

可以再参考右边网址来使用: Qt数据库总结

 

12. 弹出自定义窗口的实现

 //窗口的类型
typedef enum _window_style {
 WIND_CL_ONE_BUTTON = 700,
 WIND_CL_DOUBLE_BUTTONS,
 WIND_CL_NO_BUTTONS,
 WIND_CONFIRM,
 WIND_FAILED,
 WIND_SUCCESS,
 WIND_INFO
}window_style;

//基类:

//BaseWind.h#ifndef BASEWIND_H#define BASEWIND_H#include <QDialog>#include <QPoint>#include <QString>#include <QPainter>#include <QKeyEvent>#include "ui_BaseWind.h"//这里可能会有一些宏定义,所以根据你的需求自己确定class BaseWind : public QDialog{Q_OBJECTpublic:BaseWind(const int width, const int height, const QString title, QWidget *parent = 0);~BaseWind();protected:void setValidSize(const int min_width, const int min_height);void keyPressEvent(QKeyEvent *event);//屏蔽回车键private:Ui::BaseWind ui;protected:int m_width, m_height;//窗口的宽、高int m_relative_x, m_relative_y;//窗口的相对坐标QString m_title;//窗口的标题protected slots:void windowClose();void okButtonClicked();};#endif // BASEWIND_H


 

//BaseWind.cpp#include "BaseWind.h"BaseWind::BaseWind(const int width, const int height, const QString title, QWidget *parent): QDialog(parent){ui.setupUi(this);m_width = width;m_height = height;m_title = title;this->setFixedSize(m_width + 2, m_height + 2);//给窗口的边缘多留1个像素,否则窗口的边缘的线很细//裁剪对话框,去掉标题、按钮及边框this->setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::CustomizeWindowHint | Qt::FramelessWindowHint);//设置窗口为模态显示this->setModal(true);}BaseWind::~BaseWind(){}void BaseWind::setValidSize(const int min_width, const int min_height){if(m_width < min_width)m_width = min_width;//限定窗口宽的最小值if(m_height < min_height)m_height = min_height;//限定窗口高的最小值m_relative_x = 1;//给窗口的边缘多留1个像素,否则窗口的边缘的线很细m_relative_y = 1;}void BaseWind::keyPressEvent(QKeyEvent *event){if(event->key() == Qt::Key_Return){return;}}void BaseWind::windowClose(){this->hide();}void BaseWind::okButtonClicked(){emit accepted();}


//继承基类,派生类的实现

#ifndef AUTOCLOSEWIND_H#define AUTOCLOSEWIND_H#include "BaseWind.h"#include "ui_AutoCloseWind.h"class AutoCloseWind : public BaseWind{Q_OBJECTpublic:AutoCloseWind(const window_style style, const int width, const int height, const QString content, QWidget *parent = 0);~AutoCloseWind();private:void paintUI();void paintEvent(QPaintEvent *);private slots:void windowClose();private:Ui::AutoCloseWind ui;window_style m_style;//窗口的类型QString m_content;//提示的内容};#endif // AUTOCLOSEWIND_H


 

//AutoCloseWind.cpp#include "AutoCloseWind.h"#include <QTimer>AutoCloseWind::AutoCloseWind(const window_style style, const int width, const int height, const QString content, QWidget *parent): BaseWind(width, height, "", parent){ui.setupUi(this);m_style = style;m_content = content;paintUI();}AutoCloseWind::~AutoCloseWind(){}void AutoCloseWind::paintUI(){this->setValidSize(120, 40);//设置窗口的宽最小为120,高最小为40QTimer::singleShot(1500,  this, SLOT(windowClose()));//窗口1.5秒后消失}void AutoCloseWind::paintEvent(QPaintEvent *){if(this == NULL)return;QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing, true);//防止曲线锯齿//画背景painter.setBrush(QColor("#f6f6f6"));painter.setPen(QColor("#a0a0a0"));painter.drawRoundedRect(m_relative_x, m_relative_y, m_width, m_height, 2, 2);//画图标QPixmap icon;if(m_style == WIND_SUCCESS)icon.load(SUCCESS_ICON);else if(m_style == WIND_INFO)icon.load(INFO_ICON);painter.drawPixmap(m_relative_x + 10, m_relative_y + (m_height - 24)/2, 24, 24, icon);//设定图标的大小为:24*24//画文字QFont content_font("微软雅黑", 13);painter.setFont(content_font);painter.setPen(QColor("#404040"));painter.drawText(m_relative_x + 45, m_relative_y, m_width - 55, m_height, Qt::AlignLeft | Qt::AlignVCenter, m_content);}void AutoCloseWind::windowClose(){this->hide();delete this;}


 13. 图片大小转换

 QPixmap map("图片路径");

 QPixmap set_map = map.scaled("宽", “高',Qt::IgnoreAspectRatio);    //Qt::KeepAspectRatio:这种方式为保持比例

 

14. 窗口置顶的问题

(1).让QMainWindow窗口在QDialog窗口上面置顶层,默认是置QDialog窗口的底层
           setWindowFlags(Qt::Dialog);
           或者
           子窗口名:userManagerDialog
           Qt::WindowFlags flags = Qt::Dialog;
           userManagerDialog->setWindowFlags(flags);
 
 (2).弹出子窗口时禁用主窗口
           userManagerDialog->setWindowModality(Qt::ApplicationModal); //阻塞除当前窗体之外的所有的窗体