您现在的位置是:网站首页> PY&Rust

Python项目实操笔记

  • PY&Rust
  • 2024-09-21
  • 1276人已阅读
摘要

Python项目实操笔记

***多看库例子和各类应用例子***

Pip使用

如何生成python项目需要的最小requirements.txt文件

pip 与 Poetry 管理 Python 套件的区别

Python pyinstaller打包及缺失dll文件及相关库解决方案

Python pyinstaller spec文件 打包多个python文件为exe应用程序

PyCharm创建项目注意否则出现找不到模块

Python中文编程

Python社区

Python集成开发环境下载

Python程序打包的方法

PyQt打包后的exe太大?试试使用UPX进行压缩优化

Python界面 PyQT可视化开发(python3+PyQt5+Qt Designer)

pyqt5程序打包成exe文件的步骤和遇到的坑,以及如何更改exe的图标

安装pyinstaller.exe

python隐藏窗口_python中隐藏Console窗口

关于Micropython代码保护

用PySide写界面

Python 请求登录接口后获取cookie

在Windows平台进行OpenCV开发,需要先进行以下步骤配置开发环境

Python二维码识别

Python操作串口

Python Socket服务端与客户端例子

python蓝牙通讯代码

Python GUI 库

使用ffmpeg+opencv读取摄像头并推流到rtmp服务器

车牌识别源代码

tkinter UI开发

值得拥有的五种Python Tkinter界面美化库!

wxPython UI 开发

点击下载wxFormBuilderwxFormBuilder-4.0.0-x64.rar

Python GUI应用程序开发之wxPython库详解

Python操作数据库

一个GUI库NiceUI

如何将Python打包后的exe还原成.py

Python 打包成 exe,太大了该怎么解决

[Python]读取QQ聊天记录

一个python操作低功耗蓝牙例子包含连接发送接收

低功耗蓝牙包含扫描、连接、发送和接收数据的Python BLE操作示例

一个python操作低功耗蓝牙例子包含扫描设备,遍历服务和服务里的特征值,连接发送接收

Python 中去除 console 窗口(也称为命令行窗口)的方法

Python的websocket服务端客户端的详细介绍及例子

python调用MQTT的详细例子要求有注释




pip使用

点击查看原文

pip(package innstaller for python)可以非常方便的管理三方包,诸如,安装,列举,更新,删除等操作,只需一行命令就可完成。

安装 Python 包

这个是在开发中最常使用的命令,各种包的安装都需要使用此命令,但是这里边有很多细节

安装指定包

pip install 包名

# 这样安装的三方,会使用当前包的最新版本。


安装指定版本的包

pip install 包名==版本号 

# 安装指定版本包


pip install 包名>=版本号 

# 安装版本号大于给定版本


安装环境文件指定的包

pip install -r requirements.txt

# 安装requirements.txt 按照文件中的版本

# -r 表示reference, 引用


更新包版本

更新包版本一般用于升级当前包的版本,如果想要降级版本可以使用 pip install 中指定版本号来完成。

pip install -U 包名

# 这里的u是大写的,也可以使用下边语句

pip install 包名 --upgrade

# 更新版本会将指定包升级到最新版本


# 升级pip自己

pip install --upgrade pip


卸载三方包

有安装必有卸载,卸载也非常容易,只需要在 install 前加 un 即可。

# 卸载指定包

pip uninstall 包名

# 卸载环境配置中所有包

pip uninstall -r requirements.txt


列出当前环境安装的包

这里之所以强调当前环境,是因为如果我们使用来虚拟环境,则只会列出虚拟环境安装的包,全局环境的并不会展示。

pip list # 列出所有的包

pip list -o # 列出所有可升级的包


显示包的信

使用 show 命令可以查看指定包的目录以及一些使用帮助信息

pip show 包名


查看已安装包及其版本

freeze 有冻结的意思,这里表示 Output installed packages in requirements format.

pip freeze # 列出所有的安装包及其版本

pip freeze > requirements.txt # 将信息写入到环境文件中


pip freeze 和 pip list 展示出的内容基本相似,只不过用于环境导出


下载三方包

下载三方包的主要用处是方便环境的迁移,如果目标机器不支持联网,这时我们就无法使用 pip install 来装,所以就可以借助 pip download 来下载所有需要的三方包到目标机器(更多的时候是客户方为了机器安全,禁止访问互联网)。


pip download -d 目录 包名 # 下载单个离线包

pip download -d 目录 -r requirements.txt # 批量下载离线包

通过上述方式,就可以将三方包下载到指定的目录中。

下载完的三方包后缀名为 whl, 并且包含其依赖的其他包。


安装离线三方包

上一步我们下载来所需的离线安装包,这里我们安装指定的安装包


pip install --no-index --find-links=/目录/ 包名 #安装单个离线包

pip install --no-index --find-links=/目录/ -r requirements.txt # 批量安装离线包



更换安装源地址

在我们使用 pip 的时候是不是经常会出现 timeout 的情况,或者即使下载下载成功来,也需要很久,这是因为 python 中默认地址是国外的 pypi,不过,现在国内有很多提供下载的服务。最常用的是清华和阿里的(当然还有其他很多选择,根据实际情况来)。

临时使用

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名

如上,我们使用 -i 的参数,来指定源地址为清华服务器,只不过这样只对当前命令生效。


全局生效

配置全局源地址需要修改配置文件,路径C:\Users\ 用户名 \AppData\Roaming\pip\pip.ini,内容如下:


[global]

index-url = https://pypi.tuna.tsinghua.edu.cn/simple

[install]

trusted-host=pypi.tuna.tsinghua.edu.cn

如果 Roaming 中没有 pip 文件夹,需要新建文件和文件,如果看不到 Roaming 文件夹,需要将文件夹显示模式设置为,隐藏可见。

修改完成后,可以看到在不指定的情况下,会查找 indexes 配置的内容。这就省了我们每次下载都需要带源地址参数。



如何生成python项目需要的最小requirements.txt文件

在把自己写好的 Python 代码发给别人时,对方可能会缺少代码运行所需要的第三方库,一个个去 pip 安装比较麻烦。这时候,就可以把项目所需要的第三方库导出为一个requirements.txt文件,放在项目文件夹里一起给到对方,对方可以一键安装所需要的库。


方法一:pip freeze方法(不推荐)

1、查看包pip freeze 是一个 Python 命令,用于列出当前虚拟环境(virtual environment)中安装的所有 Python 包及其版本。这个命令在创建项目的依赖清单(requirements.txt)时非常有用,以便在其他环境中重新安装相同的依赖包。使用 Pycharm 打开工程,在下方中打开Terminal终端,输入pip freeze ,可查看项目所安装的所有第三方库。

pip freeze

2、生成requirements.txt在终端中输入pip freeze > requirements.txt,可在工程目录下生成requirements.txt文件,其中包含当前虚拟环境中安装的所有 Python 包及其版本。

pip freeze > requirements.txt

这个方法会将目前 pycharm 中已下载的所有包都导入到 requirements.txt 文件中,不推荐使用。



方法二:使用第三方库pipreqs(推荐)使用第三方库pipreqs生成项目的 requirements.txt 文件,pipreqs会分析项目中的 Python 源代码文件,找出所有依赖的包,并将它们及其版本写入 requirements.txt 文件。pipreqs可以只将用到的库生成到requirements.txt文件。

1、先安装pipreqs库在终端中使用 pip 命令方法安装 :pip install pipreqs

2、生成requirements.txt在当前目录使用pipreqs命令:

 pipreqs ./ --encoding=utf8  --force

 –encoding=utf8 :为使用utf8编码 

–force :强制执行,当 生成目录下的requirements.txt存在时覆盖 

. /: 在哪个文件生成requirements.txt 文件 

在终端中看到,已经成功生成了requirements.txt 文件。


至此,已经将项目需要的库导出到 requirements.txt 文件了,可以把 requirements.txt 文件与项目一起分享给对方。

对方只需要pip install -r requirements.txt 一条命令,就可以在他们的环境中安装相同的依赖包。



pip 与 Poetry 管理 Python 套件的区别

pip 和 Poetry 都是用于管理 Python 包的工具,但它们在设计理念和功能上有一些重要的区别。以下是它们的主要区别:

依赖管理: pip:

使用 requirements.txt 文件列出项目依赖。

不能自动处理依赖冲突。

不区分开发依赖和生产依赖。

Poetry:

使用 pyproject.toml 文件管理依赖,格式更清晰。

自动解决依赖冲突。

可以区分开发依赖和生产依赖。


虚拟环境: pip:

需要手动创建和管理虚拟环境(通常与 venv 或 virtualenv 配合使用)。

Poetry:

自动为每个项目创建和管理虚拟环境。


锁文件: pip:

没有内置的锁文件机制。

可以使用 pip freeze > requirements.txt 来创建一个类似的文件。

Poetry:

使用 poetry.lock 文件确保可重现的构建。


包发布: pip:

主要用于安装包,不直接支持包的发布。

发布包需要额外的工具(如 setuptools、twine)。

Poetry:

内置了包构建和发布功能。


项目初始化: pip:

没有项目初始化功能。

Poetry:

提供 poetry new 命令来初始化新项目。


脚本运行: pip:

不提供脚本运行功能。

Poetry:

可以在 pyproject.toml 中定义和运行脚本。


学习曲线: pip:

较简单,适合初学者。

Poetry:

相对复杂一些,但提供更多高级功能。


社区支持: pip:

作为 Python 的官方包管理器,有广泛的社区支持。

Poetry:

较新,但正在快速增长,受到许多开发者的欢迎。

总的来说,pip 更简单直接,适合小型项目或快速实验。Poetry 则提供了更全面的项目管理功能,适合中大型项目或需要更严格依赖管理的场景。选择哪个工具取决于你的项目需求和个人偏好。


python pyinstaller spec文件 打包多个python文件为exe应用程序


工作中遇到要将做好的项目打包成应用程序,放在一个裸环境下运行,这就要求将项目相关的第三方库或者包打包,使得应用程序在脱离原依赖环境下,可以直接运行。这里记录一下使用spec文件打包的过程。

使用pyinstaller 单个文件打包

# 首先安装pyinstaller

pip install pyinstaller

# 执行命名 即可完成打包,生成文件名.exe程序

pyinstaller -F  文件名.py

添加图标:

pyinstaller -F  -i someicon.ico 文件名.py

使用pyinstaller 多个文件打包

这里先看下项目文件结构:

1.png


# pyinstaller安装方法同上

# 假设以测试主程序 为打包程序时候的主程序,其他py文件为可引用资源

# 执行如下命令  生成spec文件

pyi-makespec -F PaserAdapter_Test.py 

编辑PaserAdapter_Test.spec文件

# spec文件格式如下:

# -*- mode: python ; coding: utf-8 -*-

 

 

block_cipher = None

 

 

a = Analysis(

    ['ParseAdapter_Test.py','ParseAdapter.py'], # 程序中的py文件

    pathex=[],

    binaries=[],

    datas=[('templates','.'),('ParseAdapter.conf','.')], # 资源文件,以元组形式组成列表

    hiddenimports=[],

    hookspath=[],

    hooksconfig={},

    runtime_hooks=[],

    excludes=[],

    win_no_prefer_redirects=False,

    win_private_assemblies=False,

    cipher=block_cipher,

    noarchive=False,

)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

 

exe = EXE(

    pyz,

    a.scripts,

    [],

    exclude_binaries=True,

    name='ParseAdapter_Test',

    debug=False,

    bootloader_ignore_signals=False,

    strip=False,

    upx=True,

    console=True,

    disable_windowed_traceback=False,

    argv_emulation=False,

    target_arch=None,

    codesign_identity=None,

    entitlements_file=None,

)

coll = COLLECT(

    exe,

    a.binaries,

    a.zipfiles,

    a.datas,

    strip=False,

    upx=True,

    upx_exclude=[],

    name='ParseAdapter_Test',

)

执行打包命令(可指定输出程序存放的路径)

假设使用默认路径

# 使用默认输出路径,直接执行命令

pyinstaller PaserAdapter_Test.spec 

指定程序存放路径

# 指定文件输出到文件夹parseadapter下,且exe文件存在dist下,其他存在build下

pyinstaller PaserAdapter_Test.spec 

如果在windows系统打包,则dist下会有对应的.exe程序,此时可脱离其他源文件独自执行exe文件。






python pyinstaller打包及缺失dll文件及相关库解决方案

一、pyinstaller打包步骤


(1) pip install pyinstaller安装成可执行文件


(2) pyinstaller -F main.py 执行打包动作,生成可执行程序至dist目录


二、如果可执行程序执行报缺少xxx.dll文件解决方式如下:


(1) 在python安装目录下的lib/site-packages目录中找到相关的xxx.dll文件,


(2) 将xxx.dll文件拷贝到dist生成的可执行文件目录以及main.py同级目录中


(3) 修改main.py同目录中的main.spec文件,修改binaries=[('xxx.dll','./'),('xxx1.dll','./')]


(4) 执行pyinstaller -F main.spec


dist目录中新生成的可执行程序即可正确执行。


三、报缺失相关库文件


pyinstaller --onefile --paths="C:\Users\xxx\AppData\Local\Programs\Python\Python37\Lib\site-packages\cv2" main.py


通过--paths指定位置


重新执行pyinstaller -F main.py


pyinstaller -F main.spec


终结办法就是将你python系统下的lib所有文件和项目venv\lib\site-packages下所有文件copy到打包后的_internal目录下





PyCharm创建项目注意否则出现找不到模块

勾选红色标注的选项

1.png

location 是指虚拟环境的文件夹地址

base interpreter是指系统的Python解释器

inherit global site-packages 是指虚拟环境的安装包将继承系统解释器内安装的包

make availabel to all projects是指虚拟环境可以被其他项目引用


创建项目,由于没有勾选inherit global site-packages ,可以看到新建项目的解释器没有引用系统解释器的库,如果勾选将出现两个site-packages,代表引用了系统的解释器环境。

如何更换pycharm项目的解释器

进入setting界面,点击add,添加Python解释器,选择existing environment,选择自己想要的解释器即可

1.png



Python中文编程

Python中默认的编码格式是 ASCII 格式,在没修改编码格式时无法正确打印汉字,所以在读取中文时会报错。

解决方法为只要在文件开头加入 # -*- coding: UTF-8 -*- 或者 # coding=utf-8 就行了


Python程序打包的方法

使用Python写完的代码,通常要借助一些命令将它打包成为一个程序才可以运行,在Python中,可以使用第三方库pyinstaller去实现,这篇文章详细的介绍了程序打包的方法,一起来看看吧,希望大家阅读这篇文章之后能有所收获。

在Python中,想要打包一个程序的时候,需要通过命令行的方式,使用pip工具引入PyInstaller第三方库。具体的操作方法如下:

1.png


步骤1:使用快捷键的操作,将运行窗口调出,在窗口中输入cmd之后,点击确定按钮,进入到命令行窗口

2.png

步骤2:在命令后中,首先要做的事情就是安装这个第三方库,使用的命令如下:

pip install pyinstaller

当我们输入此行命令之后,回车就会自动的进入到安装的模式,当出现Successfully字样的时候,就表示已经安装成功了。


步骤3:进入到系统的文件路径中,找到存储Python文件的目录,在目录中将.ico图标文件放入,并且在此目录打开cmd命令行,在上一步打开的命令行中操作,输入命令:

PyInstaller -i 文件名.ico -F 文件名.py

输入命令然后按回车键即可,打包Successfully(成功)后,关闭命令行。如图所示

3.png

隐藏console窗口

pyinstaller -w -F main.py

pyinstaller --onefile your_script.py

--onefile是打包成一个文件

点击查看教程


PyQt打包后的exe太大?试试使用UPX进行压缩优化

以前听说PyQt打包一个print("Hello World")都能上百兆,实在是恐怖。但是感觉小陶的亲自测试,根本没那么恐怖,就像小陶写的公式识别软件,目前也就是在四五十MB这样。V1.3版本其实内容比V1.2版本更多,但是足足少了15MB,这是我在打包的时候使用了UPX进行了压缩。本文就来分享一下如何使用UPX进行压缩

下载UPX

UPX (the Ultimate Packer for eXecutables) 是一个非常全面的可执行文件压缩软件,支持 dos/exe、dos/com、dos/sys、djgpp2/coff、 watcom/le、win32/pe、rtm32/pe、tmt/adam、atari/tos、linux/i386 等几乎所有平台上的可执行文件,具有极佳的压缩比,还可以对未压缩的文件和压缩完后进行比较。它的官方发布地址在Github上。

UPX代码-Github下载页面:https://github.com/upx/upx/releases/tag/v4.0.2

鉴于Github时不时抽风,这里我提供了UPX-4.0.2-wIn64的蓝奏云下载链接,感觉64位Windows应该用的最多吧。


UPX-4.0.2-wIn64-蓝奏云下载链接 https://wwtx.lanzout.com/iTzrN0twvq3c

打包时使用UPX

把上面的压缩包解压之后,在auto-py-to-exe的Advanced里添加upx目录:


Python界面 PyQT可视化开发(python3+PyQt5+Qt Designer)

1.安装Qt Designer

        这里需要安装两个东西:PyQt5PyQt5-tools:


安装PyQt5:打开CMD或者PowerShell,在命令窗中输入

pip install -i https://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple PyQt5

执行结果如下:

1.png


安装PyQt5-tools:打开CMD或者PowerShell,在命令窗中输入

pip install -i https://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple PyQt5-tools

执行结果如下:

2.png

 2.配置开发工具

        安装完Qt Designer后,我们利用PyCharm进行界面开发,下面进行Qt开发工具的配置。

在PyCharm中依次打开:File→Settings 弹出Settings对话框,如下图

1.png



 然后按下图的4个步骤,打开Create Tools对话窗:

2.png



这里需要配置两个


(1)配置QTDesigner,用来打开QT可视化开发工具


 如下图,分别在Name、Program、Working dirctory填入如下信息:


Name:QTDesigner

Program:D:\Anaconda3\Library\bin\designer.exe

                  注意:该路径为你Python安装路径下Lib\site-packages\pyqt5_tools文件夹里

Working dirctory:$FileDir$

1.png


(2)配置PyUIC,用来将Qt Designer开发工具生成的.ui文件转换为.py文件


 如下图,分别在Name、Program、Arguments、Working dirctory填入如下信息:


Name:PyUIC

Program:D:\Anaconda3\Scripts\pyuic5.exe

                  注意:该路径为你Python安装路径下Scripts文件夹里

Arguments:$FileName$ -o $FileNameWithoutExtension$.py

Working dirctory:$FileDir$

2.png


至此,安装和配置过程全部结束,下面介绍简单的使用教程。


 3.使用Qt Designer设计界面

 在PyCharm中创建一个项目,然后点击“Tools”--“External Tools”--“QTDesinger”打开QT Desinger,如下图:

1.png


 在New Form对话框里选择Widget模板,然后点击创建:

2.png


 然后就会出现Qt Designer主界面,向Form中分别拖入一个“Push Button”和一个“Text Edit”,如下图:

1.png


 指定点击事件及其响应函数

工具栏点击 这个图标  1.png,然后光标移动到“PushButton”按钮上,鼠标左键 点击 “PushButton”按钮 不要松开,拖动光标 到 按钮旁边的任一位置后 再松开鼠标左键

2.png


随后就出现了如下界面,在对话框左侧选中“clicked()”右侧点击“Edit”

1.png


 然后点击绿色“+”按钮,指定click事件的响应函数,名称随意,比如我这里命名为“pushButton_click()”

(我们这里只是指定事件与响应函数的关联关系,函数是还没实现的,后边我们自行实现)

2.png


最后,将设计的界面保存。

4.使用PyUIC将文件转成python代码

       关闭QT Designer回到PyCharm,查看项目,可以看到只有刚才保存的PyQT_Form.ui文件而且该文件在PyCharm是打不开的,我们需要将这个文件转成.py代码才能使用。

1.png



选中“PyQT_Form”,在其上点击鼠标右键,到“External Tools”中点击“PyUIC

2.png


之后再看项目文件,就可以看到多了一个“PyQT_Form.py”,双击查看其内容如下:

3.png


 5.编写逻辑代码

       界面与业务逻辑分离实现:这一步主要实现业务逻辑,也就是点击登录和退出按钮后程序要执行的操作。为了后续维护方便,采用界面与业务逻辑相分离来实现。也就是通过创建主程序调用界面文件方式实现。这有2个好处:1.就是实现逻辑清晰2.后续如果界面或者逻辑需要变更,好维护。新建一个.py文件程序,在里边创建一个子类(MyPyQT_Form)继承PyQT_Form.py中的Ui_Form。具体代码如下:


import sys

from PyQt5 import QtWidgets

from PyQT_Form import Ui_Form

 

class MyPyQT_Form(QtWidgets.QWidget,Ui_Form):

    def __init__(self):

        super(MyPyQT_Form,self).__init__()

        self.setupUi(self)

 

    #实现pushButton_click()函数,textEdit是我们放上去的文本框的id

    def pushButton_click(self):

        self.textEdit.setText("你点击了按钮")

 

 

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)

    my_pyqt_form = MyPyQT_Form()

    my_pyqt_form.show()

    sys.exit(app.exec_())

6.运行

      至此,我们终于完成了第一个Python界面的设计,好累 ( ̄o ̄) . z Z ,运行效果如下:

1.png2.png



我自己的工具配置

1.png

2.png


pyqt5程序打包成exe文件的步骤和遇到的坑,以及如何更改exe的图标


开始打包

首先输入下面的命令,如果pyinstaller没有下载,请pip install pyinstaller

pyinstaller -F -w -i img.ico main.py

pyinstaller -F -c -i img.ico main.py

(建议先用-c,这样如果打包不成功的话可以看到哪里有错)

-F 指只生成一个exe文件,不生成其他dll文件

-w 不弹出命令行窗口

-i 设定程序图标 ,其后面的ico文件就是程序图标

main.py 就是要打包的程序

-c 生成的exe文件打开方式为控制台打开。



安装pyinstaller.exe

执行命令行:pip install pyinstaller  -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn


将python项目打包成exe与安装包的全过程

一.打包Flask项目


1.1自己写个Flask

4.png



2.2 下载pyinstaller

可选参数 示例 说明

-F pyinstaller -F demo.py 只在dist文件夹中生成一个程序demo.exe文件,适用于一个模块没有多依赖.py文件

-D pyinstaller -D demo.py 默认选项,除了主程序demo.exe外,还会在在dist文件夹中生成很多依赖文件,推荐使用这个

-c pyinstaller -c demo.py 默认选项,只对windows有效,使用控制台

-w pyinstaller -w demo.py 只对windows有效,不使用控制台

-p pyinstaller -p D:\project\demo.py 设置导入路径

-i pyinstaller -i D:\demo.ico demo.py 给生成的demo.exe文件设置一个自定义的图标


2.3 进入到项目路径下,执行

pyinstall -D run.py

4.png


2.4 运行exe,测试


运行dist下的run.exe

4.png


二.使用nsis把文件夹打包成windows的安装包


2.1 下载安装nsis


4.png


2.2把dist文件夹下的run文件夹压缩成zip


4.png


2.3使用nsis把压缩包,做成windows安装文件


4.png

5.png


4.png




总结

到此这篇关于将python项目打包成exe与安装包的文章就介绍到这了,更多相关python打包exe与安装包内容请搜索脚本之家以前的文章或继续浏览



python隐藏窗口_python中隐藏Console窗口

import ctypes

whnd = ctypes.windll.kernel32.GetConsoleWindow()

if whnd != 0:

     ctypes.windll.user32.ShowWindow(whnd, 0)

     ctypes.windll.kernel32.CloseHandle(whnd)


还有就是将要打包的文件后缀改成.pyw

执行打包

pyinstaller -F -c -i PCEQ.ico main.pyw

不打包的情况下隐藏控制台窗口可以用 pythonw来执行Python文件



关于Micropython代码保护

micropython生成中间代码,不过这个文件的扩展名是mpy,也不能在运时自动生成,需要?款软件:mpy-cross.exe,这是micropython官

方提供的,可以?python pip直接安装,安装完成后,可以运行mpy-cross.exe pythonfile,py,就可以生成一个pythonfile.mpy的字节码文

件,这文件使用跟py文件是等效的。把所有文件生成mpy,可以在一定程度上保护你的代码。

更进一步的方式:把mpy文件藏到固件中去,这不光能保护代码,还能降低程序的内存占用,官方的描述如下:

大体的意思是这样的:运行py文件不能有效的使用内存资源,因为代码执行时需要被编译成bytecode代码,该编译需要RAM,生成的字节

码也存在RAM中,这就降低了可用内存。并且存储py文件需要文件系统,而一些嵌?设备本身不存在文件系统。

嗯,神?样的保护功能,不但降低了内存占用,还把字节码藏到了固件中,代价就是需要自己编译micropython固件。


用PySide写界面

我的安装与配置使用pyside2

pip install  pyside2  -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn

1.png

Name:PySide-Desiger

Description:PySide-Designer

Programe: C:\Python38\Lib\site-packages\PySide2\designer.exe

Arguments:

Working directory:$FileDir$


2.png

Name:PySide-UIC

Description:PySide-UIC

Programe: C:\Python38\Scripts\pyside2-uic.exe

Arguments:$FileName$ -o $FileNameWithoutExtension$.py

Working directory:$FileDir$



PySide使用

安装方式用pip直接安装 pip install  pyside6  -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn 即可


对于用PySide写界面,官方给了几种用法,选了比较简单的两种


方法一. 直接上手写Python代码,布局靠代码写


方法二. 可视化的拖拽界面,然后生成.ui文件


        a. 代码里加载.ui作为布局来用


        b. 把.ui转py代码


为了省事博主当然选方法二 b选项啦。那么就需要用到pyside-designer和pyside6-uic这两个工具了,用pyside-designer拖拽组件生成.ui文件,用pyside6-uic把.ui文件生成.py文件。为了方便,可以在pycharm里配置一下,这样可以直接在pycharm启动工具。当然不配置也是可以的,就是调用时麻烦点


配置PyCharm

官网文档给了配置方法,但是对PyCharm配置描述超简陋,博主还是网上参看其他人分享文章来配置。最终配置完的截图如下

1.png

2.png




 配置好后,现在右键菜单中就会出现对应选项,菜单中点击PySide-Designer,就会在右键所在文件上启动PySide-Designer。菜单中点击呢PySide6-UIC,就会在右键所在文件上执行.ui生成.py 

1.png

PySide-Designer拼界面

正常打开的话PySide-Designer是这样

1.png



如果是想打开已有.ui文件,那就关掉中心的提示界面,从左上打开一个界面,博主创建个MainWindow,拖个简单的TabWidget到界面上,注意这里删掉了拖拽后自带的两个tab

2.png



然后点击左上角的保存,现在.ui文件生成好了,关闭PySide-Designer,回到PyCharm


PySide-UIC生成py文件并以此基础写逻辑

在生成好的ui文件上右键,点击刚才配置好的PySide6-UIC,会生成一个对应名字的py文件。可以打开看下,如下图

1.png



 注意图中画红框的地方,一个是.ui生成.py每次重新生成,如果有手工改动注意进行版本控制保存修改。另外就是生成的代码本身不是QWidget组件,而是一个类似拼装界面的工具类,这里提供了两个函数,setupUI接收实际的MainWindow并添加组件,retranslateUI提供对UI文件的重新赋值,用于多语言情况。


此时主界面的tab已经加好了,主界面的tabWidget使用addTab的方式添加tab窗口,如下图

1.png



现在要给主界面加一个实际有用途的tab了,还是一样的方法,拼.ui,生成.py。这次要写逻辑,当然逻辑很简单,只是给界面上加了按钮的点击回调,如下图

2.png



最终效果如下,一个简易的界面就实现好了

3.png




Python 请求登录接口后获取cookie

import requests

import urllib.request

import http.cookiejar


# 1. 登录系统,获取Cookie值


cookiejar = http.cookiejar.CookieJar()  #构建一个CookieJar对象实例来保存cookie  【CookieJar是对于Cookie类的一个类似管理类的封装】

handler = urllib.request.HTTPCookieProcessor(cookiejar)     #使用HTTPCookieProcessor()来创建cookie处理器对象,参数为CookieJar()对象

opener = urllib.request.build_opener(handler)   #通过build_opener()来构建opener


# headers、请求登录接口,传递地址和参数

headers = {

    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0',

    'Accept': 'application/json, text/javascript, */*; q=0.01',

    'Accept-Encoding': 'gzip, deflate',

    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',

    'Connection': 'keep-alive',

    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'

}

url_login = 'http://xxxx.xxx.xxxx/login'

FormData = {'username':'xxxxxxx', 'password':'xxxxx'}     #<class 'dict'>

postdata = urllib.parse.urlencode(FormData).encode()    #<class 'bytes'>


request = urllib.request.Request(url_login, postdata)

response = opener.open(request) #访问系统地址,访问之后会自动保存cookie到cookiejar中

for item in cookiejar:

    Cookie = '%s=%s' % (item.name, item.value)

headers['Cookie'] = Cookie  # 向headers中追加Cookie,没有Cookie值,系统会认为用户尚未登录


# 2. 下载模板文件

url = 'http://xxxx.xxx.xxxx/downloadTemplate?templateName=userTemplate.xlsx'

r = requests.get(url, headers=headers)

with open("userTemplate.xlsx", "wb") as code:

    code.write(r.content)



在Windows平台进行OpenCV开发,需要先进行以下步骤配置开发环境

1. 下载安装Python:从Python官网下载对应版本的Python安装包,下载完成后进行安装。


2. 安装pip:pip是Python的包管理工具,可以用于安装第三方库。在命令行中执行以下命令安装pip:


   ```

   python get-pip.py

   ```


3. 安装OpenCV:可以使用pip命令安装,执行以下命令:


   ```

   pip install opencv-python  -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn 

   ```


   如果需要使用OpenCV的GUI模块,可以安装opencv-contrib-python:


   ```

   pip install opencv-contrib-python  -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn 

   ```


4. 配置环境变量:将Python和OpenCV的路径添加到系统环境变量中,方便使用。具体步骤如下:


   (1) 在电脑上右键点击“此电脑”,选择“属性”,进入系统属性界面。


   (2) 点击左侧的“高级系统设置”,进入“系统属性”窗口。


   (3) 点击“环境变量”按钮,进入“环境变量”窗口。


   (4) 在“用户变量”或“系统变量”中选择“Path”,点击编辑。


   (5) 在“变量值”中添加Python和OpenCV的路径,用分号进行分隔。例如:


       ```

       C:\Python37\;C:\Python37\Scripts\;C:\opencv\build\x64\vc15\bin

       ```


5. 测试OpenCV和Python是否配置成功:打开Python交互式命令行工具,输入以下代码,应该能够正确显示摄像头的视频画面。


   ```

   import cv2

   cap = cv2.VideoCapture(0)

   while True:

       ret, frame = cap.read()

       cv2.imshow('frame', frame)

       if cv2.waitKey(1) & 0xFF == ord('q'):

           break

   cap.release()

   cv2.destroyAllWindows()

   ```


# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.

import cv2

def HelloOpenCV():

    # Use a breakpoint in the code line below to debug your script.

    print("hello")

    cap = cv2.VideoCapture("e:\\1.mp4")

    while True:

        ret, frame = cap.read()

        cv2.imshow('frame', frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):

            break

    cap.release()

    cv2.destroyAllWindows()


# Press the green button in the gutter to run the script.

if __name__ == '__main__':

    HelloOpenCV()


# See PyCharm help at https://www.jetbrains.com/help/pycharm/



Python我二维码识别

import cv2


    qrcode_filename = "g:/1.png"

    qrcode_image = cv2.imread(qrcode_filename)

    qrCodeDetector = cv2.QRCodeDetector()

    data, bbox, straight_qrcode = qrCodeDetector.detectAndDecode(qrcode_image)


第四行代码中qrCodeDetector.detectAndDecode(qrcode_image)有三个返回值。其中data是解码后的内容,如我们打印输出结果所示。

bbox是指二维码轮廓的四个角,从左上角顺时针转的。而straight_qrcode是二维码的原始排列,也就是每个点是0还是255的一个矩阵,白色是255,黑色是0。



Python串口操作

这里给出一个Python3使用pyserial库操作串口的简单例子:


首先需要安装pyserial,可以使用pip安装:


```

pip install pyserial

```


然后就可以使用这个库了。下面是一个简单的例子,假设我们要连接到一个串口,发送一条命令,并等待返回值:


```python

import serial

import time


# 打开串口

ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)


# 发送命令

ser.write(b'hello\n')


# 等待返回值

response = ser.readline().decode().strip()

print(response)


# 关闭串口

ser.close()

```


这里我们假设连接的串口是`/dev/ttyUSB0`,波特率为9600。我们使用`ser.write()`方法发送一条字符串`hello\n`,其中`\n`表示换行符。然后使用`ser.readline()`方法等待返回值,这个方法会阻塞直到读取到一个完整的行。最后打印返回值,并使用`ser.close()`关闭串口。


当然,这只是一个简单的例子,使用pyserial可以实现更多串口通讯的功能,比如异步通讯、自动重连、错误处理等等。如果需要更复杂的功能,可以参考pyserial的官方文档:https://pyserial.readthedocs.io/en/latest/pyserial.html




一个使用Python操作串口进行读写数据的示例。这个例子使用了pyserial库,它是Python中最常用的串口通信库之一。首先,您需要安装pyserial库:

pip install pyserial


以下是一个包含读写数据的Python串口操作示例:


import serial

import time


# 配置串口

ser = serial.Serial(

    port='/dev/ttyUSB0',  # 串口名称,在Windows下可能是'COM1'

    baudrate=9600,        # 波特率

    parity=serial.PARITY_NONE,

    stopbits=serial.STOPBITS_ONE,

    bytesize=serial.EIGHTBITS,

    timeout=1

)


try:

    # 检查串口是否打开

    if ser.isOpen():

        print("串口已打开")

        

        # 写数据

        data_to_send = "Hello, Serial Port!"

        ser.write(data_to_send.encode())

        print(f"发送数据: {data_to_send}")

        

        # 等待一下,确保数据被发送

        time.sleep(0.5)

        

        # 读数据

        while True:

            if ser.in_waiting > 0:

                received_data = ser.read(ser.in_waiting).decode('utf-8')

                print(f"接收到数据: {received_data}")

                break

        

except serial.SerialException as e:

    print(f"串口错误: {e}")

    

finally:

    # 关闭串口

    ser.close()

    print("串口已关闭")


这个脚本做了以下几件事:

导入必要的库:serial用于串口通信,time用于添加延时。

配置并打开串口。您可能需要根据实际情况调整端口名称和波特率。

发送一串数据到串口。

等待一小段时间,确保数据被发送。

尝试从串口读取数据。这里使用了一个while循环来等待数据到达。

如果发生错误,捕获并打印错误信息。

最后,无论是否发生错误,都确保关闭串口。

请注意,这只是一个基本示例。在实际应用中,您可能需要根据具体需求进行调整,比如添加更复杂的错误处理、实现持续的读写操作、或者根据特定协议解析接收到的数据等。

另外,在使用这段代码时,请确保您有适当的权限访问串口设备,并且没有其他程序正在使用该串口



Python Socket服务端与客户端例子

以下是Python Socket服务端与客户端的例子代码:


服务端代码:


```python

import socket


# 创建 TCP/IP socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


# 绑定服务端IP地址和端口

server_address = ('localhost', 8888)

server_socket.bind(server_address)


# 监听客户端连接请求

server_socket.listen(1)


while True:

    print('等待客户端连接...')

    client_socket, client_address = server_socket.accept()

    print(f'客户端 {client_address} 已连接')


    # 接收并输出客户端发送的数据

    data = client_socket.recv(1024)

    print(f'客户端 {client_address} 发送的消息是:{data.decode()}')


    # 发送消息给客户端

    message = '欢迎连接到服务器'

    client_socket.sendall(message.encode())


    # 关闭客户端连接

    client_socket.close()

```


客户端代码:


```python

import socket


# 创建 TCP/IP socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


# 连接服务端IP地址和端口

server_address = ('localhost', 8888)

client_socket.connect(server_address)


# 向服务端发送消息

message = 'Hello, server!'

client_socket.sendall(message.encode())


# 接收并输出服务端发送的消息

data = client_socket.recv(1024)

print(f'服务端发送的消息是:{data.decode()}')


# 关闭客户端连接

client_socket.close()

```


以上代码可以在两个Python脚本中分别运行,实现服务端与客户端之间的通信。首先在终端中启动服务端脚本,再分别启动两个客户端脚本,即可看到服务端与客户端之间的交互信息。


python蓝牙通讯代码

以下是Python蓝牙通讯代码示例:


```python

import bluetooth


# 搜索蓝牙设备

print("Searching for devices...")

devices = bluetooth.discover_devices(duration=8, flush_cache=True, lookup_names=True)


# 打印搜索到的设备

print("Found %d devices" % len(devices))

for addr, name in devices:

    print("  %s - %s" % (addr, name))


# 连接到指定的设备

addr = "00:11:22:33:44:55"  # 替换为要连接的设备地址

port = 1  # 根据设备类型和蓝牙协议确定端口号

socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)

try:

    socket.connect((addr, port))

    print("Connected to %s" % addr)

except bluetooth.BluetoothError as e:

    print("Error: %s" % e)


# 发送和接收数据

try:

    while True:

        # 发送数据

        data = input("Send message: ")

        socket.send(data)


        # 接收数据

        data = socket.recv(1024)

        print("Received message: %s" % data)

except KeyboardInterrupt:

    pass


# 关闭连接

socket.close()

print("Disconnected from %s" % addr)

```


注意:此示例代码仅供参考,具体实现可能因不同设备和协议而异。请根据实际情况进行修改和调整。



Python GUI 库

没有最好的只有更适合的,下面简单看一下 Python GUI 库,通过各个库的优缺点,就可以选择更适合的了。

Pyside6:Pyside是QT公司官方提供的Python包,上一版本为Pyside2,对应的是QT5,最新版命名规则进行了调整,更改为Pyside6,对应的是QT6版本。由于官方出品的比较看好,缺点是发布比较晚,网上的资料没有PyQt5多。

# 安装Pyside6 

pip install Pyside6

查看原文 

Python默认的包地址在win11中类似如(各人电脑不同目录不同):C:\Users\xn\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\PySide6\designer.exe

添加Qt Designer到External Tools

Qt Designer配置External Tools

1.png


Name:QtDesigner

Description:QtDesigner

Program:{虚拟环境路径}\Lib\site-packages\PySide6\designer.exe

Working directory:$ProjectFileDir$\ui


Working directory中填写$ProjectFileDir$\ui将会使当前的项目下的ui文件夹作为工作目录,因此需要在项目下创建一个ui的目录,否则无法使用。使用ui文件夹是为了更好的目录结构,如果不需要,可以直接使用$ProjectFileDir$,这样无需创建ui文件夹

2.png



使用方式一

在目录结构中右键,可以使用External Tools中的QtDesigner

1.png

后面需要配置的pyside6-uic.exe与pyside6-rcc.exe也是类似的使用方式。

添加uic到External Tools

pyside6-uic.exe:能够将Qt Designer中的.ui文件转换为.py文件的工具,在后期的开发中会经常用到。

这个工具默认如win11类似目录(各人电脑不同目录不同):

C:\Users\xn\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\Scripts

1.png


Name:ui2py

Program:{虚拟环境路径}\Scripts\pyside6-uic.exe

Arguments:-o $FileNameWithoutExtension$_ui.py $FileName$

Working directory:$ProjectFileDir$\ui



添加rcc到External Tools 

pyside6-rcc.exe:能够将Qt Designer中的.qrc文件转换为.py文件的工具,它的使用率没有uic高,但也非常常用。.qrc是资源文件,在Qt Designer中,所有的图片(任何外部元素都可以)都是资源,通过资源来加载各种外部内容。

1.png


Name:qrc2py

Program:{虚拟环境路径}\Scripts\pyside6-rcc.exe

Arguments:$FileName$ -o ../$FileNameWithoutExtension$_rc.py

Working directory:$ProjectFileDir$\ui


在这个Arguments中使用的参数效果会与之前的uic有所不同,它会将.qrc转换的.py文件生成在ui文件外部,例如以下的目录结构:

1.png


如此操作的原因主要是uic生成的.py文件中,使用资源文件的方式为import xxx_rc,为了后期不会爆红使用方便,因此将.qrc转换的.py文件生成在最外层。


如下图,uic生成的.py文件中,调用资源文件使用的是import,如果将资源文件生成在ui文件夹中,需要每次在uic生成的.py文件中手动修改为from ui import xxx_rc,好在一个项目一般只使用一个资源文件,因此将其放置在最外层不会造成太大影响。

1.png

总结 

至此,开发Qt项目的前期工作已经完成,后期的流程主要为:Qt Designer设计原型->将原型保存为.ui并转化为.py->使用信号与槽连接各种逻辑->发布应用。


因此很多学习内容将围绕Qt Designer的使用,控件的属性介绍与如何使用进行展开






PySimpleGUI:PySimpleGUI 是 Tkinter 一层包装。使用 PySimpleGUI 实现自定义 GUI 所需的代码量要比使用 Tkinter 直接编写相同的 GUI 要少得多。

Wax:基于wxPython ,为克服wxPython的问题而制作的一个包。

Kivy:主要针对多点触控程序,智能手机平板等,也可以在没有触屏功能的系统上,全平台支持(Windows, Linux, Mac OS X, Android and iOS.)使用Python和cython编写,中文支持差,需要自己下载中文库并且制定路径。

BeeWare:Write once. Deploy everywhere.需要与Kivy配合使用。

Toga:一个使用Python开发原生APP的GUI工具包。Toga由一个具有共享接口的基础组件库组成,以简化与平台无关的GUI开发。Toga适用于Mac OS、Windows、Linux(GTK)以及Android和iOS等移动平台。

Eel:一个轻量的 Python 库,用于制作简单的类似于 Electron(但是比它更轻量) 的离线 HTML/JS GUI 应用程序,并具有对 Python 功能(capabilities)和库的完全访问权限。

Flexx:一个纯 Python 工具包,用来创建图形化界面应用程序。其使用 Web 技术进行界面的渲染。你可以用 Flexx 来创建桌面应用,同时也可以导出一个应用到独立的 HTML 文档。因为使用纯 Python 开发,所以 Flexx 是跨平台的。只需要有 Python 和浏览器就可以运行。

pywebview:是围绕 webview 组件的轻量型跨平台包装器(wrapper),它允许在其自己的本机 GUI 窗口中显示 HTML 内容。它使您可以在桌面应用程序中使用 Web 技术,同时尽最大可能隐藏使用浏览器构建GUI的事实。

enaml:一种能够让你用最小的努力就可以实现高质量GUI界面的的Python框架,也是一种独特的编程语言。enaml将声明性语言与基于约束的布局系统结合在一起,使用户可以轻松地定义灵活布局的UI。enaml应用程序可以在任何支持Python和Qt的平台上运行。

wxPython:wxPython是一款开源软件,是Python语言的一套优秀的 GUI 图形库,我们可以很方便的创建完整的、功能健全的 GUI 用户界面,wxPython 算是个压缩版的QT,但是该有的功能却完全不缺失。wxPython 文档较少,遇到问题不好解决,代码布局控件,不直观。

Tkinter:Python内置的GUI框架,使用它的时候不用安装额外的扩展包,直接import,跨平台,Tk8.0 的后续版本可以实现本地窗口风格,因为Tkinter是Python自带的图形库,所以它的性能不是很差,也更容易学习。Tkinter的缺点就是:如果你想要构建一个GUI界面布局,你就必须自己写代码,因为Tkinter没有提供一个图形界面设计器,我估计这也是很多人没有选择它来做软件的一个最主要原因。

PyQt5:Qt库是最强大的GUI库之一,Qt强大之处在于网上有很多PyQt的资源,而且Qt技术已经相当成熟,PyQt是采用基本和Qt一致的API,因此之前使用过Qt的人,转移到PyQt很容易,这也是我们学习编程的始终强调的一点,一通百通,当你一门语言学习扎实了,学透了,那么转移到其它语言是非常容易的。它有620多个类和6000个函数和方法,可以运行在所有主要的操作系统,包括UNIX、Windows、Mac OS。

PyQtGraph主要使用领域:数学/科学/工程应用等;

PyQtGraph为PyQt5/PyQt6/PySide2等图形用户界面 (GUI) 开发框架的一款强大可视化工具,底层为NumPy (快速科学计算)、Qt的GraphicsView框架 (2D图形可视化)、OpenGL (3D图形可视化);

相比于之前的工具,PyQtGraph在以下方面尤其出色:

界面修图 (点一点即可修改);

快速成图 (底层为NumPy);

实时绘制数据;

医学影像图展示 (如MRI);

交互图快速制作 (数据选择、标记、小部件);

对python/qt程序员更友好;

完美支持 Linux, Windows和OSX;

纯python编写,比pyqwt更易于移植等

安装

pip install pyqtgraph -i  https://pypi.tuna.tsinghua.edu.cn/simple



使用ffmpeg+opencv读取摄像头并推流到rtmp服务器


1、环境


python3


OSX 12.5


vscode


 


2、安装ffmpeg


brew install ffmpeg

 


3、安装cv2


pip install opencv-python

# or

pip --default-timeout=100 install opencv-python -i https://pypi.douban.com/simple

 


4、脚本


复制代码

import cv2

 

# subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。

import subprocess

 

# 视频读取对象

cap = cv2.VideoCapture(0)

 

# 推流地址

rtmp = "rtmp://192.168.10.225:1935/stream/example"# 推流的服务器地址

 

# 设置推流的参数

command = ['ffmpeg',

           '-y',

           '-f', 'rawvideo',

           '-vcodec', 'rawvideo',

           '-pix_fmt', 'bgr24',

           '-s', '1280*720',  # 根据输入视频尺寸填写

           '-r', '25',

           '-i', '-',

           '-c:v', 'h264',

           '-pix_fmt', 'yuv420p',

           '-preset', 'ultrafast',

           '-f', 'flv',

           rtmp]

 

 

# 创建、管理子进程

pipe = subprocess.Popen(command, stdin=subprocess.PIPE)

size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))

 

# 循环读取

while cap.isOpened():

    # 读取一帧

    ret, frame = cap.read()

    if frame is None:

        print('read frame err!')

        continue

 

    # 显示一帧

    # fps = int(cap.get(cv2.CAP_PROP_FPS))

    cv2.imshow("frame", frame)

 

    # 按键退出

    if cv2.waitKey(1) & 0xFF == ord('q'):

        break

 

    # 读取尺寸、推流

    img = cv2.resize(frame, size)

 

    pipe.stdin.write(img.tobytes())

    

 

# 关闭窗口

cv2.destroyAllWindows()

 

# 停止读取

cap.release()

复制代码

备注:分辨率要根据本机摄像头支持的分比率设置,不然可能显示不出图像



车辆识别源码

以下是一个基本的Python代码,使用OpenCV库来同时识别多个车牌:



import cv2

import numpy as np


# Load Haar cascade for cars and license plates identification

car_cascade = cv2.CascadeClassifier('cars.xml')

plate_cascade = cv2.CascadeClassifier('plates.xml')


# Load image and convert it to grayscale

img = cv2.imread('cars.jpg')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)


# Detect cars in the image

cars = car_cascade.detectMultiScale(gray, 1.1, 3)


# For each car detected, detect license plates

for (x,y,w,h) in cars:

    # Crop the car region from the image

    car = img[y:y+h, x:x+w]

    # Convert the car region to grayscale

    car_gray = cv2.cvtColor(car, cv2.COLOR_BGR2GRAY)

    # Detect license plates in the car region

    plates = plate_cascade.detectMultiScale(car_gray, 1.1, 3)

    # For each license plate detected, draw a rectangle around it

    for (px,py,pw,ph) in plates:

        cv2.rectangle(car, (px,py), (px+pw,py+ph), (0,255,0), 2)

    # Draw a rectangle around the car

    cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)


# Show the results

cv2.imshow('image', img)

cv2.waitKey(0)

cv2.destroyAllWindows()


这段代码首先加载用于车辆和车牌识别的Haar级联分类器文件,然后加载图像并将其转换为灰度图像。然后,他在图像中检测车辆,并为每辆车检测车牌。对于每张检测到的车牌,它会绘制一个矩形框,最后它会在每辆车周围绘制一个矩形框,并显示结果。


wxPython UI 开发

wxPyhon 可以用wxFormBuilder开发工具开发

pip install -U wxPython



tkinter UI开发

tkinter作为python自带的库其实是于python融合得最好的。不仅无需额外安装任何东西,而且它非常稳定,10年前的python版本改几条命令都可以直接运行。所以,它其实更加适合一些对稳定性要求高,但对界面友好没有很高要求的工业界的项目。

1.png

学习链接1

学习链接2

Tkinter常用组件使用介绍

上述为大家列出了Tkinter中的常用组件。各个组件有其相对应的功能和用法,而我们可以根据我这些控件自由组合来展现出我们想要的效果。这里先给大家简单介绍一下常用的,也是本文小工具里会用到的一些组件。


1. Button(按钮控件)

Button组件是用来触发事件的,它会在窗口上以按钮的方式显示,在你单击它的时候就会运行与Button绑定的函数。


from tkinter import *


def b1():    root['bg'] = "blue"

    

root = Tk()

str1 = "Hello World"

Button(root,text = str1,command = b1).place(x=0,y=0)

root.mainloop()


效果如下:1.jpg


2. Label(标签控件)

Label是标签控件,能在指定的窗口中显示的文本和图像。


from tkinter import *


root = Tk()

str1 = "Hello World"

Label(root,text=str1).place(x=0,y=0)

root.mainloop()


效果如下:1.png


3. Entry(文本框)

Entry是文本框,用来让用户输入一行文本字符串,也可以用来显示文本字符串。在使用Entry的时候,我们需要用到一个函数stringVar(),这个函数定义的变量会被一直追踪,当你想要获得该变量当时的值,只需要用该变量的get函数。


from tkinter import *


def b1():

    print(str1.get())


root = Tk()

str1 = StringVar()

str1.set("Hello world")

Entry(root,textvariable = str1).place(x=0,y=5)

Button(root,text = "输出",command=b1).place(x=150,y=0)

root.mainloop()


效果如下:1.jpg


Tkinter常用几何管理方法使用介绍

上面介绍了一些组件,下面再为大家介绍一下Tkinter的一些几何管理方法。所谓的几何管理方法就是用来组织和管理整个父配件区中子配件的布局的。正是有这些几何方法的存在,我们才可以随心所欲的,将组件安排在我们想让它出现的地方,所有的Tkinter组件都包含专用的几何管理方法。


1. pack()

pack几何管理,采用块的方式组织配件,常用在开发简单的界面。pack几何管理程序根据组件创建生成的顺序,将组件添加到父组件中去。如果不指定任何选项,默认在父窗体中自顶向下添加组件。


from tkinter import *


root = Tk()

Button(root,text='1',width=5).pack()

Button(root,text='2',width=5).pack()

Button(root,text='3',width=5).pack(side="left")

Button(root,text='4',width=5).pack(side = "right")

Button(root,text='5',width=5).pack(side = "bottom")

Button(root,text='6',width=5).pack()

root.mainloop()


效果如下:1.png


2. grid()

grid几何管理,采用表格结构组织配件,通过行(row)列(column)来确定组件摆放的位置,这也是它的两个重要的参数。


from tkinter import *


root = Tk()

Button(root,text='1',width=5).grid(row=0,column=0)

Button(root,text='2',width=5).grid(row=0,column=1)

Button(root,text='3',width=5).grid(row=0,column=2)

Button(root,text='4',width=5).grid(row=1,column=0)

Button(root,text='5',width=5).grid(row=1,column=1)

Button(root,text='6',width=5).grid(row=2,column=0)

root.mainloop()


效果如下:1.png


3. place()

place几何管理,允许指定组件的大小以及位置,通过x和y的数值来确定组件的位置,通过width和height来确定组件的大小。


from tkinter import *


root = Tk()

Button(root,text='1',width=5).place(x=80,y=0)

Button(root,text='2',width=5).place(x=80,y=170)

Button(root,text='3',width=5).place(x=0,y=85)

Button(root,text='4',width=5).place(x=155,y=85)

Button(root,text='5',width=5).place(x=70,y=70,width=60,height=60)

root.mainloop()


效果如下:1.png


Tkinter实现弹窗提示

有时候,我们想要实现一个弹窗提示功能。此时,可以使用tkinter.messagebox,这个也是Python自带的。在我们需要实现弹窗功能的时候,只需要将其导入,使用对应的函数,再输入对应的参数,一个小弹窗就做好了。


from tkinter import *

import tkinter.messagebox


def b1():

    tkinter.messagebox.showinfo(title="危险", message="出错了")


root = Tk()

Button(root,text='1',command =b1 ).pack()

root.mainloop()


效果如下:1.jpg


Tkinter案例讲解

通过上述对Tkinter的讲解,我们基本可以上手制作一个属于自己的界面窗口了。最后,还是通过一个案例讲解,为大家讲述它的使用。需求: 我写了一段自动化整理文件夹的窗口,但是想要制作一个界面,能够随意选择路径,当我点击桌面一键整理后,就可以完成文件夹的自动整理。下面是最后制作的界面效果,基本功能都实现了,同时还多出了两个功能,复制所有文件名和时间提示,是不是很棒?1.png关于自动化整理文件夹的程序,黄同学已经给出了。我在他的基础上,结合Tkinter,最终制作出了这个界面。完整代码如下:


from tkinter.filedialog import askdirectory

from tkinter import *

import time

import datetime

import os

import shutil

import pyperclip

import tkinter.messagebox


class MY_GUI():

    def __init__(self,init_window_name):

        self.init_window_name = init_window_name

        self.path = StringVar()


    def selectPath(self):           #选择路径

        path_ = askdirectory()

        self.path.set(path_)


    def error(self):    #错误弹窗

        tkinter.messagebox.showinfo(title="提示", message="未选择路径")


    def uptime(self):   #更新时间

        TimeLabel["text"] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S:') + "%d" % (datetime.datetime.now().microsecond // 100000)

        self.init_window_name.after(100, self.uptime)


    #设置窗口

    def set_init_window(self):

        self.init_window_name.title("数据分析与统计学之美")                   #窗口标题栏

        self.init_window_name.geometry('300x300+600+300')               #窗口大小,300x300是窗口的大小,+600是距离左边距的距离,+300是距离上边距的距离

        self.init_window_name.resizable(width=FALSE, height=FALSE)      #限制窗口大小,拒绝用户调整边框大小

        Label(self.init_window_name, text="欢迎使用一键整理小工具",bg="SkyBlue",fg = "Gray").place(x=80,y=10)  #标签组件,用来显示内容,place里的x、y是标签组件放置的位置

        Label(self.init_window_name, text="当前路径:",bg="SkyBlue").place(x=15,y=50)    #标签组件,用来显示内容,place里的x、y是标签组件放置的位置

        Entry(self.init_window_name, textvariable=self.path).place(x=75,y=50)           #输入控件,用于显示简单的文本内容

        Button(self.init_window_name, text="路径选择", command=self.selectPath,bg="SkyBlue").place(x=225,y=45)      #按钮组件,用来触发某个功能或函数

        Button(self.init_window_name, text="桌面一键整理",bg="SkyBlue",command=self.option).place(width=200,height=50,x=50,y=100) #按钮组件,用来触发某个功能或函数

        Button(self.init_window_name, text="复制所有文件名",bg="SkyBlue", command=self.option1).place(width=200, height=50, x=50, y=180)   #按钮组件,用来触发某个功能或函数

        self.init_window_name["bg"] = "SkyBlue"                                   #窗口背景色

        self.init_window_name.attributes("-alpha",0.8)                          #虚化,值越小虚化程度越高

        global TimeLabel    #全局变量

        TimeLabel = Label(text="%s%d" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S:'), datetime.datetime.now().microsecond // 100000),bg="SkyBlue")   #标签组件,显示时间

        TimeLabel.place(x=80, y=260)

        self.init_window_name.after(100,self.uptime)


    def arrangement(self,str1):         #整理文件

        file_dict = {

            '图片': ['jpg', 'png', 'gif', 'webp'],

            '视频': ['rmvb', 'mp4', 'avi', 'mkv', 'flv'],

            "音频": ['cd', 'wave', 'aiff', 'mpeg', 'mp3', 'mpeg-4'],

            '文档': ['xls', 'xlsx', 'csv', 'doc', 'docx', 'ppt', 'pptx', 'pdf', 'txt'],

            '压缩文件': ['7z', 'ace', 'bz', 'jar', 'rar', 'tar', 'zip', 'gz'],

            '常用格式': ['json', 'xml', 'md', 'ximd'],

            '程序脚本': ['py', 'java', 'html', 'sql', 'r', 'css', 'cpp', 'c', 'sas', 'js', 'go'],

            '可执行程序': ['exe', 'bat', 'lnk', 'sys', 'com'],

            '字体文件': ['eot', 'otf', 'fon', 'font', 'ttf', 'ttc', 'woff', 'woff2']

        }

        os.chdir(str1)

        folder = os.listdir('.')

        for each in folder:

            #print(each.split('.')[-1])

            for name,type in file_dict.items():

                if each.split('.')[-1] in type:

                    if not os.path.exists(name):

                        os.mkdir(name)

                    shutil.move(each,name)

        tkinter.messagebox.showinfo(title="提示", message="整理完成")


    def copy(self,str1):

        os.chdir(str1)

        folder = os.listdir('.')

        str1 = ''

        for each in folder:

            type = '.' + each.split('.')[-1]

            str1 = str1 + each.replace(type,'') + '\n'

        pyperclip.copy(str1)

    #获取当前时间

    def get_current_time(self):

        current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))

        return current_time


    def option(self):

        if self.path.get() == "":

            self.error()

        else:

            self.arrangement(self.path.get())


    def option1(self):

        if self.path.get() == "":

            self.error()

        else:

            self.copy(self.path.get())



if __name__ == '__main__':

    init_window = Tk()  # 实例化出一个父窗口

    ZMJ_PORTAL = MY_GUI(init_window)

    ZMJ_PORTAL.set_init_window()

    init_window.mainloop()


从代码里可以看到,为了窗口的整体美观,我们规定了窗口的大小。


#窗口大小,300x300是窗口的大小,+600是距离左边距的距离,+300是距离上边距的距离

self.init_window_name.geometry('300x300+600+300')               



还设置了窗口的基本属性。


#窗口标题栏

self.init_window_name.title("数据分析与统计学之美")    


#限制窗口大小,拒绝用户调整边框大小   

self.init_window_name.resizable(width=FALSE, height=FALSE)  

  

#虚化,值越小虚化程度越高  

self.init_window_name.attributes("-alpha",0.8)      


#窗口背景色                    

self.init_window_name["bg"] = "SkyBlue"                                   



所有组件都用了place几何方法,将组件的大小及布局,进行了良好的规划。同时,Button组件也都与其对应的功能函数,进行了链接。


#标签组件,用来显示内容,place里的x、y是标签组件放置的位置

Label(self.init_window_name, text="欢迎使用一键整理小工具",bg="SkyBlue",fg = "Gray").place(x=80,y=10)  


#标签组件,用来显示内容,place里的x、y是标签组件放置的位置

Label(self.init_window_name, text="当前路径:",bg="SkyBlue").place(x=15,y=50)  


#输入控件,用于显示简单的文本内容

Entry(self.init_window_name, textvariable=self.path).place(x=75,y=50)  


#按钮组件,用来触发某个功能或函数         

Button(self.init_window_name, text="路径选择",command=self.selectPath,bg="SkyBlue").place(x=225,y=45)    


#按钮组件,用来触发某个功能或函数

Button(self.init_window_name, text="桌面一键整理",bg="SkyBlue",command=self.option).place(width=200,height=50,x=50,y=100) 


#按钮组件,用来触发某个功能或函数

Button(self.init_window_name, text="复制所有文件名",bg="SkyBlue", command=self.option1).place(width=200, height=50, x=50, y=180)   


如果路径为空的时候,需要制作一个弹窗提示:未选择路径。


def option(self):

    if self.path.get() == "":

        self.error()

    else:

        self.arrangement(self.path.get())

           

           

def error(self):    #错误弹窗

 tkinter.messagebox.showinfo(title="提示", message="未选择路径")


程序打包与代码获取

程序打包我采用的是pyinstaller命令,在使用该命令前,最重要的当然就是安装啦!


#安装

pip install pyinstaller


#打包指令

pyinstaller -F 数据分析与统计学之美.py


在命令终端输入安装命令,下载安装pyinstaller。下载完成后再切换到存放.py文件的目录里,运行打包指令,待运行结束,我们可以看到目录里多出了一些文件夹和文件。

1.png




创建第一个tkinter程序


创建程序的第一步,一定是要导入tkinter模块,通常我们会用以下两种方式:


from tkinter import *

import tkinter as tk

如果采用第一种,在引用组件时不需要加前缀,第二种方式则必须加上tk.的前缀。当然,这两种引用方式仅限于tkinter本身的模块,不包括上文提到的扩展模块。以ttk为例,它的导入方式应该为:


from tkinter import ttk

第二步,创建控件实例:


root = Tk()

第三步,进入事件循环:


root.mainloop()

mainloop方法一定要放在最后执行。如果我们把tkinter程序看成一本连环画的话,那么mainloop方法就是翻阅连环画的动作,没有它是无法实现连环画效果的。也就是说,如果你想要设计并布局一个界面,其内容应该放在创建控件实例和进入事件循环之间:


from tkinter import *


root = Tk()

'''

在这里实现界面

'''

root.mainloop()



我们创建的一个空白界面,它没有任何的功能。tkinter默认的窗口大小是200x200,默认标题是tk。我们可以用下面的方法获取它的大小:


from tkinter import *


root = Tk()

root.update()  #一定要刷新界面,否则打印出的值是1

print("当前窗口的宽度为",root.winfo_width())

print("当前窗口的高度为",root.winfo_height())

root.mainloop()

在上面这段代码中,我们用update方法刷新了界面,这又是为什么呢?


其实,利用winfo_width和winfo_height获得的宽高其实是tkinter初始化时的值,因此在创建实例后,必须刷新界面,才能得到它当前正确的宽和高。


既然能够获取窗口大小,就一定有办法自定义。这次我们要用到geometry方法:


root.geometry("100x100") #“宽度x高度”

奇怪的事情发生了,创建的窗口宽度不是100,而是120,这是咋回事呢?


原来啊,tkinter帮我们创建了窗口装饰器,装饰器有以下几个部分:图标、标题、最大化、最小化和关闭按钮。在win10系统下,这些装饰器的最小宽度就是120,高度则不受影响。也就是说,图中灰色部分,才是我们创建的界面,最上方的装饰器是自带的。


接下来,我们再用title方法为界面取一个名字吧:


from tkinter import *


root = Tk()

root.geometry("300x300") 

root.title("林四儿的tkinter教程")  #设置标题

root.mainloop()


图1.4 给程序命名

但是这自带的图标也太丑了,有没有办法换成自己的图片呢?办法当然有,不过一定要事先准备好.ico文件,然后输入正确的路径:


from tkinter import *


root = Tk()

root.geometry("300x300") 

root.title("林四儿的tkinter教程")

root.iconbitmap('icon.ico')  #设置图标,仅支持.ico文件

root.mainloop()

到这里,第一个tkinter程序总算完成了


2.标签控件——Label

在tkinter中,可以显示文本和图像的控件有很多,但能够二者兼顾的一个最简单的控件,就是标签(Label)。如果你需要显示一行或多行文本,且不允许用户直接进行修改,那么就可以使用Label。


让我们先来创建一个空白的Label:


from tkinter import *


root = Tk()

l = Label(root)  #如果只有一个窗口,root也可以省略

l.pack()  #布局的方法,先别管它,后面再介绍

root.mainloop()

运行这段代码后可以发现(用上一节的方法输出窗口大小),原本默认大小是200x200的窗口,由于Label的存在,默认大小变成了120x23。这说明在win10系统下,Label默认的最小高度就是23。


那么我们先来改变一下Label的宽和高吧,只需要改变width和height这两个参数:


from tkinter import *


root = Tk()

l = Label(root,width=20,height=2)

l.pack()

root.mainloop()

这次窗口的宽度和高度又变了。改变width和height的值,再输出窗口大小可以发现,Label控件和宽度和高度,并非直接对应像素值——width每增加一个,像素值增加7;height每增加一个,像素值则增加17。这是因为Label默认的尺寸单位是文本单元,如果你显示的是图像,那么它的单位就是像素单元了。其实其他控件的width和height参数同样如此。


接下来,试着用Label显示一段文本吧:


from tkinter import *


root = Tk()

l = Label(root,width=20,height=2,text="林四儿")

l.pack()

root.mainloop()


图2.1 显示文本

字体太普通了,这次我们改变一下字号、字体和颜色:


from tkinter import *


root = Tk()

l = Label(root,width=20,height=2,text="林四儿",fg="red",font=('楷体',10))

l.pack()

root.mainloop()


图2.2 改变文本属性

再试一下更多的属性,比如将背景设为白色,文字按列显示,第一个字加下划线:


from tkinter import *


root = Tk()

l = Label(root,width=20,height=3,text="林四儿",fg="red",font=('楷体',10),bg="white",wraplength=3,underline=0)

l.pack()

root.mainloop()


图2.3 更多属性

比较遗憾的是,underline这个属性只能设置某一个字符下面加下划线,不能设置多个。另外,wraplength参数并不是说设置成几就会分几行显示(但网上的参考资料是这么描述的),只要设置成非0的数,它都会显示成一列。


如果你想让文本换行,在wraplength=0时,'\n'是有效的;但在wraplength≠0时,'\n'也会失效。也就是说,Label的文本可以换行,但不能换列。


下面是Label控件包含的所有参数,其中有许多通用参数,在其他控件也会用到,等讲到其他控件的时候就会看到了:


anchor 文本或图像在背景内容区的位置,默认为 center,可选值为(n,s,w,e,ne,nw,sw,se,center)eswn 是东南西北英文的首字母,表示:上北下南左西右东。

bg 标签背景颜色

bd 标签的大小,默认为 2 个像素

bitmap 指定标签上的位图,如果指定了图片,则该选项忽略

cursor 鼠标移动到标签时,光标的形状,可以设置为 arrow, circle, cross, plus 等。

font 设置字体。

fg 设置前景色。

height 标签的高度,默认值是 0。

image 设置标签图像。

justify 定义对齐方式,可选值有:LEFT,RIGHT,CENTER,默认为 CENTER。

padx x 轴间距,以像素计,默认 1。

pady y 轴间距,以像素计,默认 1。

relief 边框样式,可选的有:FLAT、SUNKEN、RAISED、GROOVE、RIDGE。默认为 FLAT。

text 设置文本,可以包含换行符(\n)。

textvariable 标签显示 Tkinter 变量,StringVar。如果变量被修改,标签文本将自动更新。

underline 设置下划线,默认 -1,如果设置 1,则是从第二个字符开始画下划线。

width 设置标签宽度,默认值是 0,自动计算,单位以像素计。

wraplength 设置标签文本按行或列显示,默认为 0。

Label的文本也可以改变,而且有两种方法:一种是利用text属性,另一种则是利用textvariable属性。具体的实现方法,我们会在下一节Button中讲述。


除了文本以外,Label的image属性还可以显示图片。但tkinter自带的PhotoImage模块只能识别png和gif图像,想要显示其他格式的图片,需要借助PIL库进行格式转换:


from tkinter import *


root = Tk()

img = PhotoImage(file='1.png')

l = Label(root,image=img)

l.pack()

root.mainloop()


图2.4 显示png图像

如果用上面的方法直接显示jpg文件,Label会因无法识别图片而显示一片空白,需要改成下面的方法:


from tkinter import *

from PIL import Image,ImageTk


root = Tk()

img = Image.open('1.jpg')

img = ImageTk.PhotoImage(img)

l = Label(root,image=img)

l.pack()

root.mainloop()

最后是位图的显示。在tkinter中,有下面这些位图是可用的:



图2.5 位图

它们从左到右依次表示为:“error”, “gray75”, “gray50”, “gray25”, “gray12”, “hourglass”, “info”, “questhead”,“question”, “warning”。如果你想要显示位图,可以简单地写作:


l = Label(root,bitmap="error")

除了在创建时设定Label的属性,也可以用config方法随时改变属性,例如:


from tkinter import *


root = Tk()

l = Label(root)

l.pack()

l.config(text="林四儿")

root.mainloop()

结果是一样的,Label会显示出我们设定的文本。一般来说,config方法会与Button或鼠标事件结合,用于即时改变Label的属性。


什么?你说你还想同时显示图像和文字……那你为什么不创建两个Label?——好吧,其实是有办法的,先卖个关子,下节再讲。


3.按钮控件——Button

Button组件用于在 tkinter 应用程序中添加按钮,按钮上可以设置文本或图像,用于监听用户行为,能够与一个 Python 函数关联;当按钮被按下时,自动调用该函数。


创建一个无关联命令的按钮:


from tkinter import *


root = Tk()

b = Button(root, text ="点我")

b.pack()

root.mainloop()


图3.1 无命令按钮

和Label类似,Button也有很多属性选项(在tkinter的新版本中,甚至不止这些):


text 按钮的文本内容

image 按钮上要显示的图片

command 按钮关联的函数,当按钮被点击时,执行该函数

height 按钮的高度

width 按钮的宽度,如未设置此项,其大小以适应按钮的内容(文本或图片的大小)

fg 按钮的前景色(按钮文本的颜色)

bg 按钮的背景色

padx 按钮在x轴方向上的内边距(padding),是指按钮的内容与按钮边缘的距离

pady 按钮在y轴方向上的内边距(padding)

activebackground 当鼠标放上去时,按钮的背景色

activeforeground 当鼠标放上去时,按钮的前景色

font 文本字体

bd 按钮边框的大小,默认为 2 个像素

highlightcolor 要高亮的颜色

justify 显示多行文本的时候,设置不同行之间的对齐方式,可选项包括LEFT, RIGHT, CENTER

relief 边框样式,设置控件3D效果,可选的有:FLAT、SUNKEN、RAISED、GROOVE、RIDGE。默认为 FLAT。

state 设置按钮组件状态,可选的有NORMAL、ACTIVE、 DISABLED。默认 NORMAL。

underline 下划线。默认按钮上的文本都不带下划线。取值就是带下划线的字符串索引,为 0 时,第一个字符带下划线,为 1 时,前两个字符带下划线,以此类推

wraplength 限制按钮每行显示的字符的数量

anchor 锚选项,控制文本的位置,默认为中心

cursor 指定当鼠标在 Button 上飘过的时候的鼠标样式,可选的有"arrow"、"circle"、"clock"、"cross"、"dotbox"、"exchange"、"fleur"、"heart"、"heart"、"man"、"mouse"、"pirate"、"plus"、"shuttle"、"sizing"、"spider"、"spraycan"、"star"、"target"、"tcross"、"trek"、"watch"

compound 控制 Button 中文本和图像的混合模式,默认情况下,如果有指定位图或图片,则不显示文本;可选的有"center"、"bottom"、"left"、"right" 或 "top"

和Label一样,你可以使用 height 和 width 选项来明确设置 Button 的大小:如果你显示的是文本,那么这两个选项是以文本单元为单位;如果你显示的是位图或者图像,那么它们以像素为单位。同时,padx 和 pady 选项可以在 Button 的内容和边框间添加额外的间距(Label也是如此)。


其他属性我就不一一展示了,有兴趣的话可以自行探索。


不知道你是否还记得,关于Label如何同时显示文本和图像,上一节我们留了个悬念。其实不仅是Label,Button也可以!在早期版本的 tkinter 中,image 选项的确会覆盖 text 选项。但在新的 tkinter 中,你可以使用 compound 选项设置二者的混合模式。例如,通过设置 compound="center" 使得文字位于图片中央的上方(重叠显示):


from tkinter import *


root = Tk()

photo = PhotoImage(file = '1.png')

b = Button(root, text="点我",image = photo, compound = "center")

b.pack()

root.mainloop()


图3.2 图片和文字混合

现在我们只是绘制了Button,接下来就用它来实现上节提到的,改变Label的内容吧。


第一种方法,通过config方法设置text属性:


from tkinter import *


def call():

l.config(text="林四儿")


root = Tk()

l = Label(root,text="123")

l.pack()

b = Button(root, text="点我",command=call)

b.pack()

root.mainloop()

第二种方法,通过StringVar()来改变textvariable属性:


from tkinter import *

from PIL import Image,ImageTk


def call():

v.set("林四儿")


root = Tk()

v = StringVar(value="123")

l = Label(root,textvariable=v)

l.pack()

b = Button(root, text="点我",command=call)

b.pack()

root.mainloop()

两种方法的结果是一样的。不同的是,config方法还可以改变其他属性,而StringVar只能设置Label的可变文本。


command选项默认的方式是点击鼠标左键,按下按钮后触发,但Button和其他控件一样,可以通过绑定鼠标或键盘事件的方式,实现其他触发效果。具体方法我们会在以后讲解。


4.界面布局

4.1 pack布局


前面你已经见到过了一个pack()函数,任何控件都可以用它来实现自动的布局。它的默认方式是从上至下分布,系统会自动安排一个合适的位置。例如:


from tkinter import *


root = Tk()

l = Label(root,text="1")

l.pack()

l = Label(root,text="2")

l.pack()

l = Label(root,text="3")

l.pack()

root.mainloop()


图4.1 pack布局示例

实际上,pack包含了几个参数:


expand 表示是否扩展,当它设置为True或YES的时候,它会占满父组件的剩余空间;

fill 指定填充的方向,可以是 X,Y,BOTH 和 NONE,即在水平方向填充,、竖直方向填充、水平和竖直方向填充和不填充;

side 指定了它停靠在哪个方向,可以为 LEFT,TOP,RIGHT,BOTTOM;

anchor 参数可以是 "n", "ne", "e", "se", "s", "sw", "w", "nw", 或 "center" 来定位(上北下南左西右东),默认值是 "nw"。

4.2 grid布局


grid布局,就是我们常说的网格布局。


grid也有4个可选参数:


row 指的是排在第几行,从0开始;

rowspan 指的是占有多少行;

column 指的是排在第几列,从0开始;

columnspan 指的是占有几列;

sticky 指的就是对齐固定方式,和anchor类似。

例如:


from tkinter import *


root = Tk()

l = Button(root,text="1")

l.grid(row=0,column=0)

l = Button(root,text="2")

l.grid(row=1,column=1)

l = Button(root,text="3")

l.grid(row=1,column=2)

root.mainloop()


图4.2 grid布局示例

但grid无法隔行或隔列设置,也就是说,如果你只有三个控件,而前两个控件在第0和第1行,那么第三个控件会自动排列在第2行。


注:grid布局无法与pack布局一起使用。


4.3 place布局


place布局最重要的是x,y两个参数,可以用来指定与父组件的左上角的横坐标和纵坐标距离(绝对布局)。


其他参数也有,比如relheight、relwidth、relx和rely,它们是相对于父组件的高度、宽度、水平位置和垂直位置(相对布局),取值范围是0.0-1.0,一般和anchor结合使用,但并不建议大家使用,掌握好x、y实现精准定位就足够了。


可以说相对于其它布局,它是最灵活的,也是最精准的,只要给出x,y就可以精确地放置到任何想要的位置,但是因为需要计算距离,所以相对来说也更麻烦一些。


例如:


from tkinter import *


root = Tk()

l = Button(root,text="1")

l.place(x=10,y=100)

root.mainloop()


图4.3 place布局

很显然,pack和grid的布局形式更多样,但没有place精准,在实际使用时,按照个人习惯选择布局方式就好了。


5.文本控件


与文本相关的基本控件主要有两种,一种是输入框Entry,另一种是文本框Text。顾名思义,输入框是用来输入简单文本的,而文本框则用来显示多行文本(当然,也可以输入)。


5.1输入框Entry


Entry通常用来输入及显示单行文本,如果你认为输入的长度会超过文本框宽度,那么可以利用xscrollcommand属性,并结合scrollbar(滚动条)控件来多行显示,具体实现方法等到讲解scrollbar的章节再说。现在,让我们来实现一个简单的登录界面:


from tkinter import *

 

root = Tk()

L1 = Label(root, text="用户名:")

L1.grid(row=0,column=0,sticky=W)

E1 = Entry(root, bd =5)

E1.grid(row=0,column=1,sticky=E)

L2 = Label(root, text="密码:")

L2.grid(row=1,column=0,sticky=W)

E2 = Entry(root, bd =5)

E2.grid(row=1,column=1,sticky=E) 

root.mainloop()


图5.1 登录界面

Entry也能用tkinter中的通用属性,同时它还有一些独有的属性,例如:


exportselection 默认情况下,你如果在输入框中选中文本,默认会复制到粘贴板,如果要忽略这个功能刻工艺设置 exportselection=0。

selectbackground 选中文字的背景颜色

selectborderwidth 选中文字的背景边框宽度

selectforeground 选中文字的颜色

show 指定文本框内容显示为字符,值随意,满足字符即可。如密码可以将值设为 show="*"

state 默认为 state=NORMAL, 文框状态,分为只读和可写,值为:normal/disabled

xscrollcommand 设置水平方向滚动条,一般在用户输入的文本框内容宽度大于文本框显示的宽度时使用

takefocus 指定使用 Tab 键可以将焦点移动到输入框中,默认为True

Entry还有很多方法,其中最重要的有两个:


1.delete ( index1, index2 ) 删除输入框里直接位置值,参数是起始位置和结束位置的索引值,例如:


E1.delete(10)      # 删除索引值为10的值

E1.delete(10, 20)  # 删除索引值从10到20之前的值

E1.delete(0, END)  # 删除所有值

2.get() 获取输入框的值


5.2文本框Text


如果需要输入多行文本,则可以使用Text控件。当你创建一个 Text 组件的时候,它里面是没有内容的。为了给其插入内容,可以使用 insert() 方法以及 "insert" 或 "end" 索引号:


from tkinter import *

 

root = Tk()

text = Text(root)

text.pack()

text.insert("insert", "林四儿")  # 插入光标当前位置

text.insert("end", "最帅")  # 插入末尾位置

root.mainloop()

Text的删除和获取也是delete()和get(),不同的是,Text的delete函数中,参数必须是浮点型,例如删除所有内容:


text.delete(0.0,END)

Text 组件的 insert() 方法有一个可选的参数,用于指定一个或多个“标签”。该标签可用于设置文本的格式,让文本框中包含不同格式的文字,例如:


from tkinter import *

 

root = Tk()

text = Text(root, width=20, height=5)

text.pack()

# 设置 tag

text.tag_config("tag_1", foreground="red")

text.insert("insert", "林四儿")

text.insert("end", "最帅", "tag_1")

root.mainloop()


图5.2 tag_config的用法

在 Text 组件中插入对象,可以使用 window_create() 和 image_create() 方法。前者可以插入其他控件,后者则可以插入图片,例如在文本框中插入按钮:


from tkinter import *

 

root = Tk()

text = Text(root, width=20, height=5)

text.pack()

text.insert("insert", "林四儿最帅\n")

b1 = Button(text, text="点我")

b1.pack()

text.window_create("insert", window=b1)

root.mainloop()


图5.3 文本框中插入按钮

删除刚才创建的按钮:


text.delete(b1)

Text控件的方法要比Entry丰富得多,也更加灵活,这里我们就只介绍一些十分常用的,如果你有兴趣的话,可以自行查阅资料,了解两种文本控件的其他用法。


最后,结合两种控件和上一章学习的布局方法,完善一下登录界面吧:


from tkinter import *


def login():

text.delete(0.0,END)

text.insert(0.0,"登录成功!")

root = Tk()

L1 = Label(root, text="用户名:")

L1.grid(row=0,column=0,sticky=W)

E1 = Entry(root, bd =5)

E1.grid(row=0,column=1,sticky=E)

L2 = Label(root, text="密码:")

L2.grid(row=1,column=0,sticky=W)

E2 = Entry(root, bd =5,show='*')

E2.grid(row=1,column=1,sticky=E) 

B = Button(root,text="登 录",command=login)

B.grid(row=2,column=1,sticky=E) 

text = Text(root, width=20, height=1)

text.place(x=15,y=65)

root.mainloop()


6.事件


tkinter中主要有三种事件:鼠标事件、键盘事件和窗体事件。它们的原理是一样的,用bind函数绑定控件和快捷键名称。


bind函数的使用方法是:对象.bind(事件类型,回调函数),其中对象可以是窗体或控件。


其中鼠标事件包括:


<ButtonPress-n>/<Button-n>/<n> 鼠标按钮n被按下,1为左键,2中键,3右键

<ButtonRelease-n> 鼠标按钮n被松开

<Double-Button-n> 鼠标按钮n被双击

<Triple-Button-n> 鼠标按钮n被三击

<Motion> 鼠标被按下,同时,鼠标发生移动

<Bn-Motion> 鼠标按钮n被按下,同时,鼠标发生移动

<Enter> 鼠标进入

<Leave> 鼠标离开

<MouseWheel> 鼠标滚轮滚动

键盘事件包括:


<Any-KeyPress>/<KeyPress>/<Key> 任意键按下(Key是按键名称)

<KeyRelease> 任意键松开

<KeyPress-key>/<Key-key>/<key> 特定键按下

<KeyRelease-key> 特定键松开

窗体事件包括:


<Configure> 改变大小或位置

<Visibility> 当组件变为可视状态时触发

<Unmap> 当组件由显示状态变为隐藏状态时触发

<Map> 当组件由隐藏状态变为显示状态时触发

<Expose> 当组件从原本被其他组件遮盖的状态中暴漏出来时触发

<FocusIn> 组件获得焦点时触发

<FocusOut> 组件失去焦点时触发

<Circulate> 当窗体由于系统协议要求在堆栈中置顶或压底时触发

<Colormap> 当窗体的颜色或外貌改变时触发,Tk中忽略此细则

<Property> 当窗体的属性被删除或改变时触发,属于TK的核心

<Destroy> 当组件被销毁时触发

<Activate> 与组件选项中的state项有关,组件由不可用变为可用时触发

<Deactiavte> 与组件选项中的state项有关,组件由可用变为不可用时触发

另外还有组合事件:


<Control-Shift-Alt-KeyPress-key>/<Control-Shift-Alt-key> 组合键按下(Alt,Shift,Control任选一到三个,想用到哪个前面就列出哪个)

事件可以与控件绑定,也可以与绑定在窗体上,几乎所有控件都可以使用bind方法,而且每个控件可以绑定若干个不重复的事件。例如,我们来实现这样一个小程序:左键点击窗体打印当前鼠标的坐标,随后拖动鼠标,释放时再次打印当前坐标,并输出拖动轨迹的长度。


from tkinter import *


def onLeft(event):

global i

print("起点:",event.x,event.y)

i=0

def mvLeft(event):

global i

i+=1

def rlLeft(event):

print("终点:",event.x,event.y)

print("总长:",i)

root = Tk()

root.bind("<Button-1>",onLeft)  #点击左键

root.bind("<ButtonRelease-1>",rlLeft)  #释放左键

root.bind("<B1-Motion>",mvLeft)  #按下并拖动左键

root.mainloop()

event.x和event.y是tkinter事件中可以用到的变量,它们是鼠标距离窗体左上角的坐标;另外还有event.x_root和event.y_root,它们是鼠标距离屏幕左上角的坐标。


除了bind以外,还有另外两种绑定事件的方法:


bind_all:参数和bind相同,绑定为全局的快捷键,可以在任何地方触发事件。

bind_class:它接受三个参数, 第一个参数是类名,第二个参数是事件类型,第三个参数是绑定的函数,例如:

root.bind_class(“Entry”, “<Control-V>”,paste)

它绑定了所有输入框的 Ctrl+V 表示粘贴(paste函数要自己写)。


通常我们只要熟练掌握bind就够了,它也更加灵活,等我们后文讲到canvas画布的时候,事件的绑定会非常有用。



7.菜单栏


任何GUI都少不了菜单,tkinter当然也有自己的菜单栏。tkinter使用Menu类创建一个菜单,并用add_command方法来添加菜单项。


下面让我们来创建顶层菜单:


from tkinter import *


root = Tk()

menubar = Menu(root)

for i in ['文件','视图','编辑','关于']:

    menubar.add_command(label=i)

#这一步不可或缺,菜单实例要应用到窗口中

root['menu']=menubar

root.mainloop()


图7.1 顶层菜单

add_command有下面几个属性:  


label:指定菜单的名称

command:被点击时调用的方法

acceletor:快捷键

underline:是否拥有下划线

通常,顶层菜单是应用到窗口的父组件,要想继续创建子菜单,需要同时用到add_command和add_cascade方法。例如:


from tkinter import *


root = Tk()

menubar = Menu(root)

#创建子菜单

fmenu = Menu(menubar)

for i in ['新建','打开','保存','另存为']:

    fmenu.add_command(label=i)

#为顶级菜单实例添加菜单,并级联相应的子菜单实例

menubar.add_cascade(label='文件',menu=fmenu)

menubar.add_cascade(label='视图')  #这里省略了menu属性,没有将后面三个选项与子菜单级联

menubar.add_cascade(label='编辑')

menubar.add_cascade(label='关于')


root['menu']=menubar

root.mainloop()


图7.2 子菜单

由此可见,我们需要通过add_cascade方法级联子菜单和顶层菜单。在子菜单存在时,也不需要利用for循环先创建好顶层菜单的每个选项,与子菜单一一对应即可。(代码中省略了后面三个选项的级联,只是显示了菜单的选项名称。)


用add_separator()方法可以添加分割线,调用的时候很简单,需要在哪添加,就把这行代码放在那个地方:


from tkinter import *


root = Tk()

menubar = Menu(root)

fmenu = Menu(menubar)

for i in ['新建','打开','保存','另存为']:

    fmenu.add_command(label=i)

    

fmenu.add_separator()

fmenu.add_command(label='分割线')


menubar.add_cascade(label='文件',menu=fmenu)

menubar.add_cascade(label='视图')

menubar.add_cascade(label='编辑')

menubar.add_cascade(label='关于')

root['menu']=menubar

root.mainloop()


图7.3 分割线

除了默认的点击后无显示的效果,Menu还可以设置单选框(add_radiobutton)与复选框(add_checkbutton),只需对应地替换掉add_command,例如复选框的实现:


from tkinter import *


root = Tk()

menubar = Menu(root)

fmenu = Menu(menubar)

for i in ['新建','打开','保存','另存为']:

    fmenu.add_checkbutton(label=i)

menubar.add_cascade(label='文件',menu=fmenu)

menubar.add_cascade(label='视图')

menubar.add_cascade(label='编辑')

menubar.add_cascade(label='关于')

root['menu']=menubar

root.mainloop()


图7.4 复选框

除了最基本的窗口菜单,tkinter还可以实现弹出菜单。由于通常用右键触发,因此也叫右键菜单。不过这并不是一个独立的实现方法,而是将Menu类与鼠标事件结合,创建好菜单后,使用post方法在指定位置弹出已创建的菜单:


from tkinter import *


#菜单弹出事件

def pop(event):

    menubar.post(event.x_root, event.y_root)

    

root = Tk()

menubar = Menu(root)

for i in ['c','java','python','php']:

    menubar.add_command(label=i)

root.bind("<Button-3>",pop)

root.mainloop()


图7.5 弹出菜单

比较遗憾的是,tkinter的菜单栏只有默认的样式,但Menu和其他组件一样,可以通过一些通用属性来改变字体、颜色等等,不过也仅此而已了。


看了上面那么多的演示图,你有没有想过子菜单的长虚线有什么作用呢?这里我要介绍一个Menu特有的属性:


tearoff 点击子菜单中的 ---------,可以将其“撕下“,默认为True,设为False关闭



值得拥有的五种Python Tkinter界面美化库!


Python Tkinter是一种广泛使用的GUI工具包,它提供了很多基本的UI组件,如按钮、标签、文本框等。

然而,Tkinter默认的UI样式可能有些过时,不够美观,因此需要使用一些界面美化库来改善UI外观。本文将介绍几个常用的Python Tkinter界面美化库以及如何使用它们。


ttkthemes


ttkthemes是一个Python Tkinter主题库,提供了多种主题,如Equilux、Adapta、Arc等。可以通过以下命令安装:


pip install ttkthemes

使用ttkthemes很简单,只需导入所需的主题并应用到Tkinter窗口即可,例如:


import tkinter as tk

from ttkthemes import ThemedStyle

root = tk.Tk()

style = ThemedStyle(root)

style.set_theme("equilux")

root.mainloop()

这里导入了ttkthemes库和Tkinter库,并创建了一个名为root的Tkinter窗口。


然后创建了一个名为style的ThemedStyle对象,并将其设置为Equilux主题。最后启动窗口。


tkmacosx


tkmacosx是一个Python Tkinter库,可用于实现类似于macOS的UI元素和动画效果。可以通过以下命令安装:


pip install tkmacosx

使用tkmacosx也很简单,只需导入所需的UI元素并将其添加到Tkinter窗口即可,例如:


import tkinter as tk

from tkmacosx import Button

root = tk.Tk()

btn = Button(root, text="Click me", bg="red")

btn.pack()

root.mainloop()

这里导入了tkmacosx库和Tkinter库,并创建了一个名为root的Tkinter窗口。


然后创建了一个名为btn的Button对象,并将其添加到窗口中。最后启动窗口。


PySimpleGUI


PySimpleGUI是一个Python GUI库,提供了多种UI组件和布局选项,同时有着现代化的外观。可以通过以下命令安装:


pip install PySimpleGUI

使用PySimpleGUI也很简单,只需导入所需的UI组件并将其添加到布局中即可,例如:


import PySimpleGUI as sg

layout = [[sg.Text("Enter your name:")],

          [sg.Input()],

          [sg.Button('Ok'), sg.Button('Cancel')]]

window = sg.Window('My window', layout)

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED or event == 'Cancel':

        break

    print(f"Hello {values[0]}!")


window.close()

这里导入了PySimpleGUI库,并创建了一个名为layout的布局,包含一个标签、一个文本框和两个按钮。


然后创建了一个名为window的PySimpleGUI窗口,并将布局设置为窗口的属性。最后在循环中读取窗口事件和值,并打印问候语。窗口关闭后,结束程序。


ttkwidgets


ttkwidgets是一个Python Tkinter库,提供了多种现代UI组件,包括滑块、进度条、开关等。可以通过以下命令安装:


pip install ttkwidgets

使用ttkwidgets也很简单,只需导入所需的UI组件并将其添加到Tkinter窗口即可,例如:


import tkinter as tk

from ttkwidgets import Slider

root = tk.Tk()

slider = Slider(root, from_=-100, to=100)

slider.pack()

root.mainloop()

这里导入了ttkwidgets库和Tkinter库,并创建了一个名为root的Tkinter窗口。


然后创建了一个名为slider的Slider对象,并将其添加到窗口中。最后启动窗口。


ttkbootstrap


ttkbootstrap是一个Python Tkinter库,提供了多种Bootstrap样式的UI组件,包括按钮、表格、卡片等。可以通过以下命令安装:


pip install ttkbootstrap

使用ttkbootstrap也很简单,只需导入所需的UI组件并将其添加到Tkinter窗口即可,例如:


import tkinter as tk

from ttkbootstrap import Style

root = tk.Tk()

style = Style(theme='flatly')

btn = style.Button(root, text="Click me")

btn.pack()

root.mainloop()

这里导入了ttkbootstrap库和Tkinter库,并创建了一个名为root的Tkinter窗口。然后创建了一个名为style的Style对象,并将其设置为Flatly主题。


最后创建了一个名为btn的Button对象,并将其添加到窗口中。启动窗口后,即可看到按钮的Bootstrap样式。


综上所述,以上就是几个常用的Python Tkinter界面美化库,使用它们可以轻松地改善Tkinter GUI的外观和体验。


除了上述几个库外,还有一些其他的Tkinter美化库,如ttkwidgets、ttkbootstrap等,读者可以根据自己的需求选择合适的库来美化Tkinter GUI。



Python操作数据库


操作sqlite,安装操作库

pip install sqlite3


接下来,我们可以创建一个SQLite数据库并进行添加、编辑和删除操作的示例:


import sqlite3


# 连接到数据库(如果数据库不存在,则会创建一个新的数据库)

conn = sqlite3.connect('example.db')


# 创建一个游标对象,用于执行SQL语句

cursor = conn.cursor()


# 创建一个表格

cursor.execute('''CREATE TABLE IF NOT EXISTS employees

                  (id INTEGER PRIMARY KEY AUTOINCREMENT,

                   name TEXT NOT NULL,

                   age INTEGER NOT NULL,

                   department TEXT NOT NULL)''')


# 添加数据

def add_employee(name, age, department):

    cursor.execute("INSERT INTO employees (name, age, department) VALUES (?, ?, ?)", (name, age, department))

    conn.commit()

    print("Employee added successfully.")


# 编辑数据

def edit_employee(employee_id, name, age, department):

    cursor.execute("UPDATE employees SET name=?, age=?, department=? WHERE id=?", (name, age, department, employee_id))

    conn.commit()

    print("Employee updated successfully.")


# 删除数据

def delete_employee(employee_id):

    cursor.execute("DELETE FROM employees WHERE id=?", (employee_id,))

    conn.commit()

    print("Employee deleted successfully.")


# 添加示例数据

add_employee("John Doe", 30, "Sales")

add_employee("Jane Smith", 35, "Marketing")


# 编辑示例数据

edit_employee(1, "John Doe", 32, "Sales")


# 删除示例数据

delete_employee(2)


# 执行查询操作

cursor.execute("SELECT * FROM employees")

rows = cursor.fetchall()


# 打印查询结果

for row in rows:

    print(row)


# 关闭游标和数据库连接

cursor.close()


操作MySQL数据库示例

首先,确保你已经安装了mysql-connector-python库。如果未安装,可以使用以下命令进行安装:

pip install mysql-connector-python


接下来,我们可以创建一个MySQL数据库并进行添加、编辑和删除操作的示例:


import mysql.connector


# 连接到数据库

conn = mysql.connector.connect(

    host="localhost",

    user="your_username",

    password="your_password",

    database="your_database"

)


# 创建一个游标对象,用于执行SQL语句

cursor = conn.cursor()


# 创建一个表格

cursor.execute('''CREATE TABLE IF NOT EXISTS employees

                  (id INT AUTO_INCREMENT PRIMARY KEY,

                   name VARCHAR(255) NOT NULL,

                   age INT NOT NULL,

                   department VARCHAR(255) NOT NULL)''')


# 添加数据

def add_employee(name, age, department):

    sql = "INSERT INTO employees (name, age, department) VALUES (%s, %s, %s)"

    values = (name, age, department)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee added successfully.")


# 编辑数据

def edit_employee(employee_id, name, age, department):

    sql = "UPDATE employees SET name=%s, age=%s, department=%s WHERE id=%s"

    values = (name, age, department, employee_id)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee updated successfully.")


# 删除数据

def delete_employee(employee_id):

    sql = "DELETE FROM employees WHERE id=%s"

    values = (employee_id,)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee deleted successfully.")


# 添加示例数据

add_employee("John Doe", 30, "Sales")

add_employee("Jane Smith", 35, "Marketing")


# 编辑示例数据

edit_employee(1, "John Doe", 32, "Sales")


# 删除示例数据

delete_employee(2)


# 执行查询操作

cursor.execute("SELECT * FROM employees")

rows = cursor.fetchall()


# 打印查询结果

for row in rows:

    print(row)


# 关闭游标和数据库连接

cursor.close()

conn.close()




操作MSSQL数据库示例

首先,确保你已经安装了pyodbc库。如果未安装,可以使用以下命令进行安装:

pip install pyodbc


接下来,我们可以创建一个MSSQL数据库并进行添加、编辑和删除操作的示例:


import pyodbc


# 连接到数据库

conn = pyodbc.connect(

    'DRIVER={SQL Server};'

    'SERVER=your_server_name;'

    'DATABASE=your_database;'

    'UID=your_username;'

    'PWD=your_password;'

)


# 创建一个游标对象,用于执行SQL语句

cursor = conn.cursor()


# 创建一个表格

cursor.execute('''CREATE TABLE IF NOT EXISTS employees

                  (id INT IDENTITY(1,1) PRIMARY KEY,

                   name NVARCHAR(255) NOT NULL,

                   age INT NOT NULL,

                   department NVARCHAR(255) NOT NULL)''')


# 添加数据

def add_employee(name, age, department):

    sql = "INSERT INTO employees (name, age, department) VALUES (?, ?, ?)"

    values = (name, age, department)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee added successfully.")


# 编辑数据

def edit_employee(employee_id, name, age, department):

    sql = "UPDATE employees SET name=?, age=?, department=? WHEREid=?"

    values = (name, age, department, employee_id)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee updated successfully.")


# 删除数据

def delete_employee(employee_id):

    sql = "DELETE FROM employees WHERE id=?"

    values = (employee_id,)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee deleted successfully.")


# 添加示例数据

add_employee("John Doe", 30, "Sales")

add_employee("Jane Smith", 35, "Marketing")


# 编辑示例数据

edit_employee(1, "John Doe", 32, "Sales")


# 删除示例数据

delete_employee(2)


# 执行查询操作

cursor.execute("SELECT * FROM employees")

rows = cursor.fetchall()


# 打印查询结果

for row in rows:

    print(row)


# 关闭游标和数据库连接

cursor.close()

conn.close()


操作PGSQL数据库示例

首先,确保你已经安装了psycopg2库。如果未安装,可以使用以下命令进行安装:

pip install psycopg2


接下来,我们可以创建一个PGSQL数据库并进行添加、编辑和删除操作的示例:



import psycopg2


# 连接到数据库

conn = psycopg2.connect(

    host="localhost",

    port="5432",

    database="your_database",

    user="your_username",

    password="your_password"

)


# 创建一个游标对象,用于执行SQL语句

cursor = conn.cursor()


# 创建一个表格

cursor.execute('''CREATE TABLE IF NOT EXISTS employees

                  (id SERIAL PRIMARY KEY,

                   name VARCHAR(255) NOT NULL,

                   age INT NOT NULL,

                   department VARCHAR(255) NOT NULL)''')


# 添加数据

def add_employee(name, age, department):

    sql = "INSERT INTO employees (name, age, department) VALUES (%s, %s, %s)"

    values = (name, age, department)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee added successfully.")


# 编辑数据

def edit_employee(employee_id, name, age, department):

    sql = "UPDATE employees SET name=%s, age=%s, department=%s WHERE id=%s"

    values = (name, age, department, employee_id)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee updated successfully.")


# 删除数据

def delete_employee(employee_id):

    sql = "DELETE FROM employees WHERE id=%s"

    values = (employee_id,)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee deleted successfully.")


# 添加示例数据

add_employee("John Doe", 30, "Sales")

add_employee("Jane Smith", 35, "Marketing")


# 编辑示例数据

edit_employee(1, "John Doe", 32, "Sales")


# 删除示例数据

delete_employee(2)


# 执行查询操作

cursor.execute("SELECT * FROM employees")

rows = cursor.fetchall()


# 打印查询结果

for row in rows:

    print(row)


# 关闭游标和数据库连接

cursor.close()

conn.close()

在上述代码中,我们首先使用psycopg2库连接到PGSQL数据库,然后创建了一个名为employees的表格。接下来,我们定义了add_employee、edit_employee和delete_employee函数,用于添加、编辑和删除员工数据。然后,我们调用这些函数来演示相应的操作,并执行一个查询操作来打印查询结果。最后,我们关闭了游标和数据库连接。


操作Oracle数据库示例

首先,确保你已经安装了cx_Oracle库。如果未安装,可以使用以下命令进行安装:

pip install cx_Oracle


接下来,我们可以创建一个Oracle数据库并进行添加、编辑和删除操作的示例:


import cx_Oracle


# 连接到数据库

conn = cx_Oracle.connect(

    user="your_username",

    password="your_password",

    dsn="your_dsn"

)


# 创建一个游标对象,用于执行SQL语句

cursor = conn.cursor()


# 创建一个表格

cursor.execute('''CREATE TABLE employees

                  (id NUMBER PRIMARY KEY,

                   name VARCHAR2(255) NOT NULL,

                   age NUMBER NOT NULL,

                   department VARCHAR2(255) NOT NULL)''')


# 添加数据

def add_employee(name, age, department):

    sql = "INSERT INTO employees (id, name, age, department) VALUES (:1, :2, :3, :4)"

    values = (1, name, age, department)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee added successfully.")


# 编辑数据

def edit_employee(employee_id, name, age, department):

    sql = "UPDATE employees SET name=:1, age=:2, department=:3 WHERE id=:4"

    values = (name, age, department, employee_id)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee updated successfully.")


# 删除数据

def delete_employee(employee_id):

    sql = "DELETE FROM employees WHERE id=:1"

    values = (employee_id,)

    cursor.execute(sql, values)

    conn.commit()

    print("Employee deleted successfully.")


# 添加示例数据

add_employee("John Doe", 30, "Sales")

add_employee("Jane Smith", 35, "Marketing")


# 编辑示例数据

edit_employee(1, "John Doe", 32, "Sales")


# 删除示例数据

delete_employee(2)


# 执行查询操作

cursor.execute("SELECT * FROM employees")

rows = cursor.fetchall()


# 打印查询结果

for row in rows:

    print(row)


# 关闭游标和数据库连接

cursor.close()

conn.close()

在上述代码中,我们首先使用cx_Oracle库连接到Oracle数据库,然后创建了一个名为employees的表格。接下来,我们定义了add_employee、edit_employee和delete_employee函数,用于添加、编辑和删除员工数据。然后,我们调用这些函数来演示相应的操作,并执行一个查询操作来打印查询结果。最后,我们关闭了游标和数据库连接。


KingbaseES  GaussDB  ZXDB  ReachDB

1. 连接数据库

连接到KingbaseES数据库的示例代码

点击查看文档资料

import psycopg2


conn = psycopg2.connect(

    host="localhost",

    port="54321",

    database="your_database",

    user="your_username",

    password="your_password"

)

连接到GaussDB数据库的示例代码


import cx_Oracle


conn = cx_Oracle.connect(

    user="your_username",

    password="your_password",

    dsn="your_dsn"

)

连接到ZXDB数据库的示例代码:



import pymysql


conn = pymysql.connect(

    host="localhost",

    port=3306,

    user="your_username",

    password="your_password",

    database="your_database"

)

连接到ReachDB数据库的示例代码:



import pyodbc


conn = pyodbc.connect(

    driver="{ReachDB Driver}",

    server="your_server",

    database="your_database",

    uid="your_username",

    pwd="your_password"

)

2. 创建游标

创建游标对象的示例代码:


cursor = conn.cursor()

3. 执行SQL语句

执行SQL语句的示例代码:


# 添加数据

sql = "INSERT INTO your_table (name, age, department) VALUES (%s, %s, %s)"

values = ("John Doe", 30, "Sales")

cursor.execute(sql, values)

conn.commit()


# 编辑数据

sql = "UPDATE your_table SET name=%s, age=%s, department=%s WHERE id=%s"

values = ("John Doe", 32, "Sales", 1)

cursor.execute(sql, values)

conn.commit()


# 删除数据

sql = "DELETE FROM your_table WHERE id=%s"

values = (2,)

cursor.execute(sql, values)

conn.commit()


# 执行查询操作

cursor.execute("SELECT * FROM your_table")

rows = cursor.fetchall()


# 打印查询结果

for row in rows:

    print(row)

4. 关闭游标和数据库连接

关闭游标和数据库连接的示例代码:


cursor.close()

conn.close()

以上是一个基本的操作示例,你可以根据实际情况进行修改和扩展。请确保在执行数据库操作之前,你已经安装了相应数据库的驱动程序库(如psycopg2、cx_Oracle、pymysql或pyodbc),并使用正确的连接字符串和凭据连接到数据库。



一个GUI库NiceUI

点击查看原文

NiceGui的安装和使用

1、安装

使用 pip 下载 NiceGui(仅支持3.7及以上版本)


pip install nicegui

2、编写一个简单代码,测试其功能是否正常


from nicegui import ui

# 创建一个标签

ui.label('Welcome to NiceGui!')  

# 创建一个按钮,设置回调函数,调用niceui的弹窗消息通知

ui.button('Click Here', on_click=lambda: ui.notify('Button Pressed'))

# niceui 运行

ui.run()

点击运行后,终端会输出web url访问地址,自动打开浏览器



如何将Python打包后的exe还原成.py?

查看原文

点击查看另一个反编译文章

用到的工具

pyinstxtractor.py 拆包(解压)工具,将exe文件解压成一个文件夹

uncompyle6 pyc反编译工具

pycdc工具可以在 Python 3.9 及以上版本取代uncompyle6库来实现反编译。(点击查看文章

Python3.9^-Decompiler.zip


010EditorEditor 或者其他二进制查看与修改工具,我这里用的010Editor


pyinstxtractor.py 工具的下载地址:https://github.com/extremecoders-re/pyinstxtractor

pyinstxtractor-master.zip


Hex编辑器 笔者这里是使用的 wxMEdit,下载地址:

https://wxmedit.github.io/downloads.html 或 https://download.csdn.net/download/qq_63585949/86509705


   

反编译要保证Python版本一致,具体点击查看文章

   

安装方法    

pip install uncompyle6    

第一步:解包    

python3 pyinstxtractor.py ***.exe   #  这里替换成你要反编译的exe文件    


   

#  会生成一个以 exe文件名+_extracted 的文件夹,这个就是解包后的数据    

1.png    

第二步:添加头信息    

PyInstaller打包后,pyc文件的前8个字节会被抹掉,所以最后要自己添加回去。前四个字节为python编译的版本,后四个字节为时间戳。想要获得编译版本可以查看打包文件里struct的信息    


   

1). 进入文件夹,找到以exe文件名命名的文件(没有后缀),这个就是目的文件    

1.png    


   


   


   

2). 用 010Editor 打开 struct,前八位就是我们想要的信息,将其复制    

1.png    


   

3). 用 010Editor 打开目的文件我这里是 abc_text,将上一步复制的信息插入到开头    


   

修改前:    

1.png    


   

修改后:    

2.png    


   

4). 将目的文件我这里是 abc_text,添加pyc的后缀    


   

第三步:逆向目的文件.pyc    

1). 其实这里已经可以使用了。了解python的都知道pyc是py文件编译后的二进制文件,因此如果想要分析源码还得继续逆向成.py文件    


   

1.png    


   


   

2). uncompyle6逆向pyc文件    


   

uncompyle6 abc_text.pyc > abc_text.py    

1.png
   


Python 打包成 exe,太大了该怎么解决


2024年,我的Python 打包成 exe,用的是Nuitka+Pystand,无法反编译,打包非常快,非常棒

nuitka --module my_module.py # 扩展模块编译,生成pyd文件,可代替py文件

nuitka --module my_package --include-package=my_package # 软件包的编译,编译整个软件包并嵌入所有模块


第一步:首先,使用上面2条命令,将所有Python代码转成pyd文件(你可以理解为C语言的dll文件),这是无法反编译的,能够保证你的Python代码安全,并且生成的速度非常快

第二步:然后,将项目的site-packages复制过去

第三步:接着,将之前的main.py内容复制进Pystand.int文件

第四步:最后,在CMD中,运行Pystand.exe,验证运行成功

第五步:7Z压缩,又快又小又安全,放心的分发给其他小伙伴吧


点击查看nuitka 打包exe简单教程

点击查看使用Nuitka打包Python程序

python生成的exe文件防止反编译(Nuitka)


正常安装 nuitka

pip install nuitka


正常运行程序,一般一个入口函数,加一个src依赖包

1.png


嵌入所有模块的编译

nuitka --follow-imports hello.py

第一次运行报错,复制一个python38.dll 过来就ok了

并且src下的所有python文件,都可以删除,程序依然正常运行


如果你想对src模块代码编译成C++,保护自己的代码不被破解

nuitka --module src/talk.py

生成的pyd,可以代替原来的python文件,并且是安全,无法还原成py文件


打包分发(文件夹)

默认会带 --follow-imports 功能,嵌入所有模块

nuitka --standalone --output-dir=out2 hello.py

进入 out2\hello.dist ,直接运行exe程序,成功,可以把dist 压缩发给别人玩耍啦


打包分发(1个exe)

nuitka --onefile --output-dir=out3 hello.py

直接把exe发给别人,就能愉快的玩耍了,nuitka 打包就是这么简单!!!



我在hello.py 中加入了 import pandas as pd ,打包一下子就慢了很多

nuitka --onefile --output-dir=out3 hello.py

打开文件夹一看,好家伙,似乎把我pandas都转成C++扩展,这不行啊

使用 --nofollow-import-to,不要使用 --nofollow-imports ,因为V2.1.3版本的nuitka的 --standalone 模式下,--nofollow-imports参数是无效的,一定要先看看你自己的nuitka版本,网上很多教程都老了,要学会查看nuitka -h帮助信息了。

nuitka --standalone --nofollow-import-to=pandas,numpy --output-dir=out2 hello.py

这个时候运行的时候会报错

然后要复制一大堆东西,才让这个exe跑起来,打包快是快了,但是你让我复制一大堆东西,有点心累啊


终于明白了,nuitka打包自己用可以很快,但是你要打包给别人用,那么对不起

nuitka --nofollow-imports --follow-import-to=src --output-dir=out5 hello.py


你要是想打包快,然后复制一堆东西,可以使用下面的命令

nuitka --standalone --nofollow-import-to=pandas,numpy --output-dir=out2 hello.py


你要是不想复制一堆东西,能够忍受打包慢,可以使用下面的命令

nuitka --standalone --output-dir=out2 hello.py


关键的几个参数,我进行了简单的解读,感觉理解的不是很深刻,但是核心就是几条命令:

nuitka --module some_module.py # 扩展模块编译,生成pyd文件,可代替py文件

nuitka --module some_package --include-package=some_package # 软件包的编译,编译整个软件包并嵌入所有模块

nuitka --standalone main.py # 程序分发,可以把程序发送给其他人使用,缺的是打包速度很慢

我经常用上面第一条和第二条,速度快,并且别人无法破解Python源代码,然后配合韦一笑的PyStand[1],可以秒级出包,嘎嘎香。


初次使用Nuitka打包Python程序,会从Github下载MinGW64,很慢,我是手动下载好,然后放到提示的位置的。

C:\Users\79216\AppData\Local\Nuitka\Nuitka\Cache\DOWNLO~1\gcc\x86_64\13.2.0-16.0.6-11.0.1-msvcrt-r1



[Python]读取QQ聊天记录

点击查看原文

要通过Python获取QQ聊天记录需要导入uiautomation库(三方库)

import uiautomation as auto

然后我们通过uiautomation找到消息窗口

通过窗口侦察发现QQ窗口类名是TXGuiFoundation,标题是对方名字:

1.png


qq_win = auto.WindowControl(searchDepth=1, ClassName='TXGuiFoundation', Name='【浴火】')


找到输入消息的编辑框,获取到里面的文字


input_edit = qq_win.EditControl()

print("当前输入框:" + input_edit.GetValuePattern().Value) 

消息在 QQ 里是一个 list(列表),列表会有很多子属性,子属性 的 Name 就是历史消息的文本


msg_list = qq_win.ListControl() #找到 list


items = msg_list.GetChildren()

最后就是做遍历处理:


for one_item in items: #遍历所有子属性

    if one_item.Name != "":

        print(one_item.Name) #打印子属性的Name属性

完整代码:


import uiautomation as auto


qq_win = auto.WindowControl(searchDepth=1, ClassName='TXGuiFoundation', Name='【浴火】')


input_edit = qq_win.EditControl()


print("当前输入框:" + input_edit.GetValuePattern().Value) 


msg_list = qq_win.ListControl()


items = msg_list.GetChildren()


for one_item in items: 

    if one_item.Name != "":

        print(one_item.Name)


此时搭配tkinter库做GUI会更好


导入库:


import tkinter

from tkinter import *

from tkinter import scrolledtext

import uiautomation as auto

定义函数(主窗口):


def get_window():


    window_four = tkinter.Tk()


    LabelMessage = tkinter.Label(window_four,text="对方名字:")

    LabelMessage.place(x=10, y=10)


    EntryName = tkinter.Entry(window_four)

    EntryName.place(x=70, y=10)


    window_four.title("QQ聊天记录读取")

    window_four.geometry("480x300")


    end_get = scrolledtext.ScrolledText(window_four, width=60, height=10)

    end_get.place(x=10, y=50)


    def to_Entry_Text():

        get_text(end_get,EntryName.get())


    get_phone = tkinter.Button(window_four, text=" 提取 ", command=to_Entry_Text)

    get_phone.place(x=225, y=6)

    JiaoChen = tkinter.Label(window_four, text="打开QQ好友(对方)的会话窗口,输入对方名字", font=(10))

    JiaoChen.place(x=70, y=260)

    window_four.resizable(False, False)

    window_four.mainloop()

定义函数(获取信息并且和主窗口互动):


def get_text(cors,name):

    message = ""

    qq_win = auto.WindowControl(searchDepth=1, ClassName='TXGuiFoundation', Name=name)


    input_edit = qq_win.EditControl()

    try:

        message += str("当前输入框:" + input_edit.GetValuePattern().Value) + "\n"

    except:

        cors.delete('1.0', 'end')

        cors.insert('end', "检测到你打开了多个聊天窗口" + "\n" + "请关闭不需要的会话窗口,留下要提取的那个人的")

    msg_list = qq_win.ListControl()


    items = msg_list.GetChildren()


    for one_item in items:

        if one_item.Name != "":

            message += str(one_item.Name)

    cors.delete('1.0', 'end')

    cors.insert('end', message)

编写Main(启动处):


if __name__ == '__main__':

    get_window()


1.png

完整代码:


import tkinter

from tkinter import *

from tkinter import scrolledtext

import uiautomation as auto


def get_text(cors,name):

    message = ""

    qq_win = auto.WindowControl(searchDepth=1, ClassName='TXGuiFoundation', Name=name)


    input_edit = qq_win.EditControl()

    try:

        message += str("当前输入框:" + input_edit.GetValuePattern().Value) + "\n"

    except:

        cors.delete('1.0', 'end')

        cors.insert('end', "检测到你打开了多个聊天窗口" + "\n" + "请关闭不需要的会话窗口,留下要提取的那个人的")

    msg_list = qq_win.ListControl()


    items = msg_list.GetChildren()


    for one_item in items:

        if one_item.Name != "":

            message += str(one_item.Name)

    cors.delete('1.0', 'end')

    cors.insert('end', message)




def get_window():


    window_four = tkinter.Tk()


    LabelMessage = tkinter.Label(window_four,text="对方名字:")

    LabelMessage.place(x=10, y=10)


    EntryName = tkinter.Entry(window_four)

    EntryName.place(x=70, y=10)


    window_four.title("QQ聊天记录读取")

    window_four.geometry("480x300")


    end_get = scrolledtext.ScrolledText(window_four, width=60, height=10)

    end_get.place(x=10, y=50)


    def to_Entry_Text():

        get_text(end_get,EntryName.get())


    get_phone = tkinter.Button(window_four, text=" 提取 ", command=to_Entry_Text)

    get_phone.place(x=225, y=6)

    JiaoChen = tkinter.Label(window_four, text="打开QQ好友(对方)的会话窗口,输入对方名字", font=(10))

    JiaoChen.place(x=70, y=260)

    window_four.resizable(False, False)

    window_four.mainloop()


if __name__ == '__main__':

    get_window()



一个python操作低功耗蓝牙例子包含连接发送接收

一个使用Python操作低功耗蓝牙(BLE)的示例,包括连接、发送和接收数据。这个例子使用了bleak库,它是一个跨平台的Python BLE客户端库。首先,您需要安装bleak:

pip install bleak


以下是一个包含连接、发送和接收数据的Python BLE操作示例:


import asyncio

from bleak import BleakClient, BleakScanner


# 设备的MAC地址(需要替换为实际设备的地址)

DEVICE_ADDRESS = "XX:XX:XX:XX:XX:XX"


# 特征UUID(需要替换为实际设备的UUID)

WRITE_CHARACTERISTIC_UUID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

READ_CHARACTERISTIC_UUID = "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"


async def run_ble_client():

    # 扫描并连接设备

    device = await BleakScanner.find_device_by_address(DEVICE_ADDRESS)

    if not device:

        print(f"无法找到地址为 {DEVICE_ADDRESS} 的设备")

        return


    async with BleakClient(device) as client:

        print(f"已连接到 {device.name}")


        # 发送数据

        data_to_send = b"Hello, BLE!"

        await client.write_gatt_char(WRITE_CHARACTERISTIC_UUID, data_to_send)

        print(f"已发送数据: {data_to_send}")


        # 接收数据

        received_data = await client.read_gatt_char(READ_CHARACTERISTIC_UUID)

        print(f"接收到数据: {received_data}")


        # 设置通知回调

        def notification_handler(sender, data):

            print(f"收到通知: {data}")


        # 启用通知

        await client.start_notify(READ_CHARACTERISTIC_UUID, notification_handler)


        # 保持连接一段时间以接收可能的通知

        await asyncio.sleep(5)


        # 禁用通知

        await client.stop_notify(READ_CHARACTERISTIC_UUID)


# 运行异步函数

asyncio.run(run_ble_client())


这个脚本做了以下几件事:

导入必要的库:asyncio用于异步操作,bleak用于BLE通信。

设置设备地址和特征UUID。您需要用实际设备的值替换这些占位符。

定义一个异步函数run_ble_client()来处理BLE操作。

使用BleakScanner查找并连接到指定地址的设备。

连接成功后,发送数据到写入特征。

从读取特征接收数据。

设置一个通知处理函数,并启用通知。这允许设备主动发送数据给客户端。

保持连接一段时间以接收可能的通知。

最后禁用通知并断开连接。


请注意以下几点:

您需要替换DEVICE_ADDRESS为实际BLE设备的MAC地址。

WRITE_CHARACTERISTIC_UUID和READ_CHARACTERISTIC_UUID需要替换为您的BLE设备实际使用的特征UUID。

这个例子使用异步编程,这是bleak库的要求。

不同的BLE设备可能有不同的服务和特征,您可能需要根据具体设备调整代码。

在某些操作系统上,您可能需要特殊权限才能访问蓝牙功能。

这个示例提供了基本的BLE操作框架。在实际应用中,您可能需要添加更多的错误处理,实现特定的数据解析逻辑,或者根据您的设备协议调整通信方式。


 

低功耗蓝牙包含扫描、连接、发送和接收数据的Python BLE操作示例:    

您需要安装bleak:

pip install bleak



import asyncio

from bleak import BleakScanner, BleakClient


# 用于存储找到的设备

discovered_devices = []


# 特征UUID(需要替换为实际设备的UUID)

WRITE_CHARACTERISTIC_UUID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

READ_CHARACTERISTIC_UUID = "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"


async def scan_devices():

    global discovered_devices

    print("开始扫描BLE设备...")

    devices = await BleakScanner.discover()

    discovered_devices = devices

    print("扫描完成。发现以下设备:")

    for i, device in enumerate(devices):

        print(f"{i+1}. {device.name or 'Unknown'} ({device.address})")


async def connect_and_communicate(device):

    async with BleakClient(device.address) as client:

        print(f"已连接到 {device.name or 'Unknown Device'}")


        # 发送数据

        data_to_send = b"Hello, BLE!"

        await client.write_gatt_char(WRITE_CHARACTERISTIC_UUID, data_to_send)

        print(f"已发送数据: {data_to_send}")


        # 接收数据

        received_data = await client.read_gatt_char(READ_CHARACTERISTIC_UUID)

        print(f"接收到数据: {received_data}")


        # 设置通知回调

        def notification_handler(sender, data):

            print(f"收到通知: {data}")


        # 启用通知

        await client.start_notify(READ_CHARACTERISTIC_UUID, notification_handler)


        # 保持连接一段时间以接收可能的通知

        print("等待接收通知...")

        await asyncio.sleep(10)


        # 禁用通知

        await client.stop_notify(READ_CHARACTERISTIC_UUID)


async def main():

    await scan_devices()

    

    if not discovered_devices:

        print("没有发现设备。")

        return


    # 让用户选择一个设备

    choice = int(input("请选择要连接的设备编号: ")) - 1

    if 0 <= choice < len(discovered_devices):

        selected_device = discovered_devices[choice]

        await connect_and_communicate(selected_device)

    else:

        print("无效的选择。")


# 运行主异步函数

asyncio.run(main())

这个脚本做了以下几件事:


导入必要的库:asyncio用于异步操作,bleak用于BLE通信。


定义一个全局列表discovered_devices来存储扫描到的设备。


定义scan_devices()函数来扫描并列出附近的BLE设备。


定义connect_and_communicate()函数来处理与选定设备的连接、数据发送和接收。


在main()函数中:


首先调用scan_devices()扫描设备。

让用户选择一个要连接的设备。

调用connect_and_communicate()与选定的设备进行通信。

在connect_and_communicate()函数中:


连接到选定的设备。

发送数据到写入特征。

从读取特征接收数据。

设置通知处理函数并启用通知。

保持连接10秒钟以接收可能的通知。

最后禁用通知并断开连接。

请注意以下几点:


您需要将WRITE_CHARACTERISTIC_UUID和READ_CHARACTERISTIC_UUID替换为您的BLE设备实际使用的特征UUID。

不同的BLE设备可能有不同的服务和特征,您可能需要根据具体设备调整代码。

在某些操作系统上,您可能需要特殊权限才能访问蓝牙功能。

这个示例使用了10秒的等待时间来接收通知,您可以根据需要调整这个时间。

这个示例提供了一个更完整的BLE操作流程,包括设备扫描、用户选择、连接、数据发送和接收。在实际应用中,您可能还需要添加更多的错误处理,实现特定的数据解析逻辑,或者根据您的设备协议调整通信方式。



一个python操作低功耗蓝牙例子包含扫描设备,遍历服务和服务里的特征值,连接发送接收    

您需要安装bleak:

pip install bleak


import asyncio

from bleak import BleakScanner, BleakClient


# 用于存储找到的设备

discovered_devices = []


# 特征UUID(需要替换为实际设备的UUID)

WRITE_CHARACTERISTIC_UUID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

READ_CHARACTERISTIC_UUID = "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"


async def scan_devices():

    global discovered_devices

    print("开始扫描BLE设备...")

    devices = await BleakScanner.discover()

    discovered_devices = devices

    print("扫描完成。发现以下设备:")

    for i, device in enumerate(devices):

        print(f"{i+1}. {device.name or 'Unknown'} ({device.address})")


async def connect_and_communicate(device):

    async with BleakClient(device.address) as client:

        print(f"已连接到 {device.name or 'Unknown Device'}")


        # 发送数据

        data_to_send = b"Hello, BLE!"

        await client.write_gatt_char(WRITE_CHARACTERISTIC_UUID, data_to_send)

        print(f"已发送数据: {data_to_send}")


        # 接收数据

        received_data = await client.read_gatt_char(READ_CHARACTERISTIC_UUID)

        print(f"接收到数据: {received_data}")


        # 设置通知回调

        def notification_handler(sender, data):

            print(f"收到通知: {data}")


        # 启用通知

        await client.start_notify(READ_CHARACTERISTIC_UUID, notification_handler)


        # 保持连接一段时间以接收可能的通知

        print("等待接收通知...")

        await asyncio.sleep(10)


        # 禁用通知

        await client.stop_notify(READ_CHARACTERISTIC_UUID)


async def main():

    await scan_devices()

    

    if not discovered_devices:

        print("没有发现设备。")

        return


    # 让用户选择一个设备

    choice = int(input("请选择要连接的设备编号: ")) - 1

    if 0 <= choice < len(discovered_devices):

        selected_device = discovered_devices[choice]

        await connect_and_communicate(selected_device)

    else:

        print("无效的选择。")


# 运行主异步函数

asyncio.run(main())

这个脚本做了以下几件事:


导入必要的库:asyncio用于异步操作,bleak用于BLE通信。


定义一个全局列表discovered_devices来存储扫描到的设备。


定义scan_devices()函数来扫描并列出附近的BLE设备。


定义connect_and_communicate()函数来处理与选定设备的连接、数据发送和接收。


在main()函数中:


首先调用scan_devices()扫描设备。

让用户选择一个要连接的设备。

调用connect_and_communicate()与选定的设备进行通信。

在connect_and_communicate()函数中:


连接到选定的设备。

发送数据到写入特征。

从读取特征接收数据。

设置通知处理函数并启用通知。

保持连接10秒钟以接收可能的通知。

最后禁用通知并断开连接。

请注意以下几点:


您需要将WRITE_CHARACTERISTIC_UUID和READ_CHARACTERISTIC_UUID替换为您的BLE设备实际使用的特征UUID。

不同的BLE设备可能有不同的服务和特征,您可能需要根据具体设备调整代码。

在某些操作系统上,您可能需要特殊权限才能访问蓝牙功能。

这个示例使用了10秒的等待时间来接收通知,您可以根据需要调整这个时间。

这个示例提供了一个更完整的BLE操作流程,包括设备扫描、用户选择、连接、数据发送和接收。在实际应用中,您可能还需要添加更多的错误处理,实现特定的数据解析逻辑,或者根据您的设备协议调整通信方式。



Python 中去除 console 窗口(也称为命令行窗口)的方法

在 Python 中去除 console 窗口(也称为命令行窗口)的方法会根据你的操作系统和应用程序类型而有所不同。以下是几种常用的方法:

对于 Windows 系统:

a. 使用 pythonw.exe 而不是 python.exe:

将你的 Python 脚本的扩展名从 .py 改为 .pyw,然后双击运行。.pyw 文件会自动使用 pythonw.exe 来运行,这个版本的 Python 解释器不会显示控制台窗口。


b. 使用 PyInstaller 创建无控制台的可执行文件:

pyinstaller --noconsole your_script.py


c. 在代码中隐藏控制台窗口(仅适用于 Windows):

import win32gui

import win32con


# 获取当前窗口的句柄

hwnd = win32gui.GetForegroundWindow()

# 隐藏窗口

win32gui.ShowWindow(hwnd, win32con.SW_HIDE)

注意:这需要安装 pywin32 库(pip install pywin32)。


对于 macOS 系统:

a. 创建一个 .app 应用程序:

你可以使用 py2app 将你的 Python 脚本打包成一个 macOS 应用程序。首先安装 py2app:

pip install py2app

然后创建一个 setup.py 文件:

from setuptools import setup


APP = ['your_script.py']

OPTIONS = {

    'argv_emulation': True,

    'packages': ['tkinter'],  # 如果你使用 tkinter

}


setup(

    app=APP,

    options={'py2app': OPTIONS},

    setup_requires=['py2app'],

)

最后,运行以下命令创建应用程序:

python setup.py py2app


对于 Linux 系统:

在 Linux 上,通常不需要特别的步骤来隐藏控制台窗口,因为图形应用程序默认就不会显示控制台。但如果你想创建一个独立的应用程序,可以考虑使用 PyInstaller:

pyinstaller --noconsole your_script.py

通用方法 - 使用 GUI 框架:

如果你的应用程序使用 GUI 框架(如 Tkinter、PyQt、wxPython 等),通常这些框架会自动处理窗口的创建,不会显示额外的控制台窗口。

例如,使用 Tkinter:

import tkinter as tk


root = tk.Tk()

# 你的 GUI 代码

root.mainloop()

当以正确的方式运行时(例如,在 Windows 上使用 .pyw 扩展名),这种 GUI 应用程序不会显示控制台窗口。

记住,虽然隐藏控制台窗口可以让你的应用程序看起来更专业,但它也会使调试变得更困难。在开发过程中,保留控制台窗口通常是有帮助的,只在最终发布时才隐藏它。


Python的websocket服务端客户端的详细介绍及例子

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许服务器和客户端之间进行实时、双向的数据交换,而不需要客户端不断地发送请求。

在Python中,我们可以使用websockets库来实现WebSocket服务端和客户端。首先,让我们安装这个库:

pip install websockets


下面是一个简单的WebSocket服务端示例:

import asyncio

import websockets


async def echo(websocket, path):

    async for message in websocket:

        print(f"Received message: {message}")

        await websocket.send(f"Echo: {message}")


async def main():

    server = await websockets.serve(echo, "localhost", 8765)

    await server.wait_closed()


if __name__ == "__main__":

    asyncio.run(main())

这个服务端会监听本地的8765端口,并对收到的每条消息进行回显。


下面是一个相应的WebSocket客户端示例:

import asyncio

import websockets


async def hello():

    uri = "ws://localhost:8765"

    async with websockets.connect(uri) as websocket:

        name = input("What's your name? ")

        await websocket.send(name)

        print(f"Sent: {name}")


        greeting = await websocket.recv()

        print(f"Received: {greeting}")


if __name__ == "__main__":

    asyncio.run(hello())

这个客户端会连接到服务端,发送用户输入的名字,然后接收并打印服务端的回复。


以下是一个更复杂的示例,展示了如何创建一个简单的聊天室:

服务端代码:

import asyncio

import websockets


# 存储所有连接的客户端

clients = set()


async def handle_client(websocket, path):

    # 添加新客户端

    clients.add(websocket)

    try:

        async for message in websocket:

            # 广播消息给所有客户端

            await broadcast(f"{websocket.remote_address}: {message}")

    finally:

        # 客户端断开连接时移除

        clients.remove(websocket)


async def broadcast(message):

    # 向所有连接的客户端发送消息

    if clients:

        await asyncio.wait([client.send(message) for client in clients])


async def main():

    server = await websockets.serve(handle_client, "localhost", 8765)

    print("Chat server started on ws://localhost:8765")

    await server.wait_closed()


if __name__ == "__main__":

    asyncio.run(main())



客户端代码:

import asyncio

import websockets

import aioconsole


async def receive_messages(websocket):

    while True:

        try:

            message = await websocket.recv()

            print(f"\nReceived: {message}")

        except websockets.ConnectionClosed:

            print("Connection closed")

            break


async def send_messages(websocket):

    while True:

        message = await aioconsole.ainput("Enter message: ")

        await websocket.send(message)


async def main():

    uri = "ws://localhost:8765"

    async with websockets.connect(uri) as websocket:

        receive_task = asyncio.create_task(receive_messages(websocket))

        send_task = asyncio.create_task(send_messages(websocket))

        await asyncio.gather(receive_task, send_task)


if __name__ == "__main__":

    asyncio.run(main())


在这个聊天室示例中,服务端维护了一个所有连接客户端的集合,并在收到消息时将其广播给所有客户端。客户端则同时运行两个协程:一个用于接收消息,另一个用于发送消息。

这些示例展示了如何使用Python的websockets库创建基本的WebSocket服务端和客户端。您可以根据具体需求进一步扩展这些代码,例如添加用户认证、消息过滤、错误处理等功能。



python调用MQTT的详细例子要求有注释

 Python调用MQTT的例子,并附带注释。这个例子将演示如何使用paho-mqtt库连接到MQTT代理,发布消息,以及订阅主题。

首先,确保您已经安装了paho-mqtt库:

pip install paho-mqtt


然后,这里是一个详细的示例代码:

import paho.mqtt.client as mqtt

import time


# MQTT代理的地址和端口

MQTT_BROKER = "test.mosquitto.org"

MQTT_PORT = 1883


# 主题

TOPIC = "example/topic"


# 当连接到MQTT代理时调用的回调函数

def on_connect(client, userdata, flags, rc):

    if rc == 0:

        print("Connected to MQTT Broker!")

    else:

        print(f"Failed to connect, return code {rc}")


# 当收到消息时调用的回调函数

def on_message(client, userdata, msg):

    print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")


# 创建MQTT客户端实例

client = mqtt.Client()


# 设置回调函数

client.on_connect = on_connect

client.on_message = on_message


# 连接到MQTT代理

client.connect(MQTT_BROKER, MQTT_PORT)


# 订阅主题

client.subscribe(TOPIC)


# 开始循环,以保持连接和处理传入消息

client.loop_start()


try:

    while True:

        # 每5秒发布一次消息

        client.publish(TOPIC, "Hello, MQTT!")

        print(f"Published message to topic `{TOPIC}`")

        time.sleep(5)


except KeyboardInterrupt:

    print("Exiting...")

    # 停止循环并断开连接

    client.loop_stop()

    client.disconnect()


这个例子的详细解释:

首先,我们导入所需的库: paho.mqtt.client 用于MQTT通信,time 用于添加延迟。

我们定义了MQTT代理的地址和端口,以及我们将要使用的主题。

on_connect 函数是一个回调,当客户端连接到MQTT代理时会被调用。它检查连接是否成功。

on_message 函数是另一个回调,当收到订阅主题的消息时会被调用。它打印接收到的消息和主题。

我们创建一个MQTT客户端实例,并设置回调函数。

然后,我们连接到MQTT代理并订阅指定的主题。

client.loop_start() 启动一个后台线程,处理网络循环。

在主循环中,我们每5秒发布一条消息到指定的主题。

最后,我们使用 try/except 块来优雅地处理程序终止。当用户按下Ctrl+C时,我们停止网络循环并断开连接。


这个例子展示了MQTT的基本操作:连接、发布和订阅。您可以根据需要修改代理地址、端口、主题和消息内容。记住,某些MQTT代理可能需要身份验证,在这种情况下,您需要在连接之前设置用户名和密码。

 

复杂的例子

import os

import cv2

import gradio as gr

import numpy as np

import random

import base64

import requests

import json

import time



def tryon(person_img, garment_img, seed, randomize_seed):

    post_start_time = time.time()

    if person_img is None or garment_img is None:

        gr.Warning("Empty image")

        return None, None, "Empty image"

    if randomize_seed:

        seed = random.randint(0, MAX_SEED)

    encoded_person_img = cv2.imencode('.jpg', cv2.cvtColor(person_img, cv2.COLOR_RGB2BGR))[1].tobytes()

    encoded_person_img = base64.b64encode(encoded_person_img).decode('utf-8')

    encoded_garment_img = cv2.imencode('.jpg', cv2.cvtColor(garment_img, cv2.COLOR_RGB2BGR))[1].tobytes()

    encoded_garment_img = base64.b64encode(encoded_garment_img).decode('utf-8')


    url = "http://" + os.environ['tryon_url'] + "Submit"

    token = os.environ['token']

    cookie = os.environ['Cookie']

    referer = os.environ['referer']

    headers = {'Content-Type': 'application/json', 'token': token, 'Cookie': cookie, 'referer': referer}

    data = {

        "clothImage": encoded_garment_img,

        "humanImage": encoded_person_img,

        "seed": seed

    }

    try:

        response = requests.post(url, headers=headers, data=json.dumps(data), timeout=50)

        # print("post response code", response.status_code)

        if response.status_code == 200:

            result = response.json()['result']

            status = result['status']

            if status == "success":

                uuid = result['result']

                # print(uuid)

    except Exception as err:

        print(f"Post Exception Error: {err}")

        raise gr.Error("Too many users, please try again later")

    post_end_time = time.time()

    print(f"post time used: {post_end_time-post_start_time}")


    get_start_time =time.time()

    time.sleep(9)

    Max_Retry = 12

    result_img = None

    info = ""

    err_log = ""

    for i in range(Max_Retry):

        try:

            url = "http://" + os.environ['tryon_url'] + "Query?taskId=" + uuid

            response = requests.get(url, headers=headers, timeout=20)

            # print("get response code", response.status_code)

            if response.status_code == 200:

                result = response.json()['result']

                status = result['status']

                if status == "success":

                    result = base64.b64decode(result['result'])

                    result_np = np.frombuffer(result, np.uint8)

                    result_img = cv2.imdecode(result_np, cv2.IMREAD_UNCHANGED)

                    result_img = cv2.cvtColor(result_img, cv2.COLOR_RGB2BGR)

                    info = "Success"

                    break

                elif status == "error":

                    err_log = f"Status is Error"

                    info = "Error"

                    break

            else:

                # print(response.text)

                err_log = "URL error, pleace contact the admin"

                info = "URL error, pleace contact the admin"

                break

        except requests.exceptions.ReadTimeout:

            err_log = "Http Timeout"

            info = "Http Timeout, please try again later"

        except Exception as err:

            err_log = f"Get Exception Error: {err}"

        time.sleep(1)

    get_end_time = time.time()

    print(f"get time used: {get_end_time-get_start_time}")

    print(f"all time used: {get_end_time-get_start_time+post_end_time-post_start_time}")

    if info == "":

        err_log = f"No image after {Max_Retry} retries"

        info = "Too many users, please try again later"

    if info != "Success":

        print(f"Error Log: {err_log}")

        gr.Warning("Too many users, please try again later")


    return result_img, seed, info


def start_tryon(person_img, garment_img, seed, randomize_seed):

    start_time = time.time()

    if person_img is None or garment_img is None:

        return None, None, "Empty image"

    if randomize_seed:

        seed = random.randint(0, MAX_SEED)

    encoded_person_img = cv2.imencode('.jpg', cv2.cvtColor(person_img, cv2.COLOR_RGB2BGR))[1].tobytes()

    encoded_person_img = base64.b64encode(encoded_person_img).decode('utf-8')

    encoded_garment_img = cv2.imencode('.jpg', cv2.cvtColor(garment_img, cv2.COLOR_RGB2BGR))[1].tobytes()

    encoded_garment_img = base64.b64encode(encoded_garment_img).decode('utf-8')


    url = "http://" + os.environ['tryon_url']

    token = os.environ['token']

    cookie = os.environ['Cookie']

    referer = os.environ['referer']


    headers = {'Content-Type': 'application/json', 'token': token, 'Cookie': cookie, 'referer': referer}

    data = {

        "clothImage": encoded_garment_img,

        "humanImage": encoded_person_img,

        "seed": seed

    }


    result_img = None

    try:

        session = requests.Session()

        response = session.post(url, headers=headers, data=json.dumps(data), timeout=60)

        print("response code", response.status_code)

        if response.status_code == 200:

            result = response.json()['result']

            status = result['status']

            if status == "success":

                result = base64.b64decode(result['result'])

                result_np = np.frombuffer(result, np.uint8)

                result_img = cv2.imdecode(result_np, cv2.IMREAD_UNCHANGED)

                result_img = cv2.cvtColor(result_img, cv2.COLOR_RGB2BGR)

                info = "Success"

            else:

                info = "Try again latter"

        else:

            print(response.text)

            info = "URL error, pleace contact the admin"

    except requests.exceptions.ReadTimeout:

        print("timeout")

        info = "Too many users, please try again later"

        raise gr.Error("Too many users, please try again later")

    except Exception as err:

        print(f"其他错误: {err}")

        info = "Error, pleace contact the admin"

    end_time = time.time()

    print(f"time used: {end_time-start_time}")


    return result_img, seed, info


MAX_SEED = 999999


example_path = os.path.join(os.path.dirname(__file__), 'assets')


garm_list = os.listdir(os.path.join(example_path,"cloth"))

garm_list_path = [os.path.join(example_path,"cloth",garm) for garm in garm_list]


human_list = os.listdir(os.path.join(example_path,"human"))

human_list_path = [os.path.join(example_path,"human",human) for human in human_list]


css="""

#col-left {

    margin: 0 auto;

    max-width: 430px;

}

#col-mid {

    margin: 0 auto;

    max-width: 430px;

}

#col-right {

    margin: 0 auto;

    max-width: 430px;

}

#col-showcase {

    margin: 0 auto;

    max-width: 1100px;

}

#button {

    color: blue;

}

"""


def load_description(fp):

    with open(fp, 'r', encoding='utf-8') as f:

        content = f.read()

    return content


def change_imgs(image1, image2):

    return image1, image2


with gr.Blocks(css=css) as Tryon:

    gr.HTML(load_description("assets/title.md"))

    with gr.Row():

        with gr.Column(elem_id = "col-left"):

            gr.HTML("""

            <div style="display: flex; justify-content: center; align-items: center; text-align: center; font-size: 20px;">

                <div>

                Step 1.  Upload a person image ⬇️

                </div>

            </div>

            """)

        with gr.Column(elem_id = "col-mid"):

            gr.HTML("""

            <div style="display: flex; justify-content: center; align-items: center; text-align: center; font-size: 20px;">

                <div>

                Step 2. Upload a garment image ⬇️

                </div>

            </div>

            """)

        with gr.Column(elem_id = "col-right"):

            gr.HTML("""

            <div style="display: flex; justify-content: center; align-items: center; text-align: center; font-size: 20px;">

                <div>

                Step 3. Press “Run” to get try-on results

                </div>

            </div>

            """)

    with gr.Row():

        with gr.Column(elem_id = "col-left"):

            imgs = gr.Image(label="Person image", sources='upload', type="numpy")

            # category = gr.Dropdown(label="Garment category", choices=['upper_body', 'lower_body', 'dresses'],  value="upper_body")

            example = gr.Examples(

                inputs=imgs,

                examples_per_page=12,

                examples=human_list_path

            )

        with gr.Column(elem_id = "col-mid"):

            garm_img = gr.Image(label="Garment image", sources='upload', type="numpy")

            example = gr.Examples(

                inputs=garm_img,

                examples_per_page=12,

                examples=garm_list_path

            )

        with gr.Column(elem_id = "col-right"):

            image_out = gr.Image(label="Result", show_share_button=False)

            with gr.Row():

                seed = gr.Slider(

                    label="Seed",

                    minimum=0,

                    maximum=MAX_SEED,

                    step=1,

                    value=0,

                )

                randomize_seed = gr.Checkbox(label="Random seed", value=True)

            with gr.Row():

                seed_used = gr.Number(label="Seed used")

                result_info = gr.Text(label="Response")

            # try_button = gr.Button(value="Run", elem_id="button")

            test_button = gr.Button(value="Run", elem_id="button")



    # try_button.click(fn=start_tryon, inputs=[imgs, garm_img, seed, randomize_seed], outputs=[image_out, seed_used, result_info], api_name='tryon',concurrency_limit=10)

    test_button.click(fn=tryon, inputs=[imgs, garm_img, seed, randomize_seed], outputs=[image_out, seed_used, result_info], api_name=False, concurrency_limit=45)


    with gr.Column(elem_id = "col-showcase"):

        gr.HTML("""

        <div style="display: flex; justify-content: center; align-items: center; text-align: center; font-size: 20px;">

            <div> </div>

            <br>

            <div>

            Virtual try-on examples in pairs of person and garment images

            </div>

        </div>

        """)

        show_case = gr.Examples(

            examples=[

                ["assets/examples/model2.png", "assets/examples/garment2.png", "assets/examples/result2.png"],

                ["assets/examples/model3.png", "assets/examples/garment3.png", "assets/examples/result3.png"],

                ["assets/examples/model1.png", "assets/examples/garment1.png", "assets/examples/result1.png"],

            ],

            inputs=[imgs, garm_img, image_out],

            label=None

        )


Tryon.queue(api_open=False).launch(show_api=False)


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   



   


   


   


   


   


   



   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   


   



   

Top