c++ qt

c++ qt

创建项目:

  • widget:普通窗口,控件
  • window:可以添加状态栏等

指定父对象

#include<QApplication>
#include<QWidget>
#include<QPushButton>

int main(int argc,char **argv){
    QApplication app(argc,argv);
    QWidget w;
    w.setWindowTitle("ttile");
    
    QPushButton b;
    b.setText("^_^");
    b.setParent(&w);//指定父对象
    b.move(100,0);

    QPushButton b1(&w);//指定父对象
    b1.setText("abe");
    //b1.move(0,100);
    b1.setGeometry(0,100,200,200);//x,y,w,h


    w.show();
    app.exec();
    return 0;
}

指定了父对象就会随父对象一起展示,如果没指定就要自己展示,而且独立存在。

标准信号和槽(hard)

  1. 一个信号可以连接多个槽函数
  2. 槽函数执行的顺序是随机的,不能控制
  3. 一个槽函数可以被多个信号连接
  4. 信号可以连接信号
  5. 信号槽连接成功后,可以断开连接disconnect
  6. 槽函数可以是lambda表达式
#include "widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QPushButton *b1 = new QPushButton(this);
    b1->setText("b1");
    connect(b1,&QPushButton::pressed,this,&QWidget::close);//点击btn关闭窗口
    /*	参数1:监听对象
    	参数2:监听动作(信号)(可自定义)
    	参数3:哪个对象处理
    	参数4:怎么处理(槽)(可自定义)
    	*/
}

自定义槽函数

#include "widget.h"
#include <QPushButton>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QPushButton *b1 = new QPushButton(this);
    b1->setText("b1");
    //connect(b1,&QPushButton::pressed,this,&QWidget::close);
    connect(b1,&QPushButton::pressed,this,&Widget::mySolt);
}
void Widget::mySolt(){
    qDebug()<<"111";
}

先添加成员函数 mySolt

lamda方式:

配置文件 最后先添加:CONFIG += C++11

#-------------------------------------------------
#
# Project created by QtCreator 2023-08-15T19:25:24
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = 03_again
TEMPLATE = app


SOURCES += main.cpp\
        widget.cpp

HEADERS  += widget.h

CONFIG += C++11

#include "widget.h"
#include <QPushButton>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QPushButton *b1 = new QPushButton(this);
    b1->setText("b1");
    //connect(b1,&QPushButton::pressed,this,&QWidget::close);
    //connect(b1,&QPushButton::pressed,this,&Widget::mySolt);
    connect(b1,&QPushButton::pressed,[=](){
        qDebug()<<"222";
    });
}

=符号是将范围内的所有成员形参形式传入匿名函数,方便里面调用。

  1. mutable:通过= 传递的是被const修饰的,是不可改变的,添加 mutable 即可不被const修饰。
  2. exception:如果想抛出异常需要添加 exception
  3. -> int:如果有返回值,需要按照这个格式添加

自定义信号

一个窗口控制另一个窗口显示:

创建新窗口,创建自定义信号:

#ifndef SUB_H
#define SUB_H

#include <QWidget>


class sub : public QWidget
{
    Q_OBJECT
public:
    explicit sub(QWidget *parent = 0);
    
signals:
    void mySignal();//自定义信号
    
public slots:
    void mySolt();//自定义处理函数(槽)
    
};

#endif // SUB_H

创建按钮并发送自定义信号:

#include "sub.h"
#include <QPushButton>

sub::sub(QWidget *parent) :
    QWidget(parent)
{
    QPushButton *b2 = new QPushButton(this);    
    b2->setText("b2");
    connect(b2,&QPushButton::released,this,&sub::mySolt);//按钮监听
}
void sub::mySolt(){
    emit mySignal();//发送自定义信号
}

第一个窗口引用新窗口:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <sub.h>

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    Widget(QWidget *parent = 0);
    ~Widget();
    void mySolt();
    void mySignalDeal();//处理自定义信号函数
    sub s;//新窗口
};

#endif // WIDGET_H

实现展示隐藏另一个窗口功能:

#include "widget.h"
#include <QPushButton>
#include <QDebug>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,300);

    QPushButton *b1 = new QPushButton(this);
    b1->setText("b1");
    connect(b1,&QPushButton::pressed,this ,&Widget::mySolt);//本窗口按钮监听
        
    connect(&s,&sub::mySignal,this,&Widget::mySignalDeal);//监听新窗口发送的自定义信号
}
void Widget::mySolt(){//展示新窗口
    s.show();
    this->hide();
}
void Widget::mySignalDeal(){//处理自定义信号函数
     //qDebug()<<"mySignalDeal";
    s.hide();
    this->show();
}

自定义带参数的信号

创建新窗口,创建自定义信号:

#ifndef SUB_H
#define SUB_H

#include <QWidget>


class sub : public QWidget
{
    Q_OBJECT
public:
    explicit sub(QWidget *parent = 0);
    
signals:
    void mySignal(int, QString);//自定义信号
    
public slots:
    void mySolt();//自定义处理函数(槽)
    
};

#endif // SUB_H

创建按钮并发送自定义信号:

#include "sub.h"
#include <QPushButton>

sub::sub(QWidget *parent) :
    QWidget(parent)
{
    QPushButton *b2 = new QPushButton(this);    
    b2->setText("b2");
    connect(b2,&QPushButton::released,this,&sub::mySolt);//按钮监听
}
void sub::mySolt(){
    emit mySignal(5,"test");//发送自定义信号
}

第一个窗口引用新窗口:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <sub.h>

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    Widget(QWidget *parent = 0);
    ~Widget();
    void mySolt();
    void mySignalDeal(int,QString);//处理自定义信号函数
    sub s;//新窗口
};

#endif // WIDGET_H

实现展示隐藏另一个窗口功能:

#include "widget.h"
#include <QPushButton>
#include <QDebug>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,300);

    QPushButton *b1 = new QPushButton(this);
    b1->setText("b1");
    connect(b1,&QPushButton::pressed,this ,&Widget::mySolt);//本窗口按钮监听
        
    //connect(&s,&sub::mySignal,this,&Widget::mySignalDeal);//监听新窗口发送的自定义信号
    void (sub::*fun)(int,QString) = &sub::mySignal;//需要 函数指针 配合
    connect(&s,fun,this,&Widget::mySignalDeal);//监听新窗口发送的自定义信号
}
void Widget::mySolt(){//展示新窗口
    s.show();
    this->hide();
}
void Widget::mySignalDeal(int i,QString str){//处理自定义信号函数
     qDebug()<<i<<str;
    s.hide();
    this->show();
}

菜单栏工具栏等

#include "mainwindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QDebug>
#include <QToolBar>
#include <QPushButton>
#include <QStatusBar>
#include <QLabel>
#include <QTextEdit>
#include <QDockWidget>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //=== menuBar ===
    QMenuBar *mBar = menuBar();
    //add menu
    QMenu *pFile = mBar->addMenu("FIle");
    //add menu item
    QAction *pNew = pFile->addAction("new");
    connect(pNew,&QAction::triggered,[=](){qDebug()<<"11";});//deal
    pFile->addSeparator();//devide
    QAction *pOpen = pFile->addAction("open");

    //=== toolBar ===
    QToolBar *toolBar = addToolBar("toolBar");
    //add item
    toolBar->addAction(pNew);
    //add item by widget
    QPushButton *b = new QPushButton(this);
    b->setText("btn");
    connect(b,&QPushButton::clicked,[=](){b->setText("^_^");});
    toolBar->addWidget(b);

    //=== stateBar ===
    QStatusBar *sBar = statusBar();
    QLabel *label = new QLabel(this);
    label->setText("Normal text file");
    sBar->addWidget(label);
    sBar->addWidget(new QLabel("label2",this));
    sBar->addPermanentWidget(new QLabel("label3",this));


    //=== coreControl ===
    QTextEdit *textEdit = new QTextEdit(this);
    setCentralWidget(textEdit);

    //=== floatWindow ===
    QDockWidget *dock = new QDockWidget(this);
    addDockWidget(Qt::RightDockWidgetArea,dock);
    dock->setWidget(new QTextEdit(this));

    resize(400,300);
}

MainWindow::~MainWindow()
{
    
}

模态和非模态对话框

模态对话框:打开时阻塞,不能对后面的主窗口进行操作。

#include "mainwindow.h"
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QMenuBar *mBar = menuBar();
//    setMenuBar(mBar);
    QMenu *menu = mBar->addMenu("dialog");
    QAction *pl = menu->addAction("modalDialog");
    connect(pl,&QAction::triggered,[=](){
        QDialog dlg;
        dlg.resize(100,100);
        dlg.exec();
    });

    QAction *p2 = menu->addAction("Dialog");
    connect(p2,&QAction::triggered,[=](){
        QDialog *dlg = new QDialog();
        dlg->resize(100,100);
        dlg->setAttribute(Qt::WA_DeleteOnClose);//close to free
        dlg->show();
    });

    resize(400,300);
}

MainWindow::~MainWindow()
{
    
}

标准对话框和文件对话框

#include "mainwindow.h"
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QMenuBar *mBar = menuBar();
//    setMenuBar(mBar);
    QMenu *menu = mBar->addMenu("dialog");
    
    //关于
    QAction *p3 = menu->addAction("about");
    connect(p3,&QAction::triggered,[=](){
        QMessageBox::about(this,"about","about Qt");
    });

    //问题
    QAction *p4 = menu->addAction("question");
    connect( p4,&QAction::triggered,[=](){
        int ret = QMessageBox::question(this,"question","Are you ok?");
        /*You can specify your own buttons
        int ret = QMessageBox::question(this,"question","Are you ok?",
                                        QMessageBox::Yes|QMessageBox::No);
        */
        switch(ret){
        case QMessageBox::Yes:
            qDebug()<<"yes";
            break;
        case QMessageBox::No:
            qDebug()<<"no";
            break;
        default:
            break;
        }
    });

    //文件
    QAction *p5 = menu->addAction("file");
    connect( p5,&QAction::triggered,[=](){
        QString path = QFileDialog::getOpenFileName(this,"open","../",
                                                    "source(*.cpp *.h *.c );;"
                                                    "Text(*.txt);;"
                                                    "all(*.*)");
        qDebug()<<path;
    });

    resize(400,300);
}

MainWindow::~MainWindow()
{
    
}

设计器

常用控件

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QCompleter>
#include <QStringList>
#include <QMovie>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //push button
    ui->btn->setText("hhh");
    //connect(ui->btn,&QPushButton::clicked,this,&MainWindow::close);

    //radio button
    ui->radioButton->setText("radio11");
    ui->radioButton_2->setChecked(true);

    //check box
    ui->checkBox->setChecked(true);
    ui->checkBox_2->setChecked(true);

    //line edit
    QString str = ui->lineEdit->text();
    qDebug()<<str;
    //line edit mode:password
    //ui->lineEdit->setEchoMode(QLineEdit::Password);

    //line edit hint
    QStringList list;
    list<<"hell"<<"How are you"<<"fine";
    QCompleter *com = new QCompleter(list,this);
    com->setCaseSensitivity(Qt::CaseInsensitive);//
    ui->lineEdit->setCompleter(com);

    //label -> img gif url
    ui->label->setText("^_^");

    //img
    ui->labelImage->setPixmap(QPixmap("://image/OnePiece.png"));
    ui->labelImage->setScaledContents(true);

    //create movie
    QMovie *myMovie = new QMovie("://image/mario.gif");
    //set movie to label
    ui->labelGif->setMovie(myMovie);
    ui->labelGif->setScaledContents(true);
    //start movie
    myMovie->start();

    //url
    ui->labelUrl->setText("<a href=\"https://www.baidu.com\">baidu</a>");
    ui->labelUrl->setOpenExternalLinks(true);

    //lcm Number
    ui->lcdNumber->display("hello");

    //progressBar
    ui->progressBar->setMaximum(100);
    ui->progressBar->setMinimum(0);
    ui->progressBar->setValue(75);



}

MainWindow::~MainWindow()
{
    delete ui;
}

//Stacked Widget change
void MainWindow::on_btn_sw_clicked()
{
    static int i = 0;
    ui->stackedWidget->setCurrentIndex(++i%2);
}

布局

绝对定位 和 布局定位。

手动拖进去点击布局模式就好。多拉几个容器,分容器内布局,最后总体布局。

自定义控件

创建一个widget:

#include "smallwidget.h"
#include <QSpinBox>
#include <QSlider>
#include <QHBoxLayout>

SmallWidget::SmallWidget(QWidget *parent) :
    QWidget(parent)
{
    QSpinBox *spin = new QSpinBox(this);
    QSlider *slider = new QSlider(Qt::Horizontal,this);

    QHBoxLayout *hLayout = new QHBoxLayout(this);
    hLayout->addWidget(spin);
    hLayout->addWidget(slider);
}

这个控件就会变成自定义控件:

给两个控件 相关联

	//interaction
    //spin -> slider // 因为QSpinBox的valueChanged方法重载了,有两个,需要类型转化
    connect(spin,static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
            slider,&QSlider::setValue);
    //slider -> spin
    connect(slider,&QSlider::valueChanged,
            spin,&QSpinBox::setValue);

样式表

	//css
    ui->label->setStyleSheet("QLabel{color:red;}");
    ui->label->setStyleSheet("QLabel{color:rgb(0,255,255);"
                             "background-color:red;"
                             "border-image:url(:/new/prefixl/image/1.png);"
                             "}");

	ui->gameWidget->setStyleSheet("QWidget{border:1 solid black;}");

	//widget内所有 label 元素
	this->setStyleSheet(QLabel{..});
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
+    a.setStyleSheet("");//全局
    Widget w;
    w.show();
    
    return a.exec();
}

伪状态

	ui->btn->setStyleSheet("QPushButton{"
                             "border-image:url(:/new/prefixl/image/1.png);"
                             "}"
                           "QPushButton:hover{"
                             "border-image:url(:/new/prefixl/image/up.png);"
                             "}"
                           "QPushButton:pressed{"
                             "border-image:url(:/new/prefixl/image/click.png);"
                             "}"
                          
                          );

事件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMouseEvent>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

protected:
	//鼠标事件
    void mousePressEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
	//进入、离开 窗口
	void enterEvent(QEvent *);
    void leaveEvent(QEvent *);
	//键盘事件
	void keyPressEvent(QKeyEvent *);
	//定时器事件
	void timerEvent(QTimerEvent *);
    
private:
    Ui::Widget *ui;

    int timerId;
};

#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //css
    ui->label->setStyleSheet("QLabel{color:rgb(0,255,255);"
                             "background-color:red;"
                             "background-image:url()"
                             "}");

    //set trace mouse
    this->setMouseTracking(true);
        
    //start timer
    timerId = this->startTimer(1000);//ms
        
    //key press 多个widget时,需要监听的widget要写这个。
    setFocusPolicy(Qt::StrongFocus);
}

void Widget::mousePressEvent(QMouseEvent *ev){
    qDebug()<<"111";
    int x = ev->x();
    int y = ev->y();
    QString str;
    /*
    if(ev->button()==Qt::LeftButton){
        str = QString("left:%1 %2").arg(x).arg(y);
    }else if(ev->button()==Qt::RightButton){
        str = QString("right:%1 %2").arg(x).arg(y);
    }else if(ev->button()==Qt::MiddleButton){
        str = QString("mid:%1 %2").arg(x).arg(y);
    }else{*/
         str = QString("other:%1 ").arg(ev->button());
//    }


    //qDebug()<<str;
    ui->label->setText(str);
}
void Widget::mouseReleaseEvent(QMouseEvent *ev){
    qDebug()<<"222";
}
void Widget::mouseMoveEvent(QMouseEvent *ev){
    int x = ev->x();
    int y = ev->y();
    QString str;
    str = QString("mid:%1 %2").arg(x).arg(y);
    ui->label->setText(str);
}
void Widget::enterEvent(QEvent *e){

    ui->label->setText("enterEvent");
}

void Widget::leaveEvent(QEvent *e){
    ui->label->setText("leaveEvent");
}
void Widget::keyPressEvent(QKeyEvent *e){
    QString str;
    str = QString("key:%1").arg((char)e->key());
    ui->label->setText(str);
}

void Widget::timerEvent(QTimerEvent *e){
    //定时器可以定义多个。
    if(e->timerId()==this->timerId){
        static int sec = 0;
        QString str;
        str = QString("timer:%1").arg(sec++);
        ui->label->setText(str);

        //cloae if 5sec
        if(sec==5){
            this->killTimer(timerId);
        }
    }
}


Widget::~Widget()
{
    delete ui;
}

事件的接收和忽略

重写点击事件,事件拦截,自定义按钮继承QPushbutton

#ifndef MYBUTTON_H
#define MYBUTTON_H

#include <QWidget>
#include <QPushButton>

class myButton : public QPushButton
{
    Q_OBJECT
public:
    explicit myButton(QWidget *parent = 0);
protected:
    void mousePressEvent(QMouseEvent *e);
signals:
    
public slots:
    
};

#endif // MYBUTTON_H
#include "mybutton.h"
#include <QMouseEvent>
#include <QDebug>
myButton::myButton(QWidget *parent) :
    QPushButton(parent)
{
}
//重写点击事件
void myButton::mousePressEvent(QMouseEvent *e){
    if(e->button()==Qt::LeftButton){//如果是左键点击,拦截事件
        qDebug()<<"222";
    }else{//点击 空格,继续传递事件
        QPushButton::mouseMoveEvent(e);
    }

}

main.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>
#include <QMessageBox>
#include <QCloseEvent>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->pushButton->setText("hh");
    connect(ui->pushButton,&QPushButton::pressed,[=](){
        qDebug()<<"111";
    });
}

Widget::~Widget()
{
    delete ui;
}

接收 或 忽略:

监听点击关闭按钮事件:

void closeEvent(QCloseEvent *);
void Widget::closeEvent(QCloseEvent *e){
    int ret = QMessageBox::question(this,"quer","do you close window?");
    if(ret==QMessageBox::Yes){
        qDebug()<<"yes";
        e->accept();//事件的传递
    }else{
        qDebug()<<"no";
        e->ignore();//事件的忽略,如果没有这行,就算选no也会关闭窗口
    }
}

event()函数

每个控件都有event()函数,通过这个函数分发具体的事件函数。

bool event(QEvent *);
bool Widget::event(QEvent *e){
    //事件分发
    switch(e->type()){
    case QEvent::Close:
        qDebug()<<"event->Close";
        QCloseEvent *env = static_cast< QCloseEvent *>(e);
        Widget::closeEvent(env);
        return true;
    }

    if(e->type()==QEvent::Timer){//屏蔽定时器
        return true;
    }else if(e->type()==QEvent::KeyPress){//屏蔽非按键B
        QKeyEvent *env =  static_cast< QKeyEvent *>(e);
        if(env->key()==Qt::Key_B){
            return QWidget::event(e);
        }
        return true;
    }
    return QWidget::event(e);//一定要返回其他事件,否则其他事件都无法下发。
}

事件过滤器

每个控件都有单独的event函数,如果想一次性屏蔽多个控件的某些event事件,需要用到:
bool eventFilter(QObject *, QEvent *);

来自QObject 最终基类。

bool eventFilter(QObject *, QEvent *);
#include <QDebug>
#include <QMessageBox>
#include <QCloseEvent>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
        
    //安装过滤器
    ui->label->installEventFilter(this);
}

bool Widget::eventFilter(QObject *obj, QEvent *e){
    //判断控件
    if(obj==ui->label){
        //判断事件
        if(e->type()==QEvent::MouseMove){
            ui->label->setText("filter");
            return true;
        }
    }
    return QWidget::eventFilter(obj,e);//一定要返回其他事件

}

注意:事件过滤器和被安装过滤器的组件必须在同一个线程里面,否则无效。
如果在安装过滤器之后,这两个组件到了不同的线程,那么只能等到二者重新回到同一个线程的时候过滤器才生效。

绘图

画背景图

protected:
    //重新绘图事件,虚函数
    //如果在窗口绘图,必须放在绘图事件里实现
	//绘图事件内部自动调用,窗口需要重绘的时候(状态改变)
    void paintEvent(QPaintEvent *);
void Widget::paintEvent(QPaintEvent *e){
    //QPainter p(this);
    QPainter p;//创建画家对象
    p.begin(this);//指定当前窗口为绘图设备

    //画背景图
    //p.drawPixmap(0,0,width(),height(),QPixmap("../Image/bk.jpg"));
    p.drawPixmap(rect(),QPixmap("../Image/bk.jpg"));

    p.end();
}

简单绘图

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>//画家
#include <QPen>//画笔
#include <QBrush>//画刷

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}

void Widget::paintEvent(QPaintEvent *e){
    //QPainter p(this);
    QPainter p;//创建画家对象
    p.begin(this);//指定当前窗口为绘图设备

    //画背景图
    //p.drawPixmap(0,0,width(),height(),QPixmap("../Image/bk.jpg"));
    p.drawPixmap(rect(),QPixmap("../Image/bk.jpg"));

    //定义画笔
    QPen pen;
    pen.setWidth(5);//设置线宽
    //pen.setColor(Qt::red);//颜色
    pen.setColor(QColor(161,242,141));//颜色
    pen.setStyle(Qt::DashLine);//设置风格
    //把画笔给画家
    p.setPen(pen);

    //画直线
    p.drawLine(50,50,150,50);
    p.drawLine(50,50,50,150);

    //画刷
    QBrush brush;
    brush.setColor(Qt::yellow);
    brush.setStyle(Qt::Dense1Pattern);

    p.setBrush(brush);

    //画矩形
    p.drawRect(100,100,150,50);

    //画圆
    p.drawEllipse(QPoint(150,150),50,25);



    p.end();
}

Widget::~Widget()
{
    delete ui;
}

手动更新窗口

	//刷新窗口,整个窗口都重绘
    update();//间接调用paintEvent()

	//刷新局部
	update(0,200,width(),height()-200);

注意:不要放到paintEvent()函数里面,不然会无限递归循环,程序会很卡,崩掉。

QBitmap 和 QPixmap 区别

QBitmap 只显示黑白两种颜色,占用内存小

#include <QPainter>
#include <QBitmap>
void Widget::paintEvent(QPaintEvent *e){
    QPainter p(this);
    //背景透明
    p.drawPixmap(0,0,QPixmap("../Image/butterfly.png"));
    p.drawPixmap(200,0,QBitmap("../Image/butterfly.png"));

    //背景白色
    QPixmap pixmap;
    pixmap.load("../Image/butterfly1.png");
    p.drawPixmap(0,200,pixmap);
    QBitmap bixmap;
    bixmap.load("../Image/butterfly1.png");
    p.drawPixmap(200,200,bixmap);
}

绘图设备

  • QPixmap:针对屏幕进行优化,和平台相关,不能对图片进行修改
  • QImage:和平台无关,可以对图片进行修改,在线程中绘图
  • QPicture:保存绘图的状态(二进制文件)

QPixmap:

#include "widget.h"
#include "ui_widget.h"
#include<QPainter>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //不在窗口内画图
    //绘图设备400x300
    QPixmap pixmap(400,300);

    QPainter p(&pixmap);
    //填充背景色
    pixmap.fill(Qt::white);
    //p.fillRect(0,0,400,300,QBrush(Qt::white));
    p.drawPixmap(0,0,80,80,QPixmap("../Image/face.png"));

    //保存图片
    pixmap.save("../Image/pixmap17.png");
}

Widget::~Widget()
{
    delete ui;
}

QImage:

#include "widget.h"
#include "ui_widget.h"
#include<QPainter>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //可指定背景透明。
    QImage image(400,300,QImage::Format_ARGB32);
    QPainter p;
    p.begin(&image);
    //绘图
    p.drawImage(0,0,QImage("../Image/face.png"));
    //对绘图设备前50个像素点进行操作
    for(int i=0;i<50;i++){
        for(int j=0;j<50;j++){
            image.setPixel(QPoint(i,j),qRgb(0,0,255));
            //获取像素点的rgb
            //image.pixel(QPoint(i,j));
        }
    }
    p.end();
    image.save("../Image/image17.png");
}

Widget::~Widget()
{
    delete ui;
}

QPicture:

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPicture>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    QPicture picture;
    QPainter p;
    p.begin(&picture);
    p.drawPixmap(0,0,80,80,QPixmap("../Image/face.png"));
    p.drawLine(50,50,150,50);
    p.end();
    picture.save("../Image/picture17.png");
    //保存的 是二进制文件 无法直接打开
}
void Widget::paintEvent(QPaintEvent *){
    //打开保存的二进制文件
    QPicture pic;
    pic.load("../Image/picture17.png");
    QPainter p(this);
    p.drawPicture(0,0,pic);
}

Widget::~Widget()
{
    delete ui;
}

QImage 和 QPixmap 相互转换

各个设备有各自的优势:显示好,传输快。

void Widget::paintEvent(QPaintEvent *){
	QPainter p(this);
    QPixmap pixmap;
    pixmap.load("../Image/face.png");
    //QPixmap -> QImage
    QImage tempImage = pixmap.toImage();
    p.drawImage(0,0,tempImage);

    QImage image;
    image.load("../Image/face.png");
    //QImage -> QPixmap
    QPixmap tempPixmap = QPixmap::fromImage(image);
    p.drawPixmap(100,0,tempPixmap);
}

不规则窗口

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void paintEvent(QPaintEvent *);
    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);

    
private:
    Ui::Widget *ui;
    QPoint p;
};

#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QMouseEvent>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //去窗口表栏
    setWindowFlags(Qt::FramelessWindowHint|windowFlags());
    //把窗口背景色设置为透明
    setAttribute(Qt::WA_TranslucentBackground);
}

//绘画背景透明图片
void Widget::paintEvent(QPaintEvent *){
    QPainter p(this);
    p.drawPixmap(0,0,QPixmap("../Image/sunny.png"));

}
//按下时:右键关闭;左键获取点击的图标并计算出左上角图标
void Widget::mousePressEvent(QMouseEvent *e){
    if(e->button()==Qt::RightButton){
        close();//右键关闭
    }else if(e->button()==Qt::LeftButton){
        //求坐标差
        //当前点击坐标 - 窗口左上角坐标
        p = e->globalPos() - this->frameGeometry().topLeft();
    }
}
//左键点击移动时,更新左上角的坐标。
void Widget::mouseMoveEvent(QMouseEvent *e){
    //判断是不是按住左键
    if(e->buttons() & Qt::LeftButton){
        move(e->globalPos()-p);
    }
}



Widget::~Widget()
{
    delete ui;
}

QFile

QFile读文件

#include "widget.h"
#include "ui_widget.h"
#include<QFile>
#include<QFileDialog>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_btn_read_clicked()
{
    QString path = QFileDialog::getOpenFileName(this,
                                        "open","../","TXT(*.txt)");
    if(path.isEmpty()==false){
        //文件对象
        QFile file(path);
        int ret = file.open(QIODevice::ReadOnly);
        if(ret == true){
            QByteArray array;

#if 0
            //一次性读文件 默认格式utf8编码
           array = file.readAll();

#endif
           //一行行读文件

           while(file.atEnd()==false){
               //读一行
               array += file.readLine();
           }

           ui->textEdit->setText(QString(array));
        }
        file.close();//关闭文件
    }
}

QFile写文件

void Widget::on_btn_write_clicked()
{
    QString path = QFileDialog::getSaveFileName(this,
                                        "save","../","TXT(*.txt)");
    if(path.isEmpty()==false){
        QFile file;//创建文件对象
        file.setFileName(path);//关联文件名字

        //打开方式,只写方式
        bool isOk = file.open(QIODevice::WriteOnly);
        if(isOk==true){
            //获取编辑器内容
            QString str = ui->textEdit1->toPlainText();
            //写文件
            //QString -> QByteArray
            file.write(str.toUtf8());

            //QString -> c++ string -> char *
            //file.write(str.toStdString().data());
            
            ///转化为本地平台编码
            //file.write(str.toLocal8Bit());
        }
        file.close();
    }
}

QFileInfo获取文件信息

#include<QFileInfo>
#include<QDebug>
#include<QDateTime>

		QFileInfo info(path);
        qDebug()<<"文件名字:"<<info.fileName().toUtf8().data();
        qDebug()<<"文件后缀:"<<info.suffix();
        qDebug()<<"文件大小:"<<info.size();
        qDebug()<<"文件创建时间:"<<
                  info.created().toString("yyyy-MM-dd hh:mm:ss");//2020-01-01 15:15:00

QDataStream读写文件

二进制方式读写。

void Widget::writeData(){
    //创建文件对象
    QFile file("../test.txt");
    bool isOk = file.open(QIODevice::WriteOnly);
    if(true==isOk){
        //创建数据流,和file文件关联
        //忘数据流中写数据,相当于往文件里写数据
        QDataStream stream(&file);
        stream<<QString("hello QDataStream")<<250;
        file.close();
    }
}
void Widget::readData(){
    //创建文件对象
    QFile file("../test.txt");
    bool isOk = file.open(QIODevice::ReadOnly);
    if(true==isOk){
        //创建数据流,和file文件关联
        //忘数据流中读数据,相当于往文件里读数据
        QDataStream stream(&file);
        //读的时候,按写的顺序取数据
        QString str;
        int a;
        stream >> str >> a;
        qDebug()<< str.toUtf8().data() << a;
        file.close();
    }
}

QTextStream操作文件

文本方式读写。

void Widget::writeData(){
    QFile file;
    file.setFileName("../demo.txt");
    bool isOk = file.open(QIODevice::WriteOnly);
    if(true==isOk){
        QTextStream stream(&file);
        //指定编码,默认按照平台的编码
        stream.setCodec("UTF-8");
        stream<<QString("hlo QTextStream")<<200;
        file.close();
    }
}
void Widget::readData(){
    QFile file;
    file.setFileName("../demo.txt");
    bool isOk = file.open(QIODevice::ReadOnly);
    if(true==isOk){
        QTextStream stream(&file);
        //指定编码,默认按照平台的编码
        stream.setCodec("UTF-8");

        //read 距离识别会出错
        QString str;
        int a;
        stream >> str >> a;
        cout << str.toUtf8().data() << a;
        file.close();
    }
}

QBuffer

和文件关系不大,内存文件。

#include "widget.h"
#include "ui_widget.h"
#include<QBuffer>
#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    QByteArray array;
    QBuffer memFile(&array);//创建内存文件
    memFile.open(QIODevice::WriteOnly);
    memFile.write("1111 ");
    memFile.write("22222");
    memFile.close();
    cout<<memFile.buffer();
    cout<<"array:"<<array;

        
    // 2
    QBuffer memFile1;
    memFile1.open(QIODevice::WriteOnly);
    QDataStream stream(&memFile1);
    stream << QString("test") <<340;
    memFile1.close();
    cout << memFile1.buffer();//读不到

    memFile1.open(QIODevice::ReadOnly);
    QDataStream in;
    in.setDevice(&memFile1);
    QString str;
    int a;
    in >> str >> a;
    memFile1.close();
    cout<< str << a;


}

Widget::~Widget()
{
    delete ui;
}

TCP通信

例子:

QT       += network

服务器:

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE

class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = nullptr);
    ~ServerWidget();

private slots:
    void on_btnSend_clicked();

    void on_btnClose_clicked();

private:
    Ui::ServerWidget *ui;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
};
#endif // SERVERWIDGET_H
#include "serverwidget.h"
#include "ui_serverwidget.h"

ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    setWindowTitle("服务器:8888");
    tcpServer = NULL;
    tcpSocket = NULL;

    //监听套接字,指定父对象,让其自动回收空间
    tcpServer = new QTcpServer(this);
    tcpServer->listen(QHostAddress::Any,8888);
    connect(tcpServer,&QTcpServer::newConnection,[=](){
        //取出建立好的连接的套接字
        tcpSocket = tcpServer->nextPendingConnection();
        //获取对方的IP和端口
        QString ip = tcpSocket->peerAddress().toString();
        quint16 port = tcpSocket->peerPort();
        QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);

        ui->textEditRead->setText(temp);

        connect(tcpSocket,&QTcpSocket::readyRead,[=](){
            //从通信套接字中取出内容
            QByteArray array = tcpSocket->readAll();
            ui->textEditRead->append(array);
        });

    });

}

ServerWidget::~ServerWidget()
{
    delete ui;
}


void ServerWidget::on_btnSend_clicked()
{
    if(NULL==tcpSocket) return;
    //获取编辑区内容
    QString str = ui->textEditWrite->toPlainText();
    //给对方发送数据,使用套接字是tcpSocket
    tcpSocket->write(str.toUtf8().data());
}


void ServerWidget::on_btnClose_clicked()
{
    if(NULL==tcpSocket) return;
    //主动和客户端端口断开连接
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
}

客户端:

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include <QWidget>
#include <QTcpSocket>

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ClientWidget(QWidget *parent = nullptr);
    ~ClientWidget();

private slots:
    void on_btnConnect_clicked();

    void on_btnSend_clicked();

    void on_btnClose_clicked();

private:
    Ui::ClientWidget *ui;
    QTcpSocket *tcpSocket;
};

#endif // CLIENTWIDGET_H

#include "clientwidget.h"
#include "ui_clientwidget.h"

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);

    setWindowTitle("客户端");
    tcpSocket = NULL;
    tcpSocket = new QTcpSocket(this);

    connect(tcpSocket,&QTcpSocket::connected,[=](){
        ui->textEditRead->setText("成功和服务器连接");
    });

    connect(tcpSocket,&QTcpSocket::readyRead,[=](){
        //获取对方发送的内容
        QByteArray array = tcpSocket->readAll();
        //追加到编辑区
        ui->textEditRead->append(array);
    });

}

ClientWidget::~ClientWidget()
{
    delete ui;
}

void ClientWidget::on_btnConnect_clicked()
{
    //获取服务器IP端口
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();

    //主动和服务器建立连接
    tcpSocket->connectToHost(QHostAddress(ip),port);
}


void ClientWidget::on_btnSend_clicked()
{
    if(NULL==tcpSocket) return;
    //获取编辑框内容
    QString str = ui->textEditWrite->toPlainText();
    //发送数据
    tcpSocket->write(str.toUtf8().data());
}


void ClientWidget::on_btnClose_clicked()
{
    if(NULL==tcpSocket) return;
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
}

显示两个窗口:main.cpp

#include "serverwidget.h"
#include "clientwidget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ServerWidget w;
    w.show();

    ClientWidget c;
    c.show();

    return a.exec();
}

out:

UDP通信

例子:

QT       += network
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QUdpSocket>//UDP套接字

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void dealMsg();//槽函数,处理对方发过来的数据

private slots:
    void on_btnSend_clicked();

    void on_btnClose_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket;
};
#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle("服务器8888");

    //分配空间,指定父对象
    udpSocket = new QUdpSocket(this);

    //绑定
    udpSocket->bind(8888);

    //当对方成功发送数据过来
    //自动触发,readyRead()
    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
}
void Widget::dealMsg(){
    //读取对方发送的内容
    char buf[1024] = {0};
    QHostAddress cliAddr;//对方地址
    quint16 port;//对方端口
    qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
    if(len>0){
        //格式化
        QString str = QString("[%1:%2[] %3")
                          .arg(cliAddr.toString())
                          .arg(port)
                          .arg(buf);
        //给编辑区设置内容
        ui->textEdit->setText(str);
    }
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_btnSend_clicked()
{
    //先获取对方的ip和端口
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();

    //获取编辑区内容
    QString str = ui->textEdit->toPlainText();

    //给指定的IP发送数据
    udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
}


void Widget::on_btnClose_clicked()
{

}


可以和其他人的程序连接。

UDP多播组播

去到局域网发送 广播 。

组播:

#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle("服务器8888");

    //分配空间,指定父对象
    udpSocket = new QUdpSocket(this);

    //绑定
    //udpSocket->bind(8888);
    udpSocket->bind(QHostAddress::AnyIPv4,8888);

    //加入某个组播
    //组播地址是D类地址
    udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
    //udpSocket->leaveMulticastGroup(QHostAddress("224.0.0.2"));//退出组播

    //当对方成功发送数据过来
    //自动触发,readyRead()
    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
}
void Widget::dealMsg(){
    //读取对方发送的内容
    char buf[1024] = {0};
    QHostAddress cliAddr;//对方地址
    quint16 port;//对方端口
    qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
    if(len>0){
        //格式化
        QString str = QString("[%1:%2[] %3")
                          .arg(cliAddr.toString())
                          .arg(port)
                          .arg(buf);
        //给编辑区设置内容
        ui->textEdit->setText(str);
    }
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_btnSend_clicked()
{
    //先获取对方的ip和端口
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();

    //获取编辑区内容
    QString str = ui->textEdit->toPlainText();

    //给指定的IP发送数据
    udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
}


void Widget::on_btnClose_clicked()
{

}

加入某个组播,绑定端口时要使用 ipv4 .

QTimer定时器

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTimer>//定时器对象

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btnStart_clicked();

    void on_btnStop_clicked();

private:
    Ui::Widget *ui;
    QTimer *myTimer;
};
#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myTimer = new QTimer(this);

    connect(myTimer,&QTimer::timeout,[=](){
        static int i =0;
        i++;
        ui->lcdNumber->display(i);
    });
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_btnStart_clicked()
{
    //如果定时器没有激活,才启动
    if(myTimer->isActive()==false){
        //启动定时器
        //时间间隔100ms
        //每隔100ms,定时器myTimer自动触发timeout()
        myTimer->start(100);
    }
}


void Widget::on_btnStop_clicked()
{
    //如果定时器已经激活,才关闭
    if(myTimer->isActive()==true){
        myTimer->stop();
    }
}


每间隔多少ms就会发送一个 timeout 信号。

TCP传文件

例子:

服务器端:

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include<QTcpServer>//监听套接字
#include<QTcpSocket>//通信套接字
#include<QFile>
#include<QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE

class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = nullptr);
    ~ServerWidget();

    void sendData();//发送文件
private slots:
    void on_btnFile_clicked();

    void on_btnSend_clicked();

private:
    Ui::ServerWidget *ui;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
    QFile file;

    QString fileName;
    qint64 fileSize;
    qint64 sendSize;

    QTimer timer;
};
#endif // SERVERWIDGET_H

#include "serverwidget.h"
#include "ui_serverwidget.h"
#include<QFileDialog>
#include<QFileInfo>

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    ui->btnFile->setEnabled(false);
    ui->btnSend->setEnabled(false);
    tcpServer = new QTcpServer(this);
    tcpServer->listen(QHostAddress::Any,8888);
    connect(tcpServer,&QTcpServer::newConnection,[=](){
        tcpSocket = tcpServer->nextPendingConnection();
        QString ip = tcpSocket->peerAddress().toString();
        quint16 port = tcpSocket->peerPort();
        QString str = QString("[%1:%2]:成功连接").arg(ip).arg(port);
        ui->textEdit->setText(str);
        ui->btnFile->setEnabled(true);

        connect(tcpSocket,&QTcpSocket::readyRead,[=](){
            //取客户端的信息
            QByteArray buf = tcpSocket->readAll();
            if(QString(buf).contains("file done")){
                //文件接收完毕
                ui->textEdit->append("文件发送完毕22");
                file.close();

                //断开客户端端口
                tcpSocket->disconnectFromHost();
                tcpSocket->close();
            }else{
                //ui->textEdit->append("文件传输:"+buf);
                cout<<"out:"<<buf;
            }
        });

    });

    connect(&timer,&QTimer::timeout,[=](){
        //关闭定时器
        timer.stop();
        //发送文件
        sendData();
    });
}

ServerWidget::~ServerWidget()
{
    delete ui;
}

void ServerWidget::on_btnFile_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(this,"open","../");
    if(false==filePath.isEmpty()){
        fileName.clear();
        fileSize = 0;

        QFileInfo info(filePath);
        fileName = info.fileName();
        fileSize = info.size();

        cout<<"fileName:"<<fileName;
        cout<<"fileSize:"<<fileSize;

        sendSize = 0;
        file.setFileName(filePath);
        bool isOk = file.open(QIODevice::ReadOnly);
        if(false==isOk){
            cout<<"文件打开失败";
        }

        ui->textEdit->append(filePath);

        ui->btnFile->setEnabled(false);
        ui->btnSend->setEnabled(true);
    }else{
        cout<<"文件路径为空";
    }
}

//发送文件按钮
void ServerWidget::on_btnSend_clicked()
{
    //先发送文件头信息 文件名##文件大小
    QString head = QString ("%1##%2").arg(fileName).arg(fileSize);
    //发送头部信息
    qint64 len = tcpSocket->write(head .toUtf8());
    if(len>0){//头部信息发送成功
        //发送真正的文件信息
        //防止TCP黏包文件
        //需要通过定时器延时 20 ms
        timer.start(20);

    }else{
        cout<<"头部信息发送失败";
        file.close();
        ui->btnFile->setEnabled(true);
        ui->btnSend->setEnabled(false);
    }
}

void ServerWidget::sendData(){
    qint64 len = 0;
    do{
        //每次发数据的大小
        char buf[4*1024] = {0};
        len = 0;

        //往文件中读数据
        len = file.read(buf,sizeof(buf));
        //发送数据,读多少,发多少
        len = tcpSocket->write(buf,len);

        //发送的数据需要积累
        sendSize += len;

        //cout<<"sendSize:"<<sendSize;

    }while( len>0 );

    //是否发送文件完毕
    /*
    if(sendSize == fileSize){
        ui->textEdit->append("文件发送完毕");
        file.close();

        //把客户端端口
        tcpSocket->disconnectFromHost();
        tcpSocket->close();
    }*/
}

客户端:

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include <QWidget>
#include<QTcpSocket>
#include<QFile>

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ClientWidget(QWidget *parent = nullptr);
    ~ClientWidget();

private slots:
    void on_btnConnect_clicked();

private:
    Ui::ClientWidget *ui;

    QTcpSocket *tcpSocket;
    QFile file;

    QString fileName;
    qint64 fileSize;
    qint64 recvSize;

    bool isStart;
};

#endif // CLIENTWIDGET_H

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include<QMessageBox>
#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);

    ui->progressBar->setValue(0);

    tcpSocket = new QTcpSocket(this);
    isStart = true;
    connect(tcpSocket,&QTcpSocket::readyRead,[=](){

        //取出接收的内容
        QByteArray buf = tcpSocket->readAll();

        if(true==isStart){
            //接收头
            isStart = false;
            //解析头部信息
            //QString buf = "hello##1024"
            //buf.section("##",0,0);
            //初始化
            fileName = QString(buf).section("##",0,0);
            fileSize = QString(buf).section("##",1,1).toInt();
            recvSize = 0;

            cout<<"client-fileName:"<<fileName;
            cout<<"client-fileSize:"<<fileSize;

            //打开文件
            file.setFileName(fileName);
            bool isOk = file.open(QIODevice::WriteOnly);
            if(false==isOk){
                cout<<"WriteOnly error";
                tcpSocket->disconnectFromHost(); //断开连接
                tcpSocket->close();
                return ;
            }

            //弹出对话框,显示接收文件的信息
            //QString str = QString("接收的文件:[%1:%2kb]").arg(fileName).arg(fileSize/1024);
            //QMessageBox::information(this,"文件信息",str);

            //设置进度条
            ui->progressBar->setMaximum(fileSize/1024);
            ui->progressBar->setMinimum(0);
            ui->progressBar->setValue(0);


        }else{
            //文件信息
            qint64 len = file.write(buf);
            //cout<<"len:"<<len;
            //cout<<"buf.size:"<<buf.size();
            if(len>0){
                 recvSize += len;
                //cout<<len;

                //客户端接收多少,就告诉服务端多少
                QString str = QString::number(recvSize);
                tcpSocket->write(str.toUtf8().data());
            }

            //cout<<"client-fileSize:"<<fileSize;
            //cout<<"client-recvSize:"<<recvSize;
            //更新进度条
            ui->progressBar->setValue(recvSize/1024);


            if(recvSize == fileSize){

                //先给服务器发送 接收文件完成的信息
                tcpSocket->write("file done");

                file.close();
                QMessageBox::information(this,"完成","文件接收完成");

                tcpSocket->disconnectFromHost();
                tcpSocket->close();
            }
        }
    });
}

ClientWidget::~ClientWidget()
{
    delete ui;
}

void ClientWidget::on_btnConnect_clicked()
{
    QString ip = ui->lineEditIP->text();
    quint16 port = ui->lineEditPort->text().toInt();

    tcpSocket->connectToHost(QHostAddress(ip),port);

    isStart = true;
    //设置进度条
    ui->progressBar->setValue(0);
}

线程(bug)

例子:

mywidget

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include<QTimer>
#include "mythread.h" //线程头文件

QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget(QWidget *parent = nullptr);
    ~MyWidget();

    void dealTimeOut();
    void dealDone();
    void stopThread();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MyWidget *ui;

    QTimer *myTimer;
    MyThread *thread;
};
#endif // MYWIDGET_H
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QThread>

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    myTimer = new QTimer(this);
    connect(myTimer,&QTimer::timeout,this,&MyWidget::dealTimeOut);

    //分配空间
    thread = new MyThread(this);

    //接收数据处理完的信号
    connect(thread,&MyThread::isDone,this, &MyWidget::dealDone);

    //关闭窗口时,关闭启动的线程
    connect(this,&MyWidget::destroyed,this,&MyWidget::stopThread);

}
void MyWidget::dealTimeOut(){
    static int i = 0;
    i++;
    ui->lcdNumber->display(i);
}
void MyWidget::dealDone(){
    cout<<"it is over";
    myTimer->stop();
}
void MyWidget::stopThread(){
    //停止线程
    thread->quit();

    thread->wait();
}

MyWidget::~MyWidget()
{
    delete ui;
}


void MyWidget::on_pushButton_clicked()
{
    if(myTimer->isActive()==false){
        myTimer->start(100);
    }

#if 0
    //非常复杂的数据处理,耗时较长
    QThread::sleep(5);

    //处理完数据后,关闭定时器
    myTimer->stop();
    cout<<"out";
#endif

    //启动线程,处理数据
    thread->start();


}

mythread

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

protected:
    //QThread 的 虚函数
    //线程处理函数
    //不能直接调用,通过start()间接调用
    void run();

signals:
    void isDone();
};

#endif // MYTHREAD_H

#include "mythread.h"

MyThread::MyThread(QObject *parent)
    : QThread{parent}
{

}
void MyThread::run(){
    //非常复杂的数据处理,耗时较长
    //需要耗时5s
    sleep(5);

    //处理完数据后发送信号
    emit isDone();
}

第二种方式:

mywidget

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include "mythread.h"
#include <QThread>
#include <QCloseEvent>

QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget(QWidget *parent = nullptr);
    ~MyWidget();

    void dealSignal();
    void dealClose();

protected:
    void closeEvent(QCloseEvent *);

signals:
    void startThread();//启动子线程的信号

private slots:
    void on_btnStart_clicked();

    void on_btnStop_clicked();

private:
    Ui::MyWidget *ui;

    MyThread *myT;
    QThread *thread;
};
#endif // MYWIDGET_H

#include "mywidget.h"
#include "ui_mywidget.h"

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MyWidget)
{
    ui->setupUi(this);

    //动态分配空间,不能指定父对象
    myT = new MyThread;

    //创建子线程
    thread = new QThread(this);

    //把自定义线程加入到子线程中
    myT->moveToThread(thread);

    connect(myT,&MyThread::mySignal,this,&MyWidget::dealSignal);

    cout<<"主线程号:"<<QThread::currentThread();

    connect(this,&MyWidget::startThread,myT,&MyThread::myTimeOut);

    connect(this,&MyWidget::destroyed,this,&MyWidget::dealClose);
}
MyWidget::~MyWidget()
{
    delete ui;
}
void MyWidget::dealClose(){
    cout<<"close1";//不知道为什么不走。。
    on_btnStop_clicked();
    delete myT;
}
void MyWidget::dealSignal(){
    static int i = 0;
    i++;
    ui->lcdNumber->display(i);
}
void MyWidget::closeEvent(QCloseEvent *){
    cout<<"close2";
    on_btnStop_clicked();
    delete myT;
}



void MyWidget::on_btnStart_clicked()
{
    if(thread->isRunning()==true){
        return;
    }

    //启动线程,但是没有启动线程处理函数
    thread->start();
    myT->setFlag(false);

    //不能直接调用线程函数,
    //直接调用,导致:线程处理函数和主线程是同一个线程。
    //myT->myTimeOut();

    //只能通过 signal-slot 方式调用
    emit startThread();
}


void MyWidget::on_btnStop_clicked()
{
    if(thread->isRunning()==false){
        return;
    }

    myT->setFlag(true);
    thread->quit();
    thread->wait();
}

mythread

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void myTimeOut();

    void setFlag(bool flag = true);

signals:
    void mySignal();
private:
    bool isStop;
};

#endif // MYTHREAD_H
#include "mythread.h"
#include <QThread>

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

MyThread::MyThread(QObject *parent)
    : QObject{parent}
{
    isStop = false;
}

void MyThread::myTimeOut(){
    while(isStop == false){
        QThread::sleep(1);
        emit mySignal();

        cout<<"子线程号:"<<QThread::currentThread();
         if(isStop)
         {
             break;
         }
    }
}
void MyThread::setFlag(bool flag){
    isStop = flag;
}

线程画图

widget

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<mythread.h>
#include<QThread>
#include<QImage>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    //重写绘图事件
    void paintEvent(QPaintEvent *);

    void getImage(QImage);//槽函数

    void dealClose();

protected:
    //void closeEvent(QCloseEvent *);

private:
    Ui::Widget *ui;

    QImage image;
    MyThread *myT;
    QThread *thread;
};
#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include<QPainter>

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myT = new MyThread;
    thread = new QThread(this);

    myT->moveToThread(thread);

    //启动子线程,但是并没有启动线程处理函数
    thread->start();

    //线程处理函数,必须通过signal-slot调用
    connect(ui->pushButton,&QPushButton::pressed,myT,&MyThread::drawImage);

    connect(myT,&MyThread::updateImage,this,&Widget::getImage);

    connect(this,&Widget::destroyed,[=](){
        cout<<"close1";

        if(thread->isRunning()==false){
            return;
        }

        thread->quit();
        thread->wait();
        delete myT;
    });

    //connect(this,&Widget::destroyed,this,&Widget::dealClose);//只有这种方式不行。
}

Widget::~Widget()
{
    delete ui;
}

void Widget::getImage(QImage temp){
    image = temp;
    update();
}

void Widget::paintEvent(QPaintEvent *e){
    QPainter p(this);
    p.drawImage(50,50,image);
}

/*
void Widget::closeEvent(QCloseEvent *){
    cout<<"close3:";
    if(thread->isRunning()==false){
        return;
    }

    thread->quit();
    thread->wait();
    delete myT;
}
*/

void Widget::dealClose(){
    cout<<"close2";
    if(thread->isRunning()==false){
        return;
    }

    thread->quit();
    thread->wait();
    delete myT;
}

mythread

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include<QImage>

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

    //线程处理函数
    void drawImage();

signals:
    void updateImage(QImage temp);
};

#endif // MYTHREAD_H

#include "mythread.h"
#include<QPainter>
#include<QPen>
#include<QBrush>
#include<cstdlib>

MyThread::MyThread(QObject *parent)
    : QObject{parent}
{

}

void MyThread::drawImage(){
    //定义QImage绘图设备
    QImage image(500,500,QImage::Format_ARGB32);
    //定义画家,指定绘图设备
    QPainter p(&image);

    //定义画笔对象
    QPen pen;
    pen.setWidth(5);
    p.setPen(pen);
    QBrush brush;
    brush.setStyle(Qt::SolidPattern);
    brush.setColor(Qt::red);
    p.setBrush(brush);

    //定义5个点
    QPoint a[]{
        QPoint(rand()%500,rand()%500),
        QPoint(rand()%500,rand()%500),
        QPoint(rand()%500,rand()%500),
        QPoint(rand()%500,rand()%500),
        QPoint(rand()%500,rand()%500)
    };

    p.drawPolygon(a,5);

    //通过信号发送图片
    emit updateImage(image);
}

数据库

MySQL需要输入账号密码,还需要连接的驱动文件。

SQLite比较简单,本地新建个后缀db的文件即可。

QT += sql
#include "widget.h"
#include "ui_widget.h"
#include<QSqlDatabase>
#include <QMessageBox>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariantList>

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //打印QT支持的数据库驱动
    cout<<QSqlDatabase::drivers(); // out: QList("QSQLITE", "QODBC", "QPSQL")

    //添加sqlite数据库
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    //设置数据库 SQLite
    db.setDatabaseName("../info.db");

    //连接数据库 MySql
    /*
    db.setHostName("127.0.0.1"); //数据库服务器IP
    db.setUserName("root"); //数据库用户名
    db.setPassword("123456"); //密码
    db.setDatabaseName("info"); //使用哪个数据库
    */

    //打开数据库
    if( !db.open() ){
        QMessageBox::warning(this,"error",db.lastError().text());
    }

    QSqlQuery query;

    // == 创建 ==
    query.exec("create table student(id int primary key, name verchar(255), age int, score int);");

    query.prepare("insert into student(name, age, score) values(?, ?, ?)");
    //给字段设置内容 list
    QVariantList nameList;
    nameList << "xiaoming" << "xiaolong" << "xiaojiang" ;
    QVariantList ageList;
    ageList << 11 << 22 << 33;
    QVariantList scoreList;
    scoreList << 59 << 69 << 79;
    //给字段绑定相应的值 按顺序绑定
    query.addBindValue(nameList);
    query.addBindValue(ageList);
    query.addBindValue(scoreList);
    //执行预处理命令
    query.execBatch();

    // == 查询 ==
    query.exec("select * from student");
    //一行一行遍历
    while(query.next()){
        //取出当前行的内容
        qDebug() << query.value(0).toInt()
                 << query.value(1).toString()
                 << query.value("age").toInt()
                 << query.value("score").toInt();
    }
}

Widget::~Widget()
{
    delete ui;
}

可视化操作

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QSqlTableModel>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btnAdd_clicked();

    void on_btnConfirm_clicked();

    void on_btnCancel_clicked();

    void on_btnDelete_clicked();

    void on_btnFind_clicked();

private:
    Ui::Widget *ui;
    QSqlTableModel *model;
};
#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include<QSqlDatabase>
#include <QMessageBox>
#include <QSqlError>
#include<QSqlRecord>


#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //打印QT支持的数据库驱动
    cout<<QSqlDatabase::drivers(); // out: QList("QSQLITE", "QODBC", "QPSQL")

    //添加sqlite数据库
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    //设置数据库 SQLite
    db.setDatabaseName("../info.db");

    //打开数据库
    if( !db.open() ){
        QMessageBox::warning(this,"error",db.lastError().text());
    }

    //设置模型
    model = new QSqlTableModel(this);
    model->setTable("student");

    //把model放在view
    ui->tableView->setModel(model);

    //显示model里的数据
    model->select();

    //设置 头部 显示
    model->setHeaderData(0,Qt::Horizontal,"学号");

    //设置model的编辑模式  手动提交修改
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);

    //设置view中的数据库不允许修改
    //ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);

}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_btnAdd_clicked()
{
    //添加空记录
    QSqlRecord record = model->record();//获取空记录
    //获取行号
    int row = model->rowCount();
    model->insertRecord(row,record);
}


void Widget::on_btnConfirm_clicked()
{
    model->submitAll();//提交动作
}


void Widget::on_btnCancel_clicked()
{
    model->revertAll();//取消所有动作
    model->submitAll();//提交动作
}


void Widget::on_btnDelete_clicked()
{
    //获取选中的模型
    QItemSelectionModel *sModel = ui->tableView->selectionModel();
    //取出模型中的索引
    QModelIndexList list = sModel->selectedRows();
    //删除所有选中的行
    for(int i=0; i<list.size();i++){
        model->removeRow(list.at(i).row());
    }
}


void Widget::on_btnFind_clicked()
{
    QString name = ui->lineEdit->text();
    QString str = QString("name = '%1'").arg(name);
    model->setFilter(str);
    model->select();
}

通用代码

#include<QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值