PyQt之布局&无边框&信号

这个例子相对综合一些,包括qt的布局,实现无边框效果,无边框也就是没有了窗口的title栏,没有title栏就不能拖动了,

所以我们进一步讲如何实现拖动。通过这边文章你可以掌握qt的布局,窗口定制,重写qt方法,QSS样式,事件信号

先上例子,后面在分析代码

```

#!/usr/bin/python3

# -*- coding: utf-8 -*-

import sys

from PyQt5.QtWidgets import (QApplication, QWidget,QPushButton,QLineEdit,QHBoxLayout, QVBoxLayout,QColorDialog,QInputDialog,QFileDialog)

from PyQt5.QtCore import Qt

from PyQt5.QtGui import QCursor,QColor

class Example(QWidget):

    def __init__(self):

        super().__init__()

        self.style = """

        QPushButton{background-color:grey;color:white;}

                #window{ background:pink; }

                #test{ background-color:black;color:white; }

            """

        self.setStyleSheet(self.style)

        self.setWindowFlags(Qt.FramelessWindowHint)

        self.initUI()

    def initUI(self):

        self.setGeometry(300, 300, 300, 220) 

        self.setWindowTitle('test')

        self.setObjectName("window")

        btn1 = QPushButton("关闭",self)

        btn1.clicked.connect(self.close)

        btn1.setObjectName('test')

        btn2 = QPushButton("最小化",self)

        btn2.clicked.connect(self.showMinimized)

        btn3 = QPushButton("最大化",self)

        btn3.clicked.connect(self.showMaximized)

        btn11 = QPushButton("改变背景",self)

        btn11.clicked.connect(self.showColor)

        btn22 = QPushButton("选择文件",self)

        btn22.clicked.connect(self.showFile)

        btn33 = QPushButton("对话框",self)

        btn33.clicked.connect(self.showDialog)

        self.linet1 = QLineEdit("111111",self)

        self.linet2 = QLineEdit("ssssssss",self)

        hbox1 = QHBoxLayout()  #水平布局

        hbox1.addWidget(btn1)

        hbox1.addWidget(btn2)

        hbox1.addWidget(btn3)

        hbox2 = QHBoxLayout()  #水平布局

        hbox2.addWidget(btn11)

        hbox2.addWidget(btn22)

        hbox2.addWidget(btn33)

        vbox = QVBoxLayout()  #垂直布局

        vbox.addLayout(hbox1)

        vbox.addLayout(hbox2)

        vbox.addWidget(self.linet1)

        vbox.addWidget(self.linet2)

        self.setLayout(vbox)

        self.show()

```

    #重写三个方法使我们的Example窗口支持拖动,上面参数window就是拖动对象

    def mousePressEvent(self, event):

        if event.button()==Qt.LeftButton:

            self.m_drag=True

            self.m_DragPosition=event.globalPos()-self.pos()

            event.accept()

            self.setCursor(QCursor(Qt.OpenHandCursor))

    def mouseMoveEvent(self, QMouseEvent):

        if Qt.LeftButton and self.m_drag:

            self.move(QMouseEvent.globalPos()-self.m_DragPosition)

            QMouseEvent.accept()

    def mouseReleaseEvent(self, QMouseEvent):

        self.m_drag=False

        self.setCursor(QCursor(Qt.ArrowCursor))

    def showColor(self):

        col = QColorDialog.getColor()

        if col.isValid():

            self.setStyleSheet(self.style+"#window{background:%s}" % col.name())

    def showDialog(self):

        text, ok = QInputDialog.getText(self, '对话框',

            '请输入你的名字:')

       

        if ok:

            self.linet1.setText(str(text))

    def showFile(self):

        fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')

        if fname[0]:

            f = open(fname[0], 'r')

            with f:

                data = f.readline()

                self.linet1.setText(data)

       

       

if __name__ == '__main__':

    app = QApplication(sys.argv)

    ex = Example()

    sys.exit(app.exec_()) 

复制代码


运行效果:

上面导入的一大堆类我就不细说了,都是下面需要用到的,在我的上一篇文章中介绍了相关知识,这里只说一下,不要这样导入

from PyQt5.QtWidgets import *

这样子会把大量你没有用到的类都导入进来,占内存,所以尽量按需导入

复制代码

self.style = """

                QPushButton{background-color:grey;color:white;}

                #window{ background:pink; }

                #test{ background-color:black;color:white; }

            """

self.setStyleSheet(self.style)

复制代码


这就是qt的css样式,和css基本是一样的写法,调用setStyleSheet方法把样式应用到当前self对象,也就是Example窗口

样式是可以继承的,和html/css是一个道理,我这个地方把样式绑定到顶层窗口,所以下面我添加的button也会去这个style里面去继承样式

和css一个道理,你在下面的button上可以再次调用setStyleSheet方法来覆盖继承的方法,

每一个窗口元素(比如按钮,输入框等等都算)都可以使用setObjectName(idname)来为一个对象取别名,然后我们就可以在样式里面单独指定样式,

比如我们有两个按钮,第一个按钮指定了别名test,看一下上面的写法是不是和css的id写法一样。就像个html中div加了一个id一样,然后在css文件里面用id来唯一标识样式

后面的按钮都没有指定别名,所以就默认继承 QPushButton{background-color:grey;color:white;}  样式,当然你还可以在button上调用setStyleSheet方法覆盖之前的样式

比如你在btn2下面加一行 btn2.setStyleSheet("QPushButton{background:green }")  就可以覆盖以前的样式了

怎么写样式就和css里面的一样,比如有这些: background,color,border,background-image,width,height等等

掌握了qss就可以写出各种花式界面了。

self.setWindowFlags(Qt.FramelessWindowHint)


这句话就是去掉窗口的顶部title栏了一级边框那些,对比一下去掉边框和没有去掉的区别

这个图片好像还看不出无边框的优越性,假设qq如果加上边框会是怎么的画面呢。。。

通常一些工具软件有无边框都无所谓,但是对于一些要求界面美观的软件来说带一个操作系统提供的外壳会显得略丑。。

加上上面那句代码后边框已经不见了,等等,以前移动都是鼠标按住顶部的title栏拖动的,现在title栏不见了还怎么拖动呢?

复制代码

    def mousePressEvent(self, event):

        if event.button()==Qt.LeftButton:

            self.m_drag=True

            self.m_DragPosition=event.globalPos()-self.pos()

            event.accept()

            self.setCursor(QCursor(Qt.OpenHandCursor))

    def mouseMoveEvent(self, QMouseEvent):

        if Qt.LeftButton and self.m_drag:

            self.move(QMouseEvent.globalPos()-self.m_DragPosition)

            QMouseEvent.accept()

    def mouseReleaseEvent(self, QMouseEvent):

        self.m_drag=False

        self.setCursor(QCursor(Qt.ArrowCursor))

复制代码


这三个方法是窗口对象自带的,默认情况下他好像什么都没干,所以我们可以大胆的重写这三个方法来实现拖动,

我相信看函数名大概也知道什么意思了吧,如果你写过js的拖动效果其实就不难理解了,

mousePressEvent流程:鼠标左键按下,设置拖动标识true,记录点击坐标,设置鼠标样式

mouseMoveEvent流程:判断当前数遍仍处于按下状态 ,调用move函数设置当前对标位置,

mouseReleaseEvent流程:释放拖动标示false

这下可以正常拖动了,等等,以前的最小化,最大化,关闭都在title栏上,现在也不见了,没有我们就自己实现呗

复制代码

        btn1 = QPushButton("关闭",self)

        btn1.clicked.connect(self.close)

        btn1.setObjectName('test')

        btn2 = QPushButton("最小化",self)

        btn2.clicked.connect(self.showMinimized)

        btn3 = QPushButton("最大化",self)

        btn3.clicked.connect(self.showMaximized) 

复制代码


第一句:btn1 = QPushButton("关闭",self)  定义一个按钮对象,并指定他的容器是self,也就是放到Example窗口里面,

第二句是关键:当btn1被点击后会产生一个点击信号,然后把这个信号连接到self.close这个处理函数来处理这次事件

这个三个按钮连接的处理函数都是内建的函数,分别实现了关闭,最小最大化,

除了连接到qt提供的函数外,也可以连接到我们自己的函数,就像下面这样

复制代码

    btn11 = QPushButton("改变背景",self)

    btn11.clicked.connect(self.showColor)

    btn22 = QPushButton("选择文件",self)

    btn22.clicked.connect(self.showFile)

    btn33 = QPushButton("对话框",self)

   btn33.clicked.connect(self.showDialog)

    def showColor(self):

        col = QColorDialog.getColor()

        if col.isValid():

            self.setStyleSheet(self.style+"#window{background:%s}" % col.name())

    def showDialog(self):

        text, ok = QInputDialog.getText(self, '对话框',

            '请输入你的名字:')

        if ok:

            self.linet1.setText(str(text))

    def showFile(self):

        fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')

        if fname[0]:

            f = open(fname[0], 'r')

            with f:

                data = f.readline()

                self.linet1.setText(data)

复制代码


事件的作用就是用来发射信号,有些信号默认已经连接到处理函数,现在我们有这样的需求,就是我们要手动发出一个信号,换句话说就是我们自定义一个事件,

系统本身内部提供了很多事件,比如,click,mousemove,valuechange等等,除了这些我们仍然可以自定义事件,比如我把 鼠标向上移动接着向右移动 定义为一个事件,

这样我们可以做一些手势功能,这个要说起来又是一篇文章,就是提到这里吧,后面的文章在专门分析,初学者基本上用不到,

下面还是抛一个简单的模板列子

复制代码

import sys

from PyQt5.QtCore import pyqtSignal, QObject

from PyQt5.QtWidgets import QMainWindow, QApplication

class Communicate(QObject):

    closeApp = pyqtSignal()

class Example(QMainWindow):

    def __init__(self):

        super().__init__()

        self.initUI()             

    def initUI(self):     

        self.c = Communicate()

        self.c.closeApp.connect(self.close)       

        self.setGeometry(300, 300, 290, 150)

        self.setWindowTitle('Emit signal')

        self.show()     

    def mousePressEvent(self, event):

        self.c.closeApp.emit()             

if __name__ == '__main__':

    app = QApplication(sys.argv)

    ex = Example()

    sys.exit(app.exec_())

复制代码

提示:发射信号的对象必须继承QObject类

说到这里突然发现漏了什么,对,就是布局

复制代码

        hbox1 = QHBoxLayout()  #水平布局

        hbox1.addWidget(btn1)

        hbox1.addWidget(btn2)

        hbox1.addWidget(btn3)

        hbox2 = QHBoxLayout()  #水平布局

        hbox2.addWidget(btn11)

        hbox2.addWidget(btn22)

        hbox2.addWidget(btn33)

        vbox = QVBoxLayout()  #垂直布局

        vbox.addLayout(hbox1)

        vbox.addLayout(hbox2)

        vbox.addWidget(self.linet1)

        vbox.addWidget(self.linet2)

        self.setLayout(vbox)

复制代码


在qt中布局有三种,绝对定位,水平布局, 垂直布局, 网格布局

我上面使用的水平布局加垂直布局。通常一个软件会多种布局混合使用,布局是可以嵌套的。绝对定位是使用moveto函数来确定控件的位置,这种方式就把位置定死了,正常窗口下(非无边框),用户可以拖动边角进行放大窗口,或者点击最大化,

假设你的按钮的位置是(100,100),不管窗口变多大,按钮都是相对于窗口的左上角(0.0)位置,

绝对定位例子:

复制代码

import sys

from PyQt5.QtWidgets import QWidget, QPushButton, QApplication

   

app = QApplication(sys.argv)

wg = QWidget()

lbl1 = QPushButton('test', wg)

lbl1.move(15, 10)     

wg.setGeometry(300, 300, 250, 150)

wg.setWindowTitle('Absolute')   

wg.show()

sys.exit(app.exec_())

复制代码


如果你的软件采用无边框,用户就无法改变窗口大小了,这种情况下采用绝对定位还是不错的。

如果你的软件允许用户改变大小,那么推荐使用流式布局,也就是水平布局和垂直布局以及网格布局都是流式布局

水平布局 就不需要例子了,最上面的综合例子就是采用水平布局加垂直布局

水平布局就是把所有的元素按照水平方向平铺,占满软件整个宽度,垂直布局一样的理解

说一下网格布局:

网格布局就是把你的软件花分成x*x的表格,比如5*5或者5x8等,然后你把控件插到相应位置就可以了,

网格布局例子:

复制代码

import sys

from PyQt5.QtWidgets import (QWidget, QGridLayout,

    QPushButton, QApplication)

class Example(QWidget):

    def __init__(self):

        super().__init__()

        self.initUI() 

    def initUI(self):

        grid = QGridLayout()

        self.setLayout(grid)

        names = ['Cls', 'Bck', '', 'Close',

                '7', '8', '9', '/',

                '4', '5', '6', '*',

                '1', '2', '3', '-',

                '0', '.', '=', '+']

        positions = [(i,j) for i in range(5) for j in range(4)]

        for position, name in zip(positions, names):

            if name == '':

                continue

            button = QPushButton(name)

            grid.addWidget(button, *position)

           

        self.move(300, 150)

        self.setWindowTitle('Calculator')

        self.show()

           

if __name__ == '__main__':

    app = QApplication(sys.argv)

    ex = Example()

    sys.exit(app.exec_())

复制代码


运行效果:

分类: PyQt

上一篇:第一个PyQt程序

下一篇:PyQt 自定义信号带参数

    C/C++(2)

    CSS(3)

    GIT(3)

    Java

    JS(3)

    Linux(18)

    MongoDB

    MYSQL(14)

    Nginx(6)

    PHP(11)

    PyQt(5)

    Python(3)

    React Native(2)

    Shell

    WEB安全

    服务器架构(2)

    软件工具(2)

    设计模式

    数据结构

    数学

    网络/协议(4)

    未分类(1)

    运维(3)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容