原始战争之主界面滑动效果

简介:在弄一个横版的游戏,需要一个随鼠标左右滑动的功能,最终做了一个拖动的效果。

  • 鼠标左右移动界面也滑动
    • 实际做的时候,搜了一下没有滑动的方案,那就假想游戏界面长度大于窗口界面(类比给卷轴加游标),然后根据鼠标左右移动事件,去计算卷轴应该在的位置,然后更新界面。如果左右移动处理麻烦(滚动速度太快或太慢,现在如果玩红警95就存在鼠标移动速度问题),也可以在血条中间加上一个滑动条,来精确的控制位置。

打算用图片来模拟卷轴大小

来用一个光头的图片来模拟的时候,发现一个问题:

QLabel设置大小无效问题

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QFrame
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPixmap

class War(QMainWindow):
    '''游戏主类'''
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.board = Scroll(self)
        
        # 窗口设置
        self.setFixedSize(QSize(720,480))
        self.setWindowTitle("原始战争")
        self.show()


class Scroll(QFrame):
    '''卷轴测试类'''
    def __init__(self, parent):
        super().__init__(parent)

        self.lbl = QLabel(self)
        self.qpix=QPixmap(sys.path[0]+'/resource/57001_one_punch_man.jpg')
        self.lbl.setGeometry(0,0,720,480)
        self.lbl.setPixmap(self.qpix)
        
if __name__ == '__main__':
    app = QApplication([])
    war = War()
    sys.exit(app.exec_())

看效果

发现图片没有显示出来,只显示在了左上角,其实不是QPixmap的问题,时QLabel大小设置的问题。如果把

self.lbl = QLabel(self)
self.qpix=QPixmap(sys.path[0]+'/resource/57001_one_punch_man.jpg')
self.lbl.setGeometry(0,0,720,480)
self.lbl.setPixmap(self.qpix)

这段代码放到War类的initUI()方法下直接调用的话,是可以的,所以问题出在了其他类中的QLable下,初始化QLabel时我用的是self,试着用了下parent就好了。
self.lbl = QLabel(parent)

原因是
QLabel::QLabel(QWidget *parent = nullptr, Qt::WindowFlags f = ...)
第一个参数为父级QWidget对象。 Qt Documentation - QLabel Class

这个问题是个小插曲,继续做滑动效果,为了方便测试还是在一个类做尝试。

尝试用鼠标移动触发

首先选择了好久最后打算用QScrollArea来处理

先将滚动条加上

def initUI(self):

        self.lbl = QLabel(self)
        self.qpix=QPixmap(sys.path[0]+'/resource/57001_one_punch_man.jpg').scaledToHeight(480)
        self.lbl.setGeometry(self.qpix.rect())
        self.lbl.setPixmap(self.qpix)
        
        # 添加滚动条,并添加组件
        scrollArea = QScrollArea(self)
        scrollArea.setWidget(self.lbl)
        scrollArea.resize(QSize(720,480))
 
        # 窗口设置
        self.resize(QSize(720,480))
        self.setWindowTitle("原始战争")

self.qpix=QPixmap(sys.path[0]+'/resource/57001_one_punch_man.jpg').scaledToHeight(480)
先把图定高等比例缩放。再添加滚动条。

效果:

搞定,鼠标拖动

鼠标拖动也刚好解决了滑动烦人的速度问题,此处功能以后还可以做RTS用。

在一个论坛看到一篇07年用C++写的拖动例子,关键在于重载滚动条的事件过滤

bool eventFilter(QObject *o, QEvent *e)
{
    if(o!=l)
        return QScrollArea::eventFilter(o,e);
    if(e->type()==QEvent::MouseButtonPress)
    {
        m_prev = ((QMouseEvent*)e)->pos();
        this->prev_diff.setX(0);
        this->prev_diff.setY(0);
        return true;
    }
    if(e->type()==QEvent::MouseMove)
    {
        QPoint pt = ((QMouseEvent*)e)->pos();
        this->diff = pt-m_prev-prev_diff*n;
        m_prev = pt;
        horizontalScrollBar()->setValue(horizontalScrollBar()->value()+diff.x()*n);
        verticalScrollBar()->setValue(verticalScrollBar()->value()+diff.y()*n);
        qDebug()<<"("+QString::number(diff.x())+","+QString::number(diff.y())+")";
        this->prev_diff=diff;
        return true;
    }
    return QScrollArea::eventFilter(o,e);
}

改了一下:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QFrame, QScrollArea, QWidget, QVBoxLayout, QHBoxLayout, 
QScrollBar, QAbstractScrollArea
from PyQt5.QtCore import QSize, Qt, QEvent, QPoint
from PyQt5.QtGui import QPixmap

class War(QMainWindow):
    '''游戏主类'''
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.lbl = QLabel(self)
        self.qpix=QPixmap(sys.path[0]+'/resource/57001_one_punch_man.jpg').scaledToHeight(480)
        self.lbl.setGeometry(self.qpix.rect())
        self.lbl.setPixmap(self.qpix)
        print(self.lbl)
        # 添加滚动条,并添加组件
        scrollArea = ScrollArea(self)
        scrollArea.setWidgetResizable(True)
        #scrollArea.setBackgroundRole(QPalette::Dark);
        scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 隐藏滚动条
        scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scrollArea.setWidget(self.lbl)
        scrollArea.resize(QSize(720,480))
  
        # 窗口设置
        self.resize(QSize(720,480))
        self.setWindowTitle("原始战争")

        #self.scroll(400,200)
        #self.update()

class ScrollArea(QScrollArea):
    '''重载滚动条类'''
    def __init__(self, parent):
        super().__init__(parent)

        self.n=0.8
        self.m_prev = QPoint()
        self.diff = QPoint()
        self.prev_diff = QPoint(0, 0)
        self.widget = None


    def setWidget(self, w):
        '''设置区域部件时,对象赋值'''
        super().setWidget(w)
        self.widget=w
        self.widget.installEventFilter(self)
        
    def eventFilter(self,obj,evt):
        '''事件过滤'''
        #print(QScrollArea.widget(self),obj)
        if(obj!=self.widget):
            return super().eventFilter(obj,evt)
        
        if(evt.type()==QEvent.MouseButtonPress):
            self.m_prev = evt.pos()
            self.prev_diff.setX(0)
            self.prev_diff.setY(0)

        # 鼠标移动
        # MouseMove为鼠标拖动效果,鼠标滑动是ToolTip
        if(evt.type()==QEvent.MouseMove):  
            #print(evt.pos())
            pt = evt.pos()
            self.diff = pt-self.m_prev-self.prev_diff*self.n
            self.diff = -1*self.diff
            print('diff x :',self.diff.x())
            self.m_prev = pt
            
            print(self.horizontalScrollBar().value()+self.diff.x()*self.n,'--')
            self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()+self.diff.x()*self.n)
            # horizontalScrollBar().setValue(horizontalScrollBar().value()+self.diff.x()*self.n);
            # verticalScrollBar()->setValue(verticalScrollBar()->value()+diff.y()*n); # 竖向暂时不需要移动
            self.prev_diff=self.diff;
            return True
        return QScrollArea.eventFilter(self,obj, evt)

   
if __name__ == '__main__':
    app = QApplication([])
    war = War()
    war.show()
    sys.exit(app.exec_())

github

最终界面效果:

总结

QT做的太好了,看C++文档和代码,一般理解了都可以无缝用PY重写。
以为俩小时能搞好的,搞了一天。英语还是很重要,就是没动力学 嗝。

最佳实践就是动手去做,在实战中学习、成长,而不是等所有的都准备好之后再开始。这两天园子首页的推荐彼之蜜糖,吾之砒霜——聊聊软件开发中的最佳实践

参考,致谢

原文地址:https://www.cnblogs.com/warcraft/p/9464100.html