先前发的代码有问题,主要是当窗口达到最大和最小尺寸的时候窗口改变大小的实际效果不符合逻辑,现在修改以后没问题了。
下面我主要分享一下我的思考过程:
当我们改变一个窗口的大小时,如果窗口的宽度(高度)已经最小(最大),那么很显然只能放大(减小),同时拖动4个角应该可以同时改变宽度和高度,或者只改变宽度或高度(根据鼠标的位置和窗口的大小)。改变尺寸时的实际效果大概就是像上面描述的那样。
首先,鼠标移动到边缘的时候我们应当可以检测的到,同时改变鼠标的样式。这个思路就是计算鼠标的位置 和窗口上下左右的距离。你可以设置一个margin、或者设置4个margin,分别判断 鼠标是否落入你的边缘区域。在实际实验中我发现一定要给边缘空出距离,如果你的窗口全都被其它部件遮住了,那么在默认的情况下你的窗口是接收不到鼠标移动事件的(除非你给靠近边缘的部件开启鼠标追踪,显然很麻烦,而且开鼠标追踪是要消耗资源的)。
然后,我们将边缘进行分类,分为 上下左右 还有4个角,4个角看成是上下左右的组合,用枚举表示成这样。
enum {nodir,
top = 0x01,
bottom = 0x02,
left = 0x04,
right = 0x08,
topLeft = 0x01 | 0x04,
topRight = 0x01 | 0x08,
bottomLeft = 0x02 | 0x04,
bottomRight = 0x02 | 0x08} resizeDir; //更改尺寸的方向
这样分类的好处是我们可以检查是否包含水平分量、是否包含纵向分量,然后可以对宽度和高度进行独立的处理,就像这样:
if(resizeDir & top){ //检测更改尺寸方向中包含的上下左右分量
if(height() == minimumHeight()){
ptop = min(event->globalY(),ptop);
}
else if(height() == maximumHeight()){
ptop = max(event->globalY(),ptop);
}
else{
ptop = event->globalY();
}
}
else if(resizeDir & bottom){
if(height() == minimumHeight()){
pbottom = max(event->globalY(),ptop);
}
else if(height() == maximumHeight()){
pbottom = min(event->globalY(),ptop);
}
else{
pbottom = event->globalY();
}
}
if(resizeDir & left){ //检测左右分量
if(width() == minimumWidth()){
pleft = min(event->globalX(),pleft);
}
else if(width() == maximumWidth()){
pleft = max(event->globalX(),pleft);
}
else{
pleft = event->globalX();
}
}
else if(resizeDir & right){
if(width() == minimumWidth()){
pright = max(event->globalX(),pright);
}
else if(width() == maximumWidth()){
pright = min(event->globalX(),pright);
}
else{
pright = event->globalX();
}
}
处理完之后就是得到 窗口的矩形坐标,代码原理很简单:反正就是包含哪个能改变的分量就改变它。
再说说窗口拖动的实现:
窗口拖动的过程是这样的: 鼠标左键按下-> 鼠标移动(拖动)
我们操作的时候就这2个步骤,实际编程的时候要再细化一下,也就是在中间插一些步骤。首先我们想一下,窗口拖动大小是不变的,那么也就是只要确定左上角的坐标就行了。左上角的坐标可以利用 鼠标按住的位置和左上角的相对位置不变 来进行确定。 所以当左键按下的时候我们要记录一下 按下的位置和左上角的相对位置。移动以后我们利用鼠标移动的位置和相对位置来获得调整后的窗口位置,也就是 编程了4个过程:
鼠标左键按下-> 记录左上角和鼠标按下的相对位置->鼠标移动->利用鼠标移动后的位置和记录的相对位置计算 窗口位置
下面贴上 实现拖动和 改变窗口大小的完整代码:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
QPoint dragPosition; //鼠标拖动的位置
int edgeMargin; //鼠标检测的边缘距离
enum {nodir,
top = 0x01,
bottom = 0x02,
left = 0x04,
right = 0x08,
topLeft = 0x01 | 0x04,
topRight = 0x01 | 0x08,
bottomLeft = 0x02 | 0x04,
bottomRight = 0x02 | 0x08} resizeDir; //更改尺寸的方向
private:
void testEdge(); //检测鼠标是否接近窗口边缘
protected:
void mousePressEvent(QMouseEvent*event);
void mouseMoveEvent(QMouseEvent*event);
void mouseReleaseEvent(QMouseEvent*event);
};
#define min(a,b) ((a)<(b)? (a) :(b))
#define max(a,b) ((a)>(b)? (a) :(b))
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
edgeMargin = 4; //设置检测边缘为4
resizeDir = nodir; //初始化检测方向为无
setWindowFlags(Qt::FramelessWindowHint); //设置无边框
setMouseTracking(true); //开启鼠标追踪
setMinimumSize(1000,540);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent * event)
{
event->ignore();
if (event->button() == Qt::LeftButton) //每当按下鼠标左键就记录一下位置
{
dragPosition = event->globalPos() - frameGeometry().topLeft(); //获得鼠标按键位置相对窗口左上面的位置
}
}
void Widget::testEdge()
{
int diffLeft = abs(cursor().pos().x() - frameGeometry().left()); //计算鼠标距离窗口上下左右有多少距离
int diffRight = abs(cursor().pos().x() - frameGeometry().right());
int diffTop = abs(cursor().pos().y() - frameGeometry().top());
int diffBottom = abs(cursor().pos().y() - frameGeometry().bottom());
QCursor tempCursor; //获得当前鼠标样式,注意:只能获得当前鼠标样式然后再重新设置鼠标样式
tempCursor = cursor(); //因为获得的不是鼠标指针,所以不能这样用:cursor().setXXXXX
if(diffTop < edgeMargin){ //根据 边缘距离 分类改变尺寸的方向
if(diffLeft < edgeMargin){
resizeDir = topLeft;
tempCursor.setShape(Qt::SizeFDiagCursor);
}
else if(diffRight < edgeMargin){
resizeDir = topRight;
tempCursor.setShape(Qt::SizeBDiagCursor);
}
else{
resizeDir = top;
tempCursor.setShape(Qt::SizeVerCursor);
}
}
else if(diffBottom < edgeMargin){
if(diffLeft < edgeMargin){
resizeDir = bottomLeft;
tempCursor.setShape(Qt::SizeBDiagCursor);
}
else if(diffRight < edgeMargin){
resizeDir = bottomRight;
tempCursor.setShape(Qt::SizeFDiagCursor);
}
else{
resizeDir = bottom;
tempCursor.setShape(Qt::SizeVerCursor);
}
}
else if(diffLeft < edgeMargin){
resizeDir = left;
tempCursor.setShape(Qt::SizeHorCursor);
}
else if(diffRight < edgeMargin){
resizeDir = right;
tempCursor.setShape(Qt::SizeHorCursor);
}
else{
resizeDir = nodir;
tempCursor.setShape(Qt::ArrowCursor);
}
setCursor(tempCursor); //重新设置鼠标,主要是改样式
}
void Widget::mouseMoveEvent(QMouseEvent * event)
{
event->ignore();
if (event->buttons() & Qt::LeftButton){ //如果左键是按下的
if(resizeDir == nodir){ //如果鼠标不是放在边缘那么说明这是在拖动窗口
move(event->globalPos() - dragPosition);
}
else{
int ptop,pbottom,pleft,pright; //窗口上下左右的值
ptop = frameGeometry().top();
pbottom = frameGeometry().bottom();
pleft = frameGeometry().left();
pright = frameGeometry().right();
if(resizeDir & top){ //检测更改尺寸方向中包含的上下左右分量
if(height() == minimumHeight()){
ptop = min(event->globalY(),ptop);
}
else if(height() == maximumHeight()){
ptop = max(event->globalY(),ptop);
}
else{
ptop = event->globalY();
}
}
else if(resizeDir & bottom){
if(height() == minimumHeight()){
pbottom = max(event->globalY(),ptop);
}
else if(height() == maximumHeight()){
pbottom = min(event->globalY(),ptop);
}
else{
pbottom = event->globalY();
}
}
if(resizeDir & left){ //检测左右分量
if(width() == minimumWidth()){
pleft = min(event->globalX(),pleft);
}
else if(width() == maximumWidth()){
pleft = max(event->globalX(),pleft);
}
else{
pleft = event->globalX();
}
}
else if(resizeDir & right){
if(width() == minimumWidth()){
pright = max(event->globalX(),pright);
}
else if(width() == maximumWidth()){
pright = min(event->globalX(),pright);
}
else{
pright = event->globalX();
}
}
setGeometry(QRect(QPoint(pleft,ptop),QPoint(pright,pbottom)));
}
}
else testEdge(); //当不拖动窗口、不改变窗口大小尺寸的时候 检测鼠标边缘
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
event->ignore();
if(resizeDir != nodir){ //还原鼠标样式
testEdge();
}
}