Qt实现完全按键控制Ui的简单框架
2014-04-16 17:18
1226 查看
近期在做一个项目,就学了一下qt的编程技术。由于完全没有触摸屏和鼠标控制,所以本项目中所有的ui控制都是需要通过按键操作实现的。
既然重点是关于qt的按键控制,我们就需要了解下qt的按键事件处理规则。首先看下C++ GUI Qt4这本书中对于事件处理的描述:
事件(event)是由窗口系统或者Qt自身产生的,用以响应所发生的各类事情。当用户按下或者松开键盘或者鼠标上的按键时,就可以产生一个键盘或者鼠标事件;当某个窗口第一次显示的时候,就会产生一个绘制事件,用来告知系统需要重新绘制它本身,从而使该窗口可见。大多数事件是作为用户动作产生的响应产生的,但是也有一些例外,比如定时器事件,则是由系统独立产生的。
在使用Qt进行编程开发时,基本不需要考虑事件,因为在发生某些重要的事情时,Qt窗口部件都会发射信号。但是当我们需要编写自己的自定义窗口部件,或者希望改变已经存在的Qt窗口部件的行为时,事件就变得非常有用了。
不应该混淆“事件”和”信号“这两个概念。一般情况下,在使用窗口部件的时候,信号是十分重要的;而在实现窗口部件时,事件则是十分有用的。例如,当使用QPushButton时,我们对于它的clicked()信号往往更为关注,而很少关心促成发射该信号的鼠标或者键盘事件。但是,如果要实现的是一个类似于QPushButton的类,就需要编写一定的处理 鼠标和键盘事件的代码,而且在必要的时候,还需要发射clicked()信号。
从上面的这三段话中简单总结一下:
1. 事件用以响应所发生的各类事件,大多数是由用户动作产生的,也有例外,是由系统自动产生的;
2. 使用组件,基本不考虑事件,在于信号的使用;与之相反,实现组件重点在于事件的处理。
下面根据我的需要,所以这里面需要重新定义系统提供的组件和实现对各组件进行整体控制的框架。为什么?我们应该从鼠标和触摸屏为我们提供了那些功能来考虑,这里主要考虑的一个功能就是鼠标和触摸屏可以方便的实现焦点的移动,这就需要通过按键可以实现,当然tab键可以实现,但是使用不是很好,所以最好的方案就是能通过上下左右键进行灵活控制。
1. 使用上下左右键,需重定义系统组件,使其向父窗口抛出,保证上下左右按键的捕获。
2. 父窗口捕获按键之后,执行响应处理,确定焦点的跳转方向,实现焦点的转移。
自定义组件示例
StyleRadioButton.h
StyleRadioButton.cpp:
所有支持按键控制widget的基类
PageBase.h
PageBase.cpp
简单说下大概思路:对于需要使用按键控制gui的组件首先需要将其设为StrongFocus属性,窗体需继承这个基类。在调用之后,首先应使用AnalizyAllFocusWidget()对所有焦点属性为StrongFocus的组件进行排序,排序的标准就是根据他们的坐标位置。
对于希望实现按键控制gui的操作的大概思路就是这样,这里面这段代码实在实际的项目使用的,这里面有很多比较好的功能没有介绍,比如,多窗口->进入和退出的焦点问题,还有虚函数显示和关闭函数的使用技巧等。但是这里面也有问题,在我的遇见看来,一般情况下应该不会涉及到的。还有也许有人不知道自定义组件如何使用的。
如需要进一步的了解,可以留言,虽然能力不高,但我会针对问题仔细进行回复的。
既然重点是关于qt的按键控制,我们就需要了解下qt的按键事件处理规则。首先看下C++ GUI Qt4这本书中对于事件处理的描述:
事件(event)是由窗口系统或者Qt自身产生的,用以响应所发生的各类事情。当用户按下或者松开键盘或者鼠标上的按键时,就可以产生一个键盘或者鼠标事件;当某个窗口第一次显示的时候,就会产生一个绘制事件,用来告知系统需要重新绘制它本身,从而使该窗口可见。大多数事件是作为用户动作产生的响应产生的,但是也有一些例外,比如定时器事件,则是由系统独立产生的。
在使用Qt进行编程开发时,基本不需要考虑事件,因为在发生某些重要的事情时,Qt窗口部件都会发射信号。但是当我们需要编写自己的自定义窗口部件,或者希望改变已经存在的Qt窗口部件的行为时,事件就变得非常有用了。
不应该混淆“事件”和”信号“这两个概念。一般情况下,在使用窗口部件的时候,信号是十分重要的;而在实现窗口部件时,事件则是十分有用的。例如,当使用QPushButton时,我们对于它的clicked()信号往往更为关注,而很少关心促成发射该信号的鼠标或者键盘事件。但是,如果要实现的是一个类似于QPushButton的类,就需要编写一定的处理 鼠标和键盘事件的代码,而且在必要的时候,还需要发射clicked()信号。
从上面的这三段话中简单总结一下:
1. 事件用以响应所发生的各类事件,大多数是由用户动作产生的,也有例外,是由系统自动产生的;
2. 使用组件,基本不考虑事件,在于信号的使用;与之相反,实现组件重点在于事件的处理。
下面根据我的需要,所以这里面需要重新定义系统提供的组件和实现对各组件进行整体控制的框架。为什么?我们应该从鼠标和触摸屏为我们提供了那些功能来考虑,这里主要考虑的一个功能就是鼠标和触摸屏可以方便的实现焦点的移动,这就需要通过按键可以实现,当然tab键可以实现,但是使用不是很好,所以最好的方案就是能通过上下左右键进行灵活控制。
1. 使用上下左右键,需重定义系统组件,使其向父窗口抛出,保证上下左右按键的捕获。
2. 父窗口捕获按键之后,执行响应处理,确定焦点的跳转方向,实现焦点的转移。
自定义组件示例
StyleRadioButton.h
#ifndef STYLERADIOBUTTON_H #define STYLERADIOBUTTON_H #include <QRadioButton> class StyleRadioButton : public QRadioButton { Q_OBJECT public: explicit StyleRadioButton(QWidget *parent = 0); protected: void keyPressEvent(QKeyEvent *e); }; #endif // STYLERADIOBUTTON_H
StyleRadioButton.cpp:
#include "StyleRadioButton.h" #include <QKeyEvent> StyleRadioButton::StyleRadioButton(QWidget *parent) : QRadioButton(parent) { } void StyleRadioButton::keyPressEvent(QKeyEvent *e) { switch(e->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: e->ignore(); break; default: QRadioButton::KeyPressEvent(e); } }
所有支持按键控制widget的基类
PageBase.h
#ifndef PAGEBASE_H #define PAGEBASE_H #include <QWidget> class PageBase : public QWidget { Q_OBJECT public: PageBase(QWidget *parent = 0); virtual void FShow(); virtual void FClose(); public slots: void focusBack(); signals: void closed(); void Show(); protected: void keyPressEvent(QKeyEvent *); void AnalizyAllFocusWidget(); PageBase * sparent; private: void findStrongWdToList(const QObjectList &objList); void addToList(QWidget *wgt,QPoint pos); QList< QPair<QWidget *,QPoint> > focuschildList; QWidget * findWgtByPos(QPoint pos); bool isAlreadyin(QWidget * c); QPoint currentIndex; void nextHFocus(); void preHFocus(); void nextVFocus(); void preVFocus(); void ReturnClick(); void FindNext(QPoint pos); void FindNext(int x,int y); }; #endif // SETTINGPAGEBASE_H
PageBase.cpp
#include "PageBase.h" #include <QPushButton> #include <QKeyEvent> PageBase::PageBase(QWidget *parent) : QWidget(parent), sparent(NULL), currentIndex(-1,-1) { if(parent && parent->inherits("PageBase")) { sparent = qobject_cast<PageBase *>(parent); } } void PageBase::FShow() { this->show(); this->setFocus(); this->focusBack(); } void PageBase::FClose() { emit closed(); this->close(); if(sparent != NULL) sparent->FShow(); } /* 上下左右键焦点切换的使用 */ void PageBase::keyPressEvent(QKeyEvent *ev) { if(!this->isVisible()) { ev->ignore(); return ; } switch(ev->key()) { case Qt::Key_Up: this->preVFocus(); ev->accept(); break; case Qt::Key_Down: this->nextVFocus(); ev->accept(); break; case Qt::Key_Right: this->nextHFocus(); ev->accept(); break; case Qt::Key_Left: this->preHFocus(); ev->accept(); break; case Qt::Key_Tab: ev->accept(); break; /* 这里涉及widget的统一的退出处理 */ case Qt::Key_Escape: this->FClose(); ev->accept(); break; /* enter键产生单击事件 */ case Qt::Key_Return: ReturnClick(); ev->accept(); break; default: ev->accept(); } } void PageBase::focusBack() { if(this->currentIndex != QPoint(-1,-1)) { QWidget * tmp = findWgtByPos(currentIndex); if(tmp != NULL) { tmp->setFocus(); } } } void PageBase::ReturnClick() { QWidget * tmp = findWgtByPos(currentIndex); if(tmp != NULL) { if(tmp->inherits("QPushButton")) { QPushButton *tb = qobject_cast<QPushButton *>(tmp); if(tb) { emit tb->click(); } } } } void PageBase::addToList(QWidget *wgt,QPoint pos) { if(isAlreadyin(wgt)) return; focuschildList.append(qMakePair(wgt,pos)); } bool PageBase::isAlreadyin(QWidget * c) { QPair<QWidget*,QPoint> pw; foreach(pw,focuschildList) { if(pw.first == c) return true; } return false; } QWidget * PageBase::findWgtByPos(QPoint pos) { QPair<QWidget*,QPoint> pw; foreach(pw,focuschildList) { if(pw.second == pos) return pw.first; } return NULL; } static bool GoodsListLessThan(QPair<QWidget *,QPoint> &m1, QPair<QWidget *,QPoint> &m2) { if(m1.first->y() - m2.first->y() < -10 ) { return true ; } else if(m1.first->y() - m2.first->y() > 10) { return false; } else { if(m1.first->x() < m2.first->x()) { return true; } else { return false; } } } /* 对widget中所有焦点属性为StrongFocus的组件进行排序 * 放入相应的list中 */ void PageBase::AnalizyAllFocusWidget() { focuschildList.clear(); foreach(QObject * obj,children()) { if(obj->isWidgetType()) { QWidget * tmp = qobject_cast<QWidget *>(obj); if(tmp->children().isEmpty()) { if(tmp->focusPolicy() == Qt::StrongFocus) { addToList(tmp,QPoint(0,0)); } } else { foreach(QObject * ccobj,tmp->children()) { QWidget * ccwgt = qobject_cast<QWidget *>(ccobj); if(ccwgt->focusPolicy() == Qt::StrongFocus) { addToList(ccwgt,QPoint(0,0)); } } } } } qSort(focuschildList.begin(),focuschildList.end(),GoodsListLessThan); int indexx = 0; int indexy = 0; int count = focuschildList.count(); for(int i = 0;i < count;i++) { if(i < count -1 && focuschildList[i].first->y() - focuschildList[i+1].first->y() < -10) { focuschildList[i].second = QPoint(indexx,indexy); indexy++; indexx = 0; } else { focuschildList[i].second = QPoint(indexx,indexy); indexx ++; } if(focuschildList[i].first->hasFocus()) { currentIndex =focuschildList[i].second; } } if((currentIndex.x() < 0 || currentIndex.y() < 0 )&& focuschildList.count() > 0) { focuschildList[0].first->setFocus(); currentIndex = QPoint(0,0); this->focusBack(); } } /* 找出widget所有属性为StrongFocus的控件,放入list中 */ void PageBase::findStrongWdToList(const QObjectList &objList) { foreach(QObject * obj, objList) { if(obj->isWidgetType()) { QWidget * tmp = qobject_cast<QWidget *>(obj); if(tmp->children().isEmpty()) { if(tmp->focusPolicy() == Qt::StrongFocus) { addToList(tmp,QPoint(0,0)); } } else findStrongWdToList(tmp->children()); } } } void PageBase::FindNext(int x,int y) { FindNext(QPoint(x,y)); } void PageBase::FindNext(QPoint pos) { if(pos.manhattanLength() != 1) return; QPoint nextFocus = currentIndex+pos; if(pos.x() == 0 && pos.y() != 0) { nextFocus.setX(0); } QWidget *tmp = findWgtByPos(nextFocus); if( tmp!= NULL) { tmp->setFocus(); currentIndex = nextFocus; } } void PageBase::nextHFocus() { FindNext(1,0); } void PageBase::preHFocus() { FindNext(-1,0); } void PageBase::nextVFocus() { FindNext(0,1); } void PageBase::preVFocus() { FindNext(0,-1); }
简单说下大概思路:对于需要使用按键控制gui的组件首先需要将其设为StrongFocus属性,窗体需继承这个基类。在调用之后,首先应使用AnalizyAllFocusWidget()对所有焦点属性为StrongFocus的组件进行排序,排序的标准就是根据他们的坐标位置。
对于希望实现按键控制gui的操作的大概思路就是这样,这里面这段代码实在实际的项目使用的,这里面有很多比较好的功能没有介绍,比如,多窗口->进入和退出的焦点问题,还有虚函数显示和关闭函数的使用技巧等。但是这里面也有问题,在我的遇见看来,一般情况下应该不会涉及到的。还有也许有人不知道自定义组件如何使用的。
如需要进一步的了解,可以留言,虽然能力不高,但我会针对问题仔细进行回复的。
相关文章推荐
- iOS微信QQ聊天界面的UI框架以及Socket简单实现群聊功能
- CI框架中通过hook的方式实现简单的权限控制
- 利用lay-ui结合ajax实现分页功能(不借助框架,简单易懂)
- 一个简单androidUI框架的实现
- 引脚太少,想多接几个按键控制怎么办?-----谈单片机AD检测法实现简单实用的按键
- angular JS 基于ionic框架 开发移动端项目 实现进入前台 判断用户权限 控制项目UI布局和tab的部门显示和隐藏
- 在开发板S3C2440中如何用自带的QT去设计一个界面来实现控制摄像头,这个界面的框架图是怎样的
- CI框架中通过hook的方式实现简单的权限控制
- 【Unity3D技巧】一个简单的Unity-UI框架的实现
- QT类学习系列(6)- Qt多线程的简单实现以及不能同时处理UI的操作
- Unity3D问题之简单UI框架设计和实现
- ios开发UI基础—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- 构建插件式的应用程序框架(八)----视图服务的简单实现
- 搭建Apache Mina框架并实现Server与Client端的简单消息传递
- 简单QT应用了可实现手动布局QT应用
- 自己动手实现简单权限控制
- QT实现一个简单的计算器
- 运用Struts1框架来简单实现登陆跳转
- Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)
- 用Maven整合SpringMVC+Spring+Hibernate 框架,实现简单的插入数据库数据功能