cx_freeze打包tkinter+speedtest脚本出现模块与stdout错误
问题解决:cx_freeze打包tkinter+speedtest脚本报错
AttributeError: 'NoneType' object has no attribute 'fileno' 错误原因
你的推测完全正确:当用cx_freeze把tkinter GUI程序打包成无控制台的exe时,sys.stdout会被设为None,而speedtest库在初始化阶段会尝试访问sys.stdout.fileno(),触发该错误。开头的__builtin__报错是speedtest库兼容Python2/3时的次要问题,核心矛盾还是stdout缺失。
解决方案
方案1:打包时保留控制台
修改cx_freeze的setup.py配置,不使用Win32GUI作为base,让打包后的exe附带控制台,保证sys.stdout存在:
import sys from cx_Freeze import setup, Executable # 不设置base为Win32GUI,默认生成带控制台的程序 target = Executable( script="st.py" # base="Win32GUI" # 注释掉这一行 ) setup( name="InternetSpeedTracker", version="1.0", description="Internet Speed Test Tool", executables=[target] )
方案2:在脚本中手动重定向stdout/stderr
如果不想保留控制台,可在导入speedtest前,将sys.stdout和sys.stderr重定向到虚拟对象,避免speedtest报错:
import sys import io from tkinter import * # 重定向标准输出/错误到虚拟IO对象 sys.stdout = io.StringIO() sys.stderr = io.StringIO() from speedtest import Speedtest # 后续代码保持不变 def update_text(): speed_test = Speedtest() download = speed_test.download() upload = speed_test.upload() download_speed = round(download / (10**6), 2) upload_speed = round(upload / (10**6), 2) down_label.config(text= "Download Speed - " + str(download_speed) + "Mbps") up_label.config(text= "Upload Speed - " + str(upload_speed) + "Mbps") root = Tk() root.title("Internet Speed Tracker") root.geometry('300x300') button = Button(root, text="Get Speed", width=30, command=update_text) button.pack() down_label = Label(root, text="") down_label.pack() up_label = Label(root, text="") up_label.pack() root.mainloop()
方案3:修改speedtest库初始化代码(不推荐)
找到speedtest.py文件(路径为c:\@python\@w4itdev\lib\site-packages\speedtest.py),修改第166行附近代码,增加sys.stdout为None的判断:
原代码:
buf = FileIO(f.fileno(), 'w')
修改为:
import io if f is not None: buf = FileIO(f.fileno(), 'w') else: buf = io.StringIO()
此方法需修改第三方库代码,后续库更新会失效,优先选用前两种方案。
内容的提问来源于stack exchange,提问作者Julian Wise
相关产品推荐
相关产品推荐

