当前位置: 首页 > news >正文

PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递

本文目录

  • PyQt5桌面应用系列
  • How old are you, Dialog?
  • QInputDialog minimalist
  • why not lambda
  • and how partial works
  • Summary

PyQt5桌面应用系列

  • PyQt5桌面应用开发(1):需求分析
  • PyQt5桌面应用开发(2):事件循环
  • PyQt5桌面应用开发(3):并行设计
  • PyQt5桌面应用开发(4):界面设计
  • PyQt5桌面应用开发(5):对话框
  • PyQt5桌面应用开发(6):文件对话框
  • PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
  • PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
  • PyQt5桌面应用开发(9):经典布局QMainWindow
  • PyQt5桌面应用开发(10):界面布局基本支持
  • PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16

How old are you, Dialog?

兜兜转转,觉得Dialog这个话题还有一点点可以写一篇。那就是QIputDialog。

我本人是不知道为啥要有这个类的。

因为我确实没感觉到有太大的需要,UI提供了在位的输入元素,比如QLineEdit、Spinner、Slider之类,直接输入就行,跳出一个对话框,让用户输入一个简单的文本、数字、浮点类型,到底有什么必要。

从用户体验上看,惊喜是应该尽可能少出现的,比如弹出一个对话框。我看到清华出版的那本《PyQt从入门到精通》里面关于QInputDialog的例子,点击一个文本框,弹出一个对话框,输入一个文本,点Ok关闭对话框,文本加入文本框。实在是叹为观止,惊为天人……

那么为什么我也写一个篇呢?有好几个理由。

  1. 我想找一个用它的理由;
  2. 我实在搬砖搬到“让用户体验毁灭吧!”
  3. 居然觉得这是一个搞清楚闭包的机会……【!】

QInputDialog minimalist

下面我们做一个最小化的QInputDialog的例子。

QInputDialog
其实还不错!

报表:

  • 用户选择的整数显示在一个QLabel上;
  • 用户选的数据可以打印出来

数据:

  • 一个整数, ∈ [ 0 , 100 ] \in [0, 100] [0,100]
  • 通过QInputDialog.getInt获得

所以这个最小化的版本里面没有按照对象继承的方法,基本的流程采用面向过程的方式编写。

import sys
from functools import partial
from types import SimpleNamespacefrom PyQt5.QtWidgets import QApplication, QInputDialog, QMainWindow, QPushButton, QLabel, QWidget, QVBoxLayoutdef global_gis(parent: QWidget, text_output: QLabel, numbers: SimpleNamespace, _: bool):val, flag = QInputDialog.getInt(parent, "Any number in 0,100", "Number", 50, 0, 100)if flag:text_output.setText(f"Number: {val}")numbers.n = valelse:text_output.setText(f"Number not set")if __name__ == '__main__':app = QApplication(sys.argv)wm = QMainWindow()but = QPushButton("Get an Integer")label = QLabel("Number: ")cw = QWidget(wm)box = QVBoxLayout(cw)cw.setLayout(box)ret = SimpleNamespace()but.clicked.connect(partial(global_gis, cw, label, ret))# but.clicked.connect(lambda check: global_gis(cw, label, ret, check))but2 = QPushButton("Current n")but2.clicked.connect(lambda check: print(ret.n))box.addWidget(but)box.addWidget(label)box.addWidget(but2)wm.setCentralWidget(cw)print(id(cw), id(label))# cw = None# label = Nonewm.setMinimumSize(400, 30)wm.show()sys.exit(app.exec_())

这里唯一麻烦事情就是,我不想定义一个类来继承QWidget,出来数据的是一个函数global_gis,这就带来一些麻烦。因为PyQt5的槽函数的形式都是类似于def slot_func(check: bool)->None的形式,那么要完成我们的功能就需要一个函数完成两个功能:

  • 类似于C/C++/C#的引用调用的方式,在参数里把QInputDialog获得的数字传递出来;
  • 还需要把QLabel或者这里的父节点传递进去。

最终,这里选择实现一个完整的函数:def global_gis(parent: QWidget, text_output: QLabel, numbers: SimpleNamespace, _: bool),然后采用partial函数,把这个函数包装成QPushButton.clicked的槽函数的形式,只有一个参数。

why not lambda

上面的程序中注释的解释了为什么不采用lambda,如果按照这种定义方式,如果在程序的下方,cw和label发生了改变,那么就会引起程序直接退出。

but.clicked.connect(lambda check: global_gis(cw, label, ret, check))
# ......
cw = None
label = None

这里的问题就在与Python的函数调用方式。Python的参数传递方式并不是传值,也不是传引用。Python实现了一个非常独特的函数调用。函数的参数实际上是采用赋值的方式传递的,通过赋值,在函数的locals()中保存对应的对象引用。这一点可以通过id()函数来查看函数参数的地址,实参与函数中的形参完全一致。

def compare_ids(x, x_id):print(id(x), " == ", x_id)x = 10  # anything
compare_ids(x, id(x))
print(id(x), " == ", id(10))

把这个值设置成任何值,都会发现,传递进去的对象是一致的。这应该是出于性能的考虑,类似于传递引用的方式。这里就很好的展示了Python中变量名和对象的关系。变量名 ↦ \mapsto 对象,变量名的类型可以随意改变,但是对象有其类型。这两个是不同的。

在函数的内部,访问一个函数参数的值,这没有什么特别的,函数传进来一个对象,函数参数是一个变量名,这个变量名在这个范围内(locals())指向这个对象。

但是在对这个变量名进行赋值的过程时,发生的情况就是这个变量指向的对象发生了改变(这是赋值在Python中的语义)。所以在函数中,改变函数参数的值,并不会改变实际参数(传进来的那个对象)的值。

这是第一个问题:函数参数的传递,Python传的是引用,传进去后绑定到局部变量名。上面的lambda还有另外一个问题。

lambda check: global_gis(cw, label, check)相当于

def _(check: bool):global_gis(cw, label, check)

这里匿名函数的内部访问了两个值:cw和label,这两个值在局部变量中没有,那么这种情况下Python会怎么去找变量名所绑定的对象呢?

  1. 在变量最近的scope中找;
  2. 在包含函数定义的scope中找。

所以,这两个变量就变成了main块中cw和labe。但是,Python变量的绑定是发生在运行时的,所以只有这个函数global_gis实际被调用时,才会找到这两个变量对应的对象,把它们赋值给函数的形式参数。如果,在运行这个函数之前,这两个变量的指向发生了改变,oops!

这就是为什么这里不能这样做。

上面这些分析,给我们通过槽函数来传递参数出来指明了路径。找一个对象,这个对象的内部状态会发生改变,把这个对象传递进函数,在函数内部改变其状态。这里我们用一个简单的SimpleNamespace对象,其实上用list、dict都行。

明白Python的函数变量名和对象的绑定关系,以及函数的参数传引用+赋值的调用方式,我们要做的就很简单,就在调用clicked.connect的当时把相应的对象传递进去,也就是强制对象绑定在当时发生。这就需要用函数编程中的partial出场了。

and how partial works

functools.partial是Python自带的一个返回函数的函数。其实现类似于:

def partial(func, /, *args, **keywords):def newfunc(*fargs, **fkeywords):newkeywords = {**keywords, **fkeywords}return func(*args, *fargs, **newkeywords)newfunc.func = funcnewfunc.args = argsnewfunc.keywords = keywordsreturn newfunc

在执行这个函数时,第一个参数就是被包装的函数,根据调用它输入的参数,它将被调用函数的部分或全部参数固定下来,构成一个新的函数,这个新函数的参数就是哪些没固定的参数。

这里很清楚的是,因为partial是一个函数,那么在connect调用的过程中,它会被实际执行,此时其参数就会被实参绑定。那么在调用这个函数后,变更变量cw和label指向的对象,就再也不影响global_gis内部的变量绑定。

Summary

  1. Python的函数参数传递方式:传引用+赋值调用;
  2. Python的变量绑定时运行时发生的
  3. QInputDialog真的没啥用……

相关文章:

PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递

本文目录 PyQt5桌面应用系列How old are you, Dialog?QInputDialog minimalistwhy not lambdaand how partial worksSummary PyQt5桌面应用系列 PyQt5桌面应用开发(1):需求分析 PyQt5桌面应用开发(2):事件…...

2.0 Vue框架设计的核心要素

本章主要讲解,一个好的框架在构建的时候,需要考虑到的要素,包含报错信息反馈、警告信息反馈、减少打包体积、良好的输出、特性开关(兼容)等 1、提升用户开发体验 提升用户开发体验主要体现在用户使用框架进行开发时&…...

“智慧赋能 强链塑链”——精细化工行业仓储物流数字化转型探讨

精细化工行业作为衡量国家化学工业水平高低的重要标志,为国民经济提供重要的终端产品支持,相比较大化工产品,精细化工产品需要高度专业技能和工艺,其生产过程需要复杂的化学反应,以及严格的控制条件,产出的…...

用DG备库做的rman备份恢复一个数据库

环境描述: 1.因为主库存储空间不足,于是将备份放在dg备库上做。 2.主库因为磁盘空间问题,数据文件有两个目录。 3.dg备库因为主库两个数据文件目录里面有两个同名数据文件,所有dg备库也有两个数据文件目录。 4.主库与备库与测…...

JAVA中的IO操作有哪些?

在Java编程语言中,输入/输出(IO)操作是很重要的部分,它允许程序从外部系统读取数据,或将数据输出到外部系统。Java提供了一组强大的IO类库,可以让开发人员方便地进行各种IO操作。 Java中的IO操作可以分为两…...

10:00面试,10:04就出来了 ,问的实在是太...

从外包出来,没想到竟然死在了另一家厂子 自从加入这家公司,每天都在加班,钱倒是给的不少,所以我也就忍了。没想到12月一纸通知,所有人都不许加班,薪资直降30%,顿时有吃不起饭的赶脚。 好在有个…...

wangzherongyao PMO

感谢【五一节】大家的相遇,总结下。 2023年05月02日,【第一组】组队开黑 我总结了下这天为什么打的那么好,首先赛季初段位在王者附近,大家心态重视程度也高,不轻敌,也不盲目,运营好兵线一步一步…...

Dart语法上

一、Dart介绍及环境 1.1 Dart介绍: Dart是由谷歌开发的计算机编程语言,它可以被用于web、服务器、移动应用 和物联网等领域的开发。Dart诞生于2011年,号称要取代JavaScript。但是过去的几年中一直不温不火。直到Flutter的出现现在被人们重新重视。 要学…...

SignOff Criteria——POCV(Parametric OCV) introduction

文章目录 1. O v e r v i e w Overview Overview2. P O C V A n a l y s i s POCV\ Analysis POCV Analysis3. P O C V F l o w POCV\ Flow POCV Flow4. P O C V R e p o r t POCV\ Report POCV Report 1. O v e r v i e w Overview Overview P r o c e s s v a r i a t i…...

Android 内存分析(java/native heap内存、虚拟内存、处理器内存 )

1.jvm 堆内存(dalvik 堆内存) 不同手机中app进程的 jvm 堆内存是不同的,因厂商在出厂设备时会自定义设置其峰值。比如,在Android Studio 创建模拟器时,会设置jvm heap 默认384m , 如下图所示: 当app 进程中java 层 new 对象(加起来总和)占用…...

产品思维与工程师思维

目录标题 什么是产品思维用户痛点体验价值 产品思维与工程师思维有什么区别?产品需要什么能力洞察需求的能力逻辑思维能力成本意识 场景化思维和用户体验数据分析和售后服务数据分析服务大多数用户原则 什么是产品思维 产品思维就是考虑产品的方方面面,…...

Android---启动速度优化

App 启动流程 1. 点击桌面 App 图标,Launcher 进程采用 Binder IPC 向 system_server 进程发起 startActivity 请求 ; 2. system_server 进程接收到请求后,向 zygote 进程发送创建进程的请求; 3. zygote 进程 fork 出新的子进程…...

使用 Mercury 直接从 Jupyter 构建 Web 程序

动动发财的小手,点个赞吧! 有效的沟通在所有数据驱动的项目中都至关重要。数据专业人员通常需要将他们的发现和见解传达给利益相关者,包括业务领导、技术团队和其他数据科学家。 虽然传达数据见解的传统方法(如 PowerPoint 演示文…...

Python基础(二)

目录 一、类型转换 1、为什么需要数据类型转换 2、数据类型转化的函数 3、str()函数类型转换使用 4、int()函数类型转换使用 4.1int()不能将str类型数据转换成int 4.2int()将bool类型转换成int 4.3int()将float转换成int 5、Float()函数类型转换使用 5.1Float()函数不…...

第41讲:Python循环语句中的break-else语法结构

文章目录 1.在循环正常结束后执行动作的思路2.通过控制布尔值变量的方式在循环正常结束后执行某些操作2.1.while循环语句2.2.for-in循环语句3.通过else从句来执行某些操作1.在循环正常结束后执行动作的思路 在执行while循环语句或者for循环语句时,如果循环是正常结束的,非执…...

双系统-真机安装ubuntu

服务器系统最好选择legacy启动mbr硬盘,数据盘可以使用gpt格式,超过2t的只能用gpt。 华为2288v3用uefi找不到启动硬盘,或者是找到硬盘后无法引导,迁移系统得到有efi引导文件的硬盘也不行,选择用legacy吧。 ubuntu默认uefi启动,若使用legacy,则需要easybcd处理一下引导。 …...

Android实现向facebook回复消息代码

以下是一个示例代码,它基于Facebook SDK版本5.0,具体实现如下: 1. 集成Facebook SDK库 下载Facebook SDK并将其加入到Android Gradle构建文件中,像这样: groovy dependencies { implementation com.facebook.an…...

IDEA小技巧-Git的回滚强推代码找回

标题IDEA小技巧-Git的回滚&&强推&&代码找回 本地未Commit 新增文件 delete 变更文件 rollback 第一种方式 第二种方式 切换默认变更列表 Commit未push undo commit 仅适用于最后一次的提交进行回滚 drop commit 回滚 revert commit revert commi…...

即时通讯为什么不采用UDP的连接方式呢

即时通讯为什么不采用UDP的连接方式呢 博主今天从网络上找了几个比较关注的热点的内容进行讲解 1.首先介绍一下UDP连接的缺点 不可靠:UDP是一种无连接的传输协议,它不提供数据包的可靠传输保证。这意味着当使用UDP进行通信时,数据包可能会丢…...

二叉树(纲领篇)

文档阅读 文档阅读 二叉树解题的思维模式分两类: 1、是否可以通过遍历一遍二叉树得到答案?如果可以,用一个 traverse 函数配合外部变量来实现,这叫「遍历」的思维模式。 2、是否可以定义一个递归函数,通过子问题&a…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...