您的位置:首页 > 产品设计 > UI/UE

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

#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的操作的大概思路就是这样,这里面这段代码实在实际的项目使用的,这里面有很多比较好的功能没有介绍,比如,多窗口->进入和退出的焦点问题,还有虚函数显示和关闭函数的使用技巧等。但是这里面也有问题,在我的遇见看来,一般情况下应该不会涉及到的。还有也许有人不知道自定义组件如何使用的。

如需要进一步的了解,可以留言,虽然能力不高,但我会针对问题仔细进行回复的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: