QT初探

源文来自http://zhilong2382.blog.163.com/blog/static/74804221200971911552664/

实例一:  在QT上点亮LED灯

通过这个小例子说明怎样通过QT启动另一个程序,LED灯程序名为led-player,这是开发板上已经有的,下面开始:
我们首先把QTE-4.5.1移植到ARM板上,方法可参照我前面的文章,移植好后,我们就开始QT程序设计,首先创建一个按钮,
用于启动LED程序,先创建Qtled.h头文件吧,看它到底有什么功能,
#ifndef QTLED_H
#define QTLED_H
#include <QWidget>

class Qtled : public QWidget  //我们定义一个Qtled类,继承于QWidget。
{
  Q_OBJECT
 public:
Qtled();
~Qtled() {};                //析构

public slots:              //我们自定义的槽
 void runLed();

private:
 void startLed();
};
#endif
  从头文件中我们看到了有runled()和startled()两个函数,这两个函数到底是干什么呢?我们再来看看Qtled.cpp的具体内容,
#include <QPushButton>
#include <QApplication>
#include <unistd.h>
#include <sys/types.h>
#include "Qtled.h"

Qtled ::Qtled():QWidget()
{
    startLed();
}

void Qtled::startLed()
{
 QPushButton* quit = new QPushButton("LED",this);      //创建一个按钮,
 connect(quit,SIGNAL(clicked()),this,SLOT(runLed()));  //如果按钮被点击,即发送clicked()信号给槽;
}

void Qtled::runLed()
{
pid_t pid=fork();   //建立一个新的进程
if(pid==0)          //如果PID为0,即为子进程,这里为子进程所要做的具体事,
{
qDebug("new start.PID is : %d/n",getpid());
int result=system("/led/led-player");//执行程序
if(result<0)                           //一般不会执行到这里
{
qCritical("failed to execl led!/n");
}
_exit(-1);    //退出,但不做清理工作
}
else if(pid>0)    //如果PID>0,则为父进程,这里为父进程所要做的事,由于没什么可做,就让它打印一个语句吧。
{
qDebug("I am parent /n");
}
}
可以看出startLed是用来创建按钮的,并通过这个按钮来连接runLed(),即信号与槽的连接,槽主要是叉分出一个进程,让它去执行LED程序,
即达到了在QT上启动另一个程序的目的,^-^ ,反正我是这样实现的,肯定还有其它更好的方法,呵呵...
最后再写个main.cpp就可以完工了:
#include <QApplication>
#include "Qtled.h"

int main(int argc,char *argv[])
{
 QApplication app(argc,argv);
 Qtled lightled;
 lightled.show();
 return app.exec();
}
由于我们是在ARM板上运行,所以要指定qmake了,打开一个终端,cd到当前目录,设置环境变量:
export LD_LIBRARY_PATH=/usr/local/Trolltech/QtEmbedded-4.5.1-arm/lib:
执行 /usr/local/Trolltech/QtEmbedded-4.5.1-arm/bin/qmake -project
再执行 /usr/local/Trolltech/QtEmbedded-4.5.1-arm/bin/qmake
最后就是我们熟悉的 make 了;
打开ARM板,通过NFS把程序放到板上(NFS方法可以参照我前面的文章),在板上建目录 mkdir led
并把led-player程序放到led中,以便QT调用时能找到该程序并执行;
最后执行 ./qtled -qws 呵呵,画面出来了吧,点按钮看看,LED是不是亮起来了?呵呵是不是有感觉了,有的话继续来看
下一个例子。
实例二:  QT串口与51单片机通信

 

通过这个小例子主要想说明QT怎样进行线程编程的思想,实例如图,好吧,下面是过程
上一个例子我们采用的是手工编写代码的方法,这个例子我们来玩一下designer,其实Qt4己经把界面与功能分开了,用designer来进行界面
设计,再手工编写一些功能,如信号与槽,这样开发效率会大大提高,呵呵,开一个终端,输入/usr/local/Trolltech/Qt-4.5.1/bin/designer
,如果第一次打开出现字体不对,可以打开qtconfig进行一些相关配置,打开后我们新建一个Main Window,在右边的属性框中设置一下界面大小,
我ARM板的LCD大小为320x240,所以我也设为320x240;左边是一些我们常用的窗口部件,这里我们用到一个lable标签来做显示,再放几个
pushButton按钮,在属性objectName重新更改它的名字,改为我们记得的,这样在写功能时记得哪个按钮叫什么名字,对于一个初学QT的
人来说,很想知道每一个部件到底有什么信号和槽,别急,我们可以这样来看,选中一个lable,按F4,再点击lable拖动出现接地符号时松开
,弹出编辑信号与槽,这时左边列出的是信号,右边为槽,这里我们不用配置连接,等下我们再手工写,最后我们用到一个lable标签和三个pushButton
按钮,并命名为dis_label、writeButton、readButton、closeButton,然后保存为mainwindow.ui,这样designer就完工了,呵呵..
  下面我们编写一个线程,用于管理串口收发工作,它不涉及到任何界面,只做好它的本份工作就得了,编写一个thread.h文件 gedit thread.h,
#ifndef THREAD_H
#define THREAD_H
#include<QThread>

class Thread:public QThread
{
  Q_OBJECT
 public:
  Thread();  
  char buf[128];
  volatile bool stopped; 
  volatile bool write_rs;
  volatile bool read_rs;

 protected:
  virtual void run();
};
#endif
我们定义一个Thread类,它继承于QThread,看到只设有一些变量和一个run函数,virtual表示为虚函数,你也可以去掉,加上去会增加一些内存开销,
但提高了效率,对于这个小程序是看不出什么效果的,volatile这个大家都懂了吧,就是防止偷懒,呵呵,再看看thread.cpp
#include"thread.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>    //串口用到的
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>

#define BAUDRATE B9600
//#define RS_DEVICE "/dev/ttyS0"       //串口1
#define RS_DEVICE "/dev/ttySAC1"       //串口1

Thread::Thread()
{}                                                 //析构

void Thread::run()          //这就是线程的具体工作了
{
 int fd,c=0,res;
struct termios oldtio,newtio;     //termios结构是用来保存波特率、字符大小等

printf("start.../n");

fd=open(RS_DEVICE,O_RDWR|O_NOCTTY);     //以读写方式打开串口。不控制TTY
if(fd<0)
{
perror("error");
exit(1);                             //失败退出
}

printf("Open.../n");

tcgetattr(fd,&oldtio);             //保存当前设置到oldtio
bzero(&newtio,sizeof(newtio));     //清除newtio结构,并重新对它的成员设置如下

newtio.c_cflag=BAUDRATE|CS8|CLOCAL|CREAD;  //9600、8位、忽略DCD信号、启用接收装置
newtio.c_iflag|=IGNPAR;                    //忽略奇偶
newtio.c_oflag=0;
newtio.c_lflag=0;  

newtio.c_cc[VMIN]=0;      
newtio.c_cc[VTIME]=100;                   //在规定时间(VTIME)内读取(VMIN)个字符;
tcflush(fd,TCIFLUSH);                    //清除所有队列在串口的输入与输出;
tcsetattr(fd,TCSANOW,&newtio);           //把我们的设置写入termios

while(stopped)                          //stopped为0时将退出线程
{
 if(write_rs)                           //write_rs为1时把字符串从串口中输出
{
write_rs=0;
write(fd,"QtEmbedded-4.5.1",16);
}

 if(read_rs)                           //read_rs为1时读取,并存在buf
{
read_rs=0;
res=read(fd,buf,10);
buf[res]=0;
emit finished();                      //读完后发一个信号
}

}
printf("Close.../n");
tcsetattr(fd,TCSANOW,&oldtio);      //重新设置回原来的
close(fd);
quit();                            
}
QT有terminated()和wait函数来停止或暂停线程,为什么不用呢,具书上说会造成阻塞什么的,呵呵,我也不懂,所以我就用stopped变量来控制,让
它死循环在那里,其它我已经在注释上说明了,应该没什么问题了吧,没有的话再写一个mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include<QtGui>
#include"ui_mainwindow.h"    //奇怪?这个头文件从哪里来的?呵呵,刚才用designer做的mainwindow.ui文件,经过make后会生成这个头文件, 
#include"thread.h"           //把我们前面定义的线程包含进来

class MainWindow:public QMainWindow,public Ui::MainWindow  //多继承
{
  Q_OBJECT
 public:
  MainWindow(QWidget *parent=0);
 public slots:
  void writeThread();
  void readThread();
  void closeThread();
  void display();
 
 private:
  Thread *yy;
};
#endif
MainWindow继承于QMainWindow和MainWindow,即多继承,对于不是很复杂的程序,用多继承是一个较好的方法,如果程序较复杂,还是用单继承了,
我们再看一下mainwindow.cpp,看这些槽具有什么功能:
#include"mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
  :QMainWindow(parent)
{
  setupUi(this);  
  yy = new Thread;
  yy->start();          //启动线程
  yy->stopped=1;        //初始化变量
  yy->write_rs=0;
  yy->read_rs=0;
    
  connect(writeButton,SIGNAL(clicked()),this,SLOT(writeThread()));      //信号与槽
  connect(readButton,SIGNAL(clicked()),this,SLOT(readThread()));
  connect(closeButton,SIGNAL(clicked()),this,SLOT(closeThread()));
  connect(yy,SIGNAL(finished()),this,SLOT(display()));      //前面线程读完了不是发一个信号么,这个信号就是发到这个槽
}

void MainWindow::display()
{
dis_label->setText(yy->buf);     //读到的在dis_label显示,dis_label就是我们前面designer放的标签,
}

void MainWindow::writeThread()  //前面线程都是根据stopped、write_rs、read_rs的状态来工作的^_^
{
  yy->write_rs=1;
}

void MainWindow::readThread()
{
  yy->read_rs=1;
}

void MainWindow::closeThread()
{
yy->stopped=0;
}
还差个main.cpp就完工了,哈哈...
#include<QApplication>
#include"mainwindow.h"

int main(int argc,char *argv[])
{
  QApplication app(argc,argv);
  MainWindow mw;
  mw.show();
  return app.exec();
}
啊!终于完成了,不!这只是完成了ARM这边,还有MCU这边,呵呵,很久不玩单片机了,下面为AT89S51编一个串口程序,并用1602来显示:
/**************************************************************/ 
/*              三贱客龙 & 07机电(1)班                                 */               
/*               目标器件  AT89S51                            */                   
/*                晶振  11.0592 MHZ                     */               
/*                编译环境  keil uVsion3                 */                       
/*               适用于51综合开发实验板                  */                       
/**************************************************************/ 
/**********************  1602  ********************************/ 
/**************一定要关了上拉电阻再下载,有干拢****************/ 
#include <reg51.h> 
#include <intrins.h> 
 
typedef unsigned char BYTE;      //类型定义,编译时再处理 
typedef bit BOOL ;               //位 
/****************串口部份**********************************/ 
unsigned char key_s, key_v, tmp; 
sbit    K1 = P1^4; 
BOOL flag1=0; 
BYTE i=0; 
BYTE z=0; 
 
void isr_uart(void);                    //串口中断服务函数 
void send_str();                         // 传送字串 
bit    scan_key();                             // 扫描按键 
void proc_key();                         // 键处理 
void delayms(unsigned char ms); 
 
com_init()                               //串口初始部分 
{ 
   TMOD = 0x20;        // 定时器1工作于8位自动重载模式, 用于产生波特率方式2 
    TH1 = 0xFD;                // 波特率9600 
    TL1 = 0xFD; 
    SCON = 0x50;            // 设定串行口工作方式为1且没有校验位 
    PCON=0X00;            // 波特率不倍增,倍增时为0x80 
    TR1 = 1;                // 启动定时器1运行 
    ET1 = 0;                // 禁T1定时器产生中断 
    ES=1;                   //允串口中断 
    EA=1;                   //开总中断      
} 
/****************************串口部分************************************/ 
sbit rs    = P2^0;                  // 寄存器选择 
sbit rw = P2^1;                   //读写信号线 
sbit ep = P2^2;                 //使能端 
sbit right = P2^6;          //背光
char idata display[16] = {"zhilong2382@163"};  //开机时显示的字符串        
char code str[] = {"xxxI Love you ! "};      //按K1时将发送这个内容,由于前3位对方总是收不到,这里用xxx干掉了,用串口调试助手也一样, 
delay(BYTE ms) 
{                            // 延时子程序 
    BYTE i; 
    while(ms--) 
    { 
        for(i = 0; i< 250; i++) 
        { 
            _nop_(); 
            _nop_(); 
            _nop_(); 
            _nop_(); 
        } 
    } 
} 
BOOL lcd_bz()                 //只检测最高位 
{                            // 测试LCD忙碌状态 
    BOOL result; 
    rs = 0;                   //指令寄存器 
    rw = 1;                   //读 
    ep = 1;                   //接收指令,为下降沿做准备 
    _nop_();               //空语句,延时约1US 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    result = (BOOL)(P0 & 0x80);     //取最高位 
    ep = 0;                         //执行命令 
    return result;     
} 
 /**************  写入指令数据到LCD  **************/ 
lcd_wcmd(BYTE cmd) 
{                         
    while(lcd_bz());        //忙时执行空语句 
    rs = 0; 
    rw = 0; 
    ep = 0; 
    _nop_(); 
    _nop_();     
    P0 = cmd; 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    ep = 1; 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    ep = 0;         
} 
lcd_pos(BYTE pos) 
{                            //设定显示位置 
    lcd_wcmd(pos | 0x80);    //指令8 
} 
lcd_wdat(BYTE dat)              //写入字符显示数据到LCD 
{                            //写入要显示的字符串 
    while(lcd_bz());        //读忙 
    rs = 1;                    //写数据命令 
    rw = 0; 
    ep = 0; 
    P0 = dat; 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    ep = 1; 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    _nop_(); 
    ep = 0;     
} 
lcd_init() 
{                            //LCD初始化设定 
    lcd_wcmd(0x38);            //指令6,不检测忙信号 
    delay(1); 
    lcd_wcmd(0x0c);            //指令4,显示开/关控制 
    delay(1); 
    lcd_wcmd(0x06);            //指令3,输入模式 
    delay(1); 
    lcd_wcmd(0x01);            //清除LCD的显示内容 
    delay(1); 
} 
/*****************************************main********************************/ 
main() 
{ 
    BYTE i,j;     j=0; 
    right=0;                //开背光 
    lcd_init();                // LCD             
    delay(10); 
    com_init();            // 初始化 串口 
  /*************** 显示内容及方式*********************/     
 
 while(1) 
    { 
        if(scan_key())        // 扫描按键    第一遍为真 
        { 
            delayms(10);            // 延时去抖动 
            if(scan_key())            // 再次扫描 
            { 
                key_v = key_s;        // 保存键值 
                proc_key();            // 键处理 
            } 
        }     
    lcd_wcmd(0x01);            //清除LCD的显示内容  
    delay(1); 
    lcd_pos(0);                // 设置显示位置为第一行的第3个字符     
     i = 0; 
    while(display[i] != '/0')    //是否到字符串末尾 
    {                        // 显示字符 
        lcd_wdat(display[i]); 
        i++; delay(70);         
    }     
   } 
} 
// 扫描按键 
bit scan_key()     
{ 
    key_s = 0x00; 
    key_s |= K1;            //字节与位运算改变的是最低位 
    return(key_s ^ key_v);     
} 
// 键处理 
void proc_key() 
{ 
    if((key_v & 0x01) == 0) 
    {            // K1按下 
     TI=1;            //send_str();        // 传送字串到PC 
    } 
} 
// 每次传送完一串字符 
void send_str() 
 
{ 
    unsigned char i = 0; 
    while(str[i] != '/0') 
    { 
        SBUF = str[i]; 
        while(!TI);                // 等特数据传送,发完后TI=1 
        TI = 0;                    // 清除数据传送标志 
        i++;                    // 下一个字符 
    }     
} 
// 延时子程序 
void delayms(unsigned char ms)     
{                         
    unsigned char i; 
    while(ms--) 
    { 
        for(i = 0; i < 120; i++); 
    } 
} 
void isr_uart(void) interrupt 4           //串口服务函数 
{     
      if(RI==1) 
      { RI=0; 
      display[z]=SBUF; 
           z++; 
       if(z>=16) 
        z=0;     
             } 
       if(TI==1) 
       send_str();              
} 
呵呵,这个是我大一时学单片机时弄的,看起来乱乱的,但我保证绝对能用,程序编译后把hex文件烧到单片机,插上串口线,串口线我是自己做的,
注意有直通与交叉之分,一切准备好后,通电...怎么样,当在ARM板上点击write按钮时,将发送QtEmbedded-4.5.1给单片机,并在1602上显示
出来,点read后,再按下p1.4键时(按久点,因为我是用查询方法读键),单片机发送xxxI Love you !给ARM,ARM板上显示I Love you !,
怎么样,是不是更爽了,是的话我们再编一个复杂点的例子,编什么呢?PWM吧,可以用来控制电机的转速,我是学机电的,也就是我的专业是机电一体化,
是不是很奇怪学机电的也搞程序?哈哈..兴趣!!这个例子为在QT上可以改变频率、占空比,并有语音提示,还专门编了个数字小键盘方便输入,代码下次再贴了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值