前言
因为现在是大一,学校还没开python的课,我现在是假期里纯靠自学的,有些问题可能有点蠢,写的代码也不大规范(比如各种奇奇怪怪的变量名,这个连我自己都看不下去了,但是因为不影响运行我就没改),这也是我第一次写博客,有哪里写的不好也希望各位能够见谅……
这里是代码用到的库:
pyside2,cython,auto-py-to-exe,re,threading,time,os,shutil,
另外还需要vs c++生成器。
介绍:
这个程序是我为了练习python的图形化界面写的,主要是用来熟悉pyside2的各个控件的应用、多窗口运行、多线程运行的,所以可能会有什么奇奇怪怪的bug……
程序代码
#导入所需模块
from PySide2.QtWidgets import QApplication,QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile
from PySide2.QtGui import QIcon
import os
import threading,time
import re
###TYMLISY###
#多线程
def threadings(fun_name):
x1 = threading.Thread(target=fun_name, args=("",))
x1.start()
#主窗口程序
class lode_ui:
def __init__(self):
# 加载ui定义
# ui=input("ui路径:")
uiloder=QFile("untitled.ui")
uiloder.open(QFile.ReadOnly)
uiloder.close()
# 用“window”接收ui里的数据,创建一个窗口
self.window=QUiLoader().load(uiloder)
#事件绑定
self.window.button1_1.clicked.connect(self.apte3)
self.window.button1_2.clicked.connect(self.apte4)
self.window.button3.clicked.connect(self.pytoc2)
self.window.button2.clicked.connect(self.pip2)
self.window.check_2.clicked.connect(win2)
#打开auto-py-to-exe页面
def apte(self):
def fuc():
self.cmd("auto-py-to-exe")
threadings(fuc())
def apte3(self):
tx=threading.Thread(target=lode_ui.apte,args=(self,))
tx.start()
#直接使用pipinstaller打包单文件
def apte2(self):
def fuc():
# 获取textedit里的文本数据
# info = self.window.input.toPlainText()
# 获取lineedit里的文本数据
info = self.window.input1.text()
info2=f"pyinstaller -F {info}"
self.cmd(info2)
time.sleep(0.5)
m = re.findall(r'([^<>/\\\|:""\*\?]+)\.\w+$', info)[0]
os.remove(f"{m}.spec")
threadings(fuc())
def apte4(self):
t3 = threading.Thread(target=lode_ui.apte2, args=(self,))
t3.start()
#用于执行cmd的命令并把结果输出
def cmd(self,command):
r = os.popen(command)
info = r.readlines()
with open("data.data", "a", encoding="utf-8") as f:
f.write("\n")
for line in info:
line = line.strip('\r\n')
f.write(line)
f.write("\n\n")
#使用pip下载python包(使用清华镜像)
def pip(self):
info = self.window.input2.text()
command =f"pip install {info} -i https://pypi.tuna.tsinghua.edu.cn/simple" # 可以直接在命令行中执行的命令
self.cmd(command)
def pip2 (self):
t5 = threading.Thread(target=lode_ui.pip, args=(self,))
t5.start()
#使用cython将python转c语言提高递归和循环速度
def pytoc(self):
import shutil
shutil.copy("pytoc.py", "mode.py")
name = self.window.input3.text()
name2 = name.replace('.py', '.pyx')
shutil.copy(name, name2)
with open("mode1.py","r",encoding="utf-8") as f:
txt=f.read()
txt=txt.replace("name2",name2.replace("\\","\\\\"))
with open("mode1.py","w",encoding="utf-8") as f:
f.write(txt)
self.cmd("python mode1.py build_ext --inplace")
test = name2
m = re.findall(r'([^<>/\\\|:""\*\?]+)\.\w+$', test)[0]
shutil.copy(f"{m}.cp310-win_amd64.pyd", name2.replace(f"{m}.pyx",f"{m}.cp310-win_amd64.pyd"))
os.remove(f"{m}.cp310-win_amd64.pyd")
def pytoc2(self):
t4 = threading.Thread(target=lode_ui.pytoc, args=(self,))
t4.start()
#打开输出界面
def win2():
win2 = lode_ui2()
win2.window.close()
with open("data.data","r",encoding="utf-8") as f:
txt=f.read()
win2.window.txt.setPlainText(txt)
win2.window.show()
def sl():
time.sleep(1000)
win2.window.close()
t4 = threading.Thread(target=sl)
t4.start()
#主函数
def start():
#窗口初始化
app = QApplication([])
# 主窗口图标
app.setWindowIcon(QIcon('logo.png'))
#导入ui
win = lode_ui()
win2 = lode_ui2()
# 检查是否为第一次启动,是则安装必要库
with open("data.data", "a", encoding="utf-8")as f:
f.write("")
with open("data.data", "r", encoding="utf-8") as f:
if f.read()=="":
list=["cython","auto-py-to-exe"]
t1 = threading.Thread(target=lode_ui.cmd,args=("", f"pip install --upgrade pip",))
t1.start()
for info in list:
t1=threading.Thread(target=lode_ui.cmd,args=("",f"pip install {info} -i https://pypi.tuna.tsinghua.edu.cn/simple",))
t1.start()
QMessageBox.about(win2.window,"提示",f"请先安装vs c++")
with open("data.data","w",encoding="utf-8")as f:
f.write("……")
win.window.show()
win2.window.close()
#循环
app.exec_()
#加载输出框的ui
class lode_ui2:
def __init__(self):
# 加载ui定义
# ui=input("ui路径:")
uiloder=QFile("untitled2.ui")
uiloder.open(QFile.ReadOnly)
uiloder.close()
self.window=QUiLoader().load(uiloder)
#程序入口
if __name__ == '__main__':
t1=threading.Thread(target=start)
t1.start()
exe文件和源码与ui:
https://download.csdn.net/download/m0_74979077/87500070
写这个代码时的一些问题:
关键点1:
刚开始的时候我是写的单线程,后来发现当点击按钮后,如果该事件需要比较长的时间来运行,那么主界面就会无响应,只能等该事件结束后才能恢复。
解决方法:我引入了threading模块,但刚开始的时候我是将多线程放在了类中的函数里面直接运行的(例:
def apte2(self):
def fuc():
info = self.window.input1.text()
info2=f"pyinstaller -F {info}"
self.cmd(info2)
threadings(fuc())
当按下按钮时调用apte2这个函数)
但是当时我发现这样还是会阻塞主程序,后来经过尝试我又给它套了一层函数,然后莫名其妙就好了。
( def apte4(self):
t3 = threading.Thread(target=lode_ui.apte2, args=(self,))
t3.start()
按下按钮时通过apte4去调用apte2)
(这里希望有大佬帮能我解释一下是为什么)
关键点2:
因为要图形化,控制台的那个大黑框当然是要去掉的,但是去掉以后就看不到程序到底在干什么了,所以需要一个可以将控制台的输出接入到输出框的功能。
( def cmd(self,command):
r = os.popen(command)
info = r.readlines()
with open("data.data", "a", encoding="utf-8") as f:
f.write("\n")
for line in info:
line = line.strip('\r\n')
f.write(line)
f.write("\n\n")
)
我这里用的是os.popen()来取到控制台的输出,但是这个方法只能接收到部分的输出,而且效率较低。(下面的问题2)
关键点3:
因为传入的文件地址前缀是随机的,所以我需要在地址中用正则表达式取出文件的名字,以便后面的各种操作, m = re.findall(r'([^<>/\\\|:""\*\?]+)\.\w+$')。(里面的正则不是我写的,是网上随便找的,这个看着就好麻烦……)
关键点4:
因为需要在不同文件间传递文件的地址,但是“\”是有转义的作用的,直接传的话如果“\”后有n、t、a之类的字母时会产生错误,所以我用name2.replace("\\","\\\\"))来替换,取消了转义。(这点真的不是凑数,我当时是真的不懂,还去查了百度……)
问题1:
因为我不知道子线程怎么关闭,所以在主程序被关闭后会有多余的线程在继续运行,现在我只能用
'
time.sleep(1000)
window.close()
'
这样的笨办法在不影响主程序的情况下来结束……(但好像并没有什么样用)
问题2:
pyside2的可编辑文本怎么样实时输出控制台的数据,我现在只能用把数据写进文件,再让它读取的方式输出数据,虽然功能是有了,但是这样的话输出延迟会有点大。
问题3:
输出窗口怎么让它只能最多同时存在一个,然后还可以实时刷新里面的数据,感觉每次要看输出都要重新点开一个输出窗口会很麻烦。
问题4:
关于包含动态库打包时要注意的点,希望有大佬帮我解释一下。
问题5:
关于程序运行需要的各种包和依赖如何检查,这个程序在我的电脑上可以正常运行,但是在分享给朋友后,在他的电脑上就会因为缺少cython以及vs c++导致部分功能无法使用,但是并没有导致程序退出或者卡死。
所以后来我把pip install cython之类所需要的的库直接写到了程序里。
但是我不知道要怎么样才可以让缺少了什么东西的错误可以被抛出来提醒使用者去下载,比如vs c++。
最后:
希望有大佬看到后可以顺便回答一下我的几个问题,求求了。
另外如果对这段代码有什么建议可以评论里说一下,如果有和我一样刚刚开始学的人我们也可以交流一下。