目录
2.1.3 设置背景时将QWidget的子控件也设置成了相同背景
1 综合案例介绍
我们最终的综合案例是这样的,首先运行程序后会出现像这样的登陆界面,素材我找不到我就用别的东西替代了,上面的雪景是一个gif
输入的用户名与密码不符合要求时窗口会出现抖动,点击右侧的二维码图标可以弹出一个该二维码的网页,点击注册账号后会从界面下方向上弹出这样一个页面,快到最终位置时会上下抖动两下
在这里进行账号注册,注册之后把账号保存到本地,之后才能进行登陆
点击左上角菜单时会弹出下列子菜单
点击关于后会弹出一个带有信息的对话框
点击重置会清空我们注册界面三个单行文本输入框的内容,点击退出后会退回到登陆界面,注册界面从右侧动画退出
当在菜单展开状态时再点击菜单,菜单会合起来
当我们登陆界面输入正确的账号密码后,会重修按一个计算器页面,计算器可以正常使用
计算器页面可拖拽,控件会随着窗口大小变化
点击运算符号时,运算符号按钮会改变样式,一直到点击下一个按钮,等于号不改变样式
我们每拿到一个项目,我们首先需要单独搞定项目中的每一个界面
2 注册模块
2.1 页面实现
背景这个图我找不到我就先随便换一个背景
我搞出来是这样的
2.1.1 控件大小不随布局管理器改变
在布局的过程中,如果调整布局管理器,控件的大小并不改变,我们对控件调整尺寸策略
默认是fixed(固定),我们可以选择expanding(扩展)
然后再更改纵向的拉伸系数
2.1.2 想修改控件之间的间距
2.1.3 设置背景时将QWidget的子控件也设置成了相同背景
可以使用objectname的选择器,我当前的objectname是Form
2.1.4 统一设置样式
只能在右侧属性列表修改styleSheet,如果使用右键点击修改不能进行统一修改
2.1.5 控件背景颜色设置为透明
2.1.6 按钮按下与非按下两个状态设置不同的颜色
我当前的qss是这样的
我现在加上伪状态
这个是我按下时的颜色
也可以加hover,鼠标放上去样式也改变,一般来讲颜色的深度是越来越深的,普通深于hover,pressed深于普通
2.1.7 单行文本编辑器只保留边框下方的横线
现在我的界面是这样的
现在我们修改这三个单行文本编辑器的样式,我们首先把边框全部消除,之后添加下边框,下边框宽度2px,实线,红色
2.1.8 左上角的样式
我们需要给菜单这个按钮设置两个样式,一个是选中(checked)时的,另一个是非选中(unchecked)时的,首先我们需要把QPushButton属性改为可选中
然后改样式
运行之后选中菜单
2.1.9 hover状态
作为正常的UI,当我们把鼠标放到按钮上也应该有反应,所以我们对这四个按钮加入hover的伪状态,就像这个样子
下面的注册我们也放一下
2.1.10 更改objectname
为了之后逻辑实现中使用变量更加方便,我们把所有控件的名称都改一下,这些是原本的名称
改完之后是这样的
如果在之前使用了objectname选择器的样式表,此时也要修改样式表
2.1.11 菜单槽函数
我们使用点击信号的第二个,这个可以传递按钮的状态
- 一定不要把槽函数拖到屏幕外面去,如果拖到外面会非常难选中
除了菜单槽函数,我们还需要给所有按钮设置槽函数,其它就使用clicked()就好
2.1.12 单行文本编辑器快速删除按钮
我们每次删除都需要按住退格键才能把内容都删了,现在我们添加一个快速删除的按钮,在这个地方把对勾打上
这个时候我们点击这里就可以快速删除内容
2.2 逻辑实现
当前我们已经有一个完成的UI文件了,我们此时将UI转为PY文件
虽然现在已经有了UI文件,但是我们不知道后续还要不要继续修改,客户如果觉得不好想提出修改很正常,所以我们不能对转完的register.py这个文件进行直接修改,我们需要再创建一个文件以便后期覆盖register.py
然后我们把qrc和槽函数搞完,当前代码是这样的
此时可以运行
2.2.1 导入后窗口背景加载不出来
这样需要在这里加上这个语句
这样我们的背景图片就可以加载出来了
这里我再说一下另一种直接导入UI的方式,我觉得少一个文件就少一个错误的可能,我更倾向于直接导入UI的方法,所以我们现在的代码是这样的
但是如果直接读UI在打包的时候会出问题,如果我们是想打包的话只能读取UI转换后的PY文件,这个在 93.pyqt py文件打包 会讲到
运行后同样可以显示界面
当然这种方式有可能会造成误覆盖,这个根据每个人的需求不一样选择不同的处理方式
2.2.2 动画
动画只能手码,在QTdesigner不能实现动画
我们首先要在占用内存与运行速度直接做出一个取舍
- 如果我优先考虑要占用内存变小,则我只创建一个动画组,然后想要执行收起或分发的时候再覆盖掉原来的动画组,然后再执行动画组。
- 如果要考虑运行速度则我要创建两个动画组,然后把它都创建完毕,之后在判定时执行不同的动画组
我在这里优先考虑运行速度
考虑到动画的要求,我的菜单按钮永远要在最前面,所以我们在UI中点击放到前面
我们使用的动画都是统一的,我们可以使用for循环创建动画,但是因为动画不多,有可能后期会有改变,所以我们选择把能创建的动画都创建了,如果之后有数量很多的动画再使用for循环
由于我们当前的界面是这样的,所以我们现在要把当前三个按钮的位置信息保存下来以便之后调用,之后把关于,退出,重置三个按钮移动到菜单按钮处
之后定义动画
把按钮变量赋值下来以便后面调用,然后创建动画组,创建动画,修改位置属性,起始点为菜单按钮位置,结束点为之前记录的各个按钮位置
- 向外移动动画
- 向内移动动画
- 向内实际上虽然就是向外的逆向动画,但是如果将向外赋值给向内再对向内反向操作,向外与向内的哈希值会相等,会对其中一个动画有影响
之后我们修改槽函数
我们如果不想再创建一个收回的动画,此时可以逆向播放动画
两个分支都有start(),我们也可以把start()提取出判断以外
这个时候动画就做成了,这个是界面的初始状态
现在我们点击一下菜单
2.2.3 关于按钮的槽函数
它的作用是弹出一个消息对话框,我们直接可以使用静态方法,我们就使用下面这个吧
运行之后我点击一下关于按钮
2.2.4 重置按钮的槽函数
这个是作用是把我三行单行文本编辑器的内容全部清空
2.2.5 注册按钮状态
当三行文本编辑器有一个为空,或密码与确认密码不一致时,注册按钮设定为不可点击状态
这个时候我们要使用三个单行文本的信号了,我们都使用textChanged这个信号,每当文本改变一次就进行判定
我们添加这三个槽函数
这三个槽函数的内容都是一样的
意思是如果:
- 1.账户内容长度>0
- 2.密码内容长度>0
- 3.确认密码内容长度>0
- 4.密码与确认密码输入内容相同
如果满足上面4个条件则按钮可用,如果不满足则不可用
之后再将起始状态设置为不可用
我们也可以修改UI直接改成无效状态,把这个对勾取消
上面使用三个槽函数是比较好理解的一种写法,实际上在这个项目中如果没有别的需求我们三个信号可以连接一个槽函数
但是在实际情况中,我们可能会判断账户是否已经存在,密码强度是否达标,所以我就保留之前的三个槽函数,再次对三个信号连接一个新的槽函数,用户是否存在,强度是否达标也可以在统一的槽函数中判定,这个看需求
三合一,同样可以达到预期效果
为了使效果更加明显,我们修改按钮的不可用状态样式为灰色
2.2.6 与外界的交互
现在还有两个按钮的信号没弄,一个是退出按钮信号,一个是注册按钮信号,这两个信号都是与外界交互的信号,在这个界面起传递作用而不是处理作用,所以我们要自定义信号再将这两个信号发射出去
首先我们创建两个自定义信号
在下方这两个槽函数中各自发射
为了后面好找到这个信号,我在这换一个类名
下面这个测试函数里最好也换一下,不过不换也不耽误调用
注册信号需要传递用户名和密码出去,我们修改一下
3 登陆界面
3.1 页面实现
3.1.1 二维码图片太大
如果图片太大这个时候我们就不要用背景图片了,我们要使用边框图片,这样可以将整个图在指定范围内铺满
3.1.2 布局的间距
当我们在布局时不想要红框出的间距与边距时,我们可以在右侧修改对应的属性取消间距与边距
3.1.3 改控件名称
3.1.4 combox加条目
可以右键在combox中加入条目
可以在添加的条目中修改属性(右下角Properties按钮),属性中有一个图标,我们可以添加该条目前面的图标,不需要展开icon,直接在后面扩展的点儿里选择资源就行了
退回后按ctrl+r就能看到效果了
3.1.5 设置按钮图标
这个在样式表里没有,在属性列表里有
在iconsize可以调整图标的尺寸
3.1.6 其他样式
- 账户
普通状态下无背景仅下边框,鼠标放上去是黑色边框,选中是蓝色边框
drop-down是comboBox的按钮区域,down-arrow是按钮区域上的箭头
QAbstractItemView是按钮区域左侧可以看到条目的区域区域
QAbstractItemView:item 这个是条目打开是中的条目区域
- drop-down,drop-arrow,QAbstractItemView,QAbstractItemView:item我在py文件中没换
- 密码
- 登陆按钮
3.2 逻辑实现
首先在pycharm中读取UI
我们发现有几张图没有,这个是因为我们之前改过qrc但是每转qrc,现在我们中心转一下,然后运行
3.2.1 gif图
这个只能手码,QTDesigner里搞不了
我们方法用的是和之前QLabel一样的方法,只是路径不要再使用文件路径,而是使用qrc中的文件路径
- 直接导入文件路径方法
- 导入qrc中的gif文件
这个时候我们将UI转为Py,然后输入:/这样就可以看到其他的控件是怎么访问qrc的
我们直接粘贴进来,然后改个名字
- 如果没有其他额外的操作,qrc中的图名字和文件夹中的图名字相同
- gif图中有白色背景这个处理不了,需要找一个没有白色背景的贴上去才行
此时这个gif是没有显示全的,我们把movie对象的尺寸改成label的大小就行
我觉得还是没展示全比较好看一点儿,我在Py文件中会注释掉这一行
3.2.2 注册账号按钮
首先连接注册账号按钮的槽函数与槽,我们不应该让独立的界面有任何的联系,所以我们要新建一个py文件作为主代码,那么我们就不应该在登陆界面中直接导入注册界面,而是应该把注册账号这个信号甩出去给之后的主代码用
这个时候我们注意,我们定义的信号最好不要起相同的名字,这样虽然对程序没有影响(因为调用信号时会注明从哪个文件中调用),但是我们阅读起来很麻烦
所以我们也要改一下之前注册界面的信号
- 注册界面
- 登陆界面
- 连接两个界面
首先我要创建一个新的py文件main,主函数因为代码量较少,不适合再使用面向对象写法增加封装性,所以我们使用面向过程
这样的话我的登陆界面就可以直接读出来
如果我想让注册界面点击按钮之后直接跳出来就这样写
运行之后只有一个页面
这个时候我们点击注册账号
我现在想让注册界面做动画效果然后与登陆界面在一个界面内展示,这个时候我应该这样写
这个时候会报错,因为我在设计独立页面时没有考虑到父对象,所以我们现在需要修改一下注册界面
修改之前
修改之后
我们对其设置父对象之后(默认为None),args和//*kwargs是我们如果之后还要传递什么参数这样就可以直接传了
我们现在把登陆界面也修改一下
之后我们在main中这样写
这样动画效果就做出来了
我们可以修改为运行完动画后删除
3.2.3 注册界面的退出按钮
我们接收到退出信号后,做另一个移动的动画
为了结构简洁一点儿,我们把这三行拿到方法外,然后加入信号,加入动画
现在也可以正常退出了
3.2.4 注册界面注册按钮槽函数
在这个例子中我们就在main中打印出来,当然我们也可以存在一个txt文档中,这个在我们的 67.读取,修改,保存指定的txt文件.py 有对文本文档操作的例子
3.2.5 二维码按钮
点击之后去访问我们指定的连接,这个连接是咱们手动和二维码设置成一样的,不是它识别的,如果要识别的可以再加其他的方法
放一个槽函数
这个就要回到我们之前单独的登陆界面的py文件中修改了,槽函数这样写
这样我们点击一下按钮就可以用默认浏览器打开网页了
3.2.6 登陆按钮状态判定
不为空
至于怎么处理得到的用户与密码这个是登陆按钮的问题
3.2.7 登陆按钮槽
我们甩个信号出去就好
然后我们在main函数中收一下,顺便把注册里的acc与pwd的变量改了
3.2.8 自动登陆与记住密码
如果点击自动登陆我们应自动勾选记住密码,如果我们取消了记住密码,我们应该自动取消自动登陆
首先在UI中分别接两个槽函数,这两个槽都是要布尔点击的那种
然后再定义槽函数
3.2.9 账户密码错误的窗口抖动
我们应该把抖动的动画写在独立的login界面中,把触发抖动的信号放在main中
- 设置动画
animation的setobject不能一次设置多个对象,所以我们要把下方的所有控件放在同一个widget中,让widget动
我们首先拖一个widget到界面上来
然后右键widget点击放到后面,然后把控件都拖进这个widget中,之后再给widget命名为shake_widget
然后我们找到shake_widget的当前位置,这个后面有用
然后我们在pycharm中制作动画函数
- 由于我在函数中创建的动画,所以我每次动画运行完可以把它删了减少多余的内存占用
这样就能做出一个抖动的效果
- 接收信号
我们写死一组账户密码
- 这个在后面会完善if分支
然后修改连接的槽函数
这个时候输入错误的密码就可以看见抖动了
4 计算器页面
4.1 页面实现
4.1.1 按钮无法占满所有位置
用qtdesigner摆好所有控件,纵向拉伸后无法占满所有位置
使用默认的最大高度不行,我们现在给每个按钮都设置为最大高度为9999,我们给空间框起来同一设置就好,这个时候无论纵向还是横向都可以达到要求
4.2 逻辑实现
首先我们要创建一个Py文件读取界面
发现可以运行,我现在是没把界面做完就做了逻辑实现
4.2.1 加载界面
在main中进行修改,导入文件后在判定条件中加入展示界面代码
实例化,此处应该在外界进行实例化,使这个变量成为全局变量,如果在def中定义会被释放掉
展示计算器界面之后隐藏登陆界面
- 经测试可实现效果
4.2.2 按钮的样式
我们一共有三种样式的按钮,现在这个例子中,如果我对按钮一个一个进行设置就会非常麻烦,这个时候我们应该对相同样式的按钮设置属性,然后使用属性选择器统一设置样式
我们不在QTDesigner中写qss,因为下面要涉及到圆角,圆角大小要随着值改变,所以无法在QTDesigner中写
- 我们也不要既在QTDesigner中写又在pycharm中写样式,这样这两种样式不会叠加,而是只会调用其中一个(后赋值的会被调用)
- 视频中使用的方法是每一种按钮新创建一个类,之后在QTDesigner中分别提升类,然后对新建的类设置样式,我和视频中思路不同,如果向了解他的做法可以在视频中学习
在之前我是设置了样式,现在我要取消掉所有样式
然后我们把对象的名称都改一下
- 给所有按钮设置属性,分为三类num数字类,operator运算符类,others其他
我们也可以在QTDesigner中添加属性
输入属性名称
我们输入bg后点击OK,然后在属性列表中就可以查到,然后我们对其进行更改
- 按钮的圆角
我们首先把按钮的圆角写了,因为这个是统一的样式
通过这两张图我们可以看出来,圆角 = min(最大值,最小值)/2
我们实际操作中再对最小的数值进行微调,所以咱们这样写,由于圆角是随着控件大小而变化的,控件大小随着窗口大小而变化,所以我们重写widget中的更改尺寸事件
我们现在把字体再搞大一点
- 数字类型按钮的样式
- 运算符类型按钮的样式
- 其他类型按钮的样式
- QLineEdit的样式
我们的QLineEdit也可以在QTDesigner中修改,因为这个不是stylesheet的属性所以可以在QTDesigner中修改
- 每种按钮的伪状态
我们除等号外的运算符是可以被选中的,我们在QTDesigner中设置一下
我是使用调色盘调整的颜色,如果想快速写完颜色的话只需要将rgb三个值提高相同数值,则可以使颜色更亮
运算符设置为选中后我们要避免这样一个情况
我们这四个运算符实际上是有互斥性的
我们可以用代码设置
也可以在QTDesigner中完成,我推荐在QTDesigner中完成
首先将所有按钮设置为可选中状态
然后再将所有按钮设置排他性
这个时候我点击加号
再点减号
再点一下数字6
发现会取消掉选中状态,这是因为此时6按钮是被选中的,但是我没设置选中样式,所以和没选中看起来一样
但是这样做会出现一个问题
所以我们更改0的属性type
然后设置0按钮的样式
- 经测试现在可以了
如果不设置按钮普通样式的话,按钮的选中状态是有一个默认样式的,设置按钮普通样式后默认样式消失
4.2.3 计算逻辑实现
我们python内置有一个方法eval,是这样用的
这样我就可以向其中添加字符了
现在正式开始,我们现在给每一个按钮加上槽函数,这个只能一个一个拖,没有别的办法,我们信号选选择正常点击就好
我们要再更改一下样式,这些按钮设置为不可选中
- 设置四个初始值
- 点 槽函数
数字类的槽函数是相似的
- 零 槽函数
- 一 槽函数
- 二 槽函数
- 三 槽函数
- 四 槽函数
- 五 槽函数
- 六 槽函数
- 七 槽函数
- 八 槽函数
- 九 槽函数
- AC 槽函数
- 相反数 槽函数
- 百分号 槽函数
- 加减乘除 槽函数
- 等于 槽函数
这样就差不多,最起码像一个计算器,下面我记录一下视频中的方法
视频中是建立了自定义类,然后自定义信号,使每一个自定义信号链接同一个槽函数,由于是自定义信号,所以在点击的时候可以传递属性,按钮上面的文字与类别就被传递了下来
自定义信号key_pressed,传递按钮上的文字与类型
重写鼠标按下事件,当鼠标按下时信号发射
槽函数get_key,接收按钮的文字与类型
此时如果在槽函数中Print出两个变量,按下不同的按钮会有这种效果
当前我们一共有四种角色
- num 数字
- operator 运算符
- clear 清空
- caculate 等于
之后它在pane中重新定义了一个类,用于处理计算器的逻辑
然后在加入核心类中加入这两行
现在点击按钮之后就是这样
统一槽函数的好处是这样的,能分级进行操作,比如说现在点击了AC这个按钮就不会执行下面所有的分支,这样我们写分支的时候就会少了很多的约束
等于逻辑
之后开始写计算逻辑
如果列表的空的且添加角色为数字就正常添加
之后再次添加,如果角色相同,角色为数字分支,角色都为数字就将这两个数字的文字合并到一起
两者角色不相同就直接正常加入
现在考虑运算符与前者相同的问题,如果都是运算符就替换掉它
然后我们处理0的问题,现在我们按三个0这个结果是不对的
如果不等于0就正常添加,如果等于0则进行替换
之后就是小数点的问题
当什么都没添加时,点击小数点,会添加的内容为 0.
如果角色相同,且上一个按键中已经包含小数点则跳出分支
之后考虑百分号和+/-按钮,如果是百分号与正负号则进入分支
- 如果前面不是数字类型,则跳出判定
- 如果前面是数字类型进入下一轮判定
- 如果是百分号,则将数值转换为浮点型之后除100,然后再转换为字符串型
- 如果是正负号,则将数值转换为浮点型之后乘-1,然后再转换为字符串型
我们回到之前的清空判定,直接赋值为空列表完事儿
计算判定
首先创建了一个方法,创建一个空的字符串expression,然后遍历咱们刚刚创建的列表,把title加到字符串中
然后在判定分支中进行调用
这样点完等于号之后我们就获得了一个要计算的字符串
然后使用内置函数eval进行计算
这样就能计算值出来了
现在点击清空之后再进行一轮计算会有问题,我们再修改一下,我们只需要在等于方法中加一个判定,如果列表中有内容且最后一个内容是运算符则删除那个运算符内容
当前我们运算逻辑就完毕了,现在要把我们的计算过程显示在QLineEdit上
我们首先在核心类中定义一个信号
我们按下按钮的清空分支,要显示字符串0.0
等于分支中创建的方法,把之前的return None改成return result
然后在等于分支中把result转换为字符串然后扔出去
当列表为空时且角色为数字时,显示添加的数字(第一次添加数字或清空后第一次添加数据的情况)
处理好百分号与正负号时发送
融合处理完毕后发射信号
点击运算符后发送的是结果,它这个能做到连加功能
此时当我们两次点击的角色不一致时就要修改判定了,就是咱们的这个地方
改成这样,如果点击的是数字发送当前的title,如果点击的是运算符则发送结果
以上就是我们所有要发射信号的地方,下面在核心类的init中连接信号与槽函数
然后在核心类中定义一个槽函数,功能是将接收到的字符串显示出来
我们运行之后发现小数点还是有问题,然后我们在这里加入这个,当我们的类型不相同时,在内容中包含点,百分号,正负号,就跳出判定
至此我们最终的案例就完成了,我在gitee中的py文件使用的是我自己的写法,如果想了解视频中的方法建议看一下视频