kerln888 发表于 2023-7-12 10:27:03

求助,pyinstaller打包以后和pycharm运行的不一样

学习了多进程,想试一下。代码如下,在pycharm运行,会生成一个窗口,其他进程计算square。运行pyinstaller -F -w main.py打包以后成exe以后,运行会弹出很多个tk窗口,导致系统卡死。
请教一下各位大佬,这是什么问题。该如何解决

# cython: language_level=3
# -*- coding:utf-8 -*-
import sys
import tkinter as tk
import time
import ntplib
import os
import multiprocessing


# 创建多进程要跑的方法,while循环
def square(i):
    while True:
      print(i*i*i*i*i*i*i)
      # time.sleep(5)


def start():
    cpuNum = multiprocessing.cpu_count()
    print(cpuNum)
    for i in range(cpuNum):
      p = multiprocessing.Process(target=square, args=(i,))
      p.daemon = True
      p.start()
    print('CPU number:' + str(multiprocessing.cpu_count()))
    for p in multiprocessing.active_children():
      print('Child process name: ' + p.name + ' id: ' + str(p.pid))
    loginWindow = tk.Tk()
    loginWindow.title('test')
    screenWidthLogin = loginWindow.winfo_screenwidth()
    screenHeightLogin = loginWindow.winfo_screenheight()
    loginWindow.geometry('400x260+{}+{}'.format((screenWidthLogin - 400) // 2, (screenHeightLogin - 260) // 2))
    loginWindow.resizable(False, False)
    loginWindow.config(bg='pink')

    loginFrame = tk.Frame(loginWindow, relief='sunken')
    loginFrame.place(relx=0.5, rely=0.5, relheight=0.985, relwidth=0.985, anchor='center')

    # 设置登录窗口标签属性
    loginLabelUser = tk.Label(loginFrame, text='用户名', width=5, height=1)
    loginLabelUser.place(relx=0.27, rely=0.345)
    loginLabelPassword = tk.Label(loginFrame, text='密   码', width=5, height=1)
    loginLabelPassword.place(relx=0.27, rely=0.555)

    loginEntryUser = tk.Entry(loginFrame, width=15)
    loginEntryUser.place(relx=0.44, rely=0.345)
    loginEntryPassword = tk.Entry(loginFrame, show='*', width=15)
    loginEntryPassword.place(relx=0.44, rely=0.555)

    loginButtonLogin = tk.Button(loginFrame, text='登录', width=15, height=1)
    loginButtonLogin.place(relx=0.2, rely=0.8)
    loginButtonExit = tk.Button(loginFrame, text='退出', width=15, height=1, command=loginWindow.destroy)
    loginButtonExit.place(relx=0.5, rely=0.8)

    loginWindow.mainloop()

    sys.exit()


if __name__ == '__main__':
    while True:
      try:
            c = ntplib.NTPClient()
            response = c.request('ntp2.aliyun.com')
            ts = response.tx_time
            _date = time.strftime('%Y-%m-%d', time.localtime(ts))
            _time = time.strftime('%X', time.localtime(ts))
            # 设置本机系统时间
            os.system('date {} && time {}'.format(_date, _time))
            break
      except Exception as time_out:
            print('同步超时', time_out)
            continue

    start()

歌者文明清理员 发表于 2023-7-12 10:27:04

kerln888 发表于 2023-7-12 10:56
是因为物理机运行以后会死机,我才在虚拟机里面运行截图

打包成exe后出现多个窗口的问题是由于多进程的特性引起的。在Windows系统上,当使用PyInstaller将多进程的Python脚本打包成exe时,由于进程间通信的机制不同,可能会出现多个窗口弹出的情况。

解决这个问题的方法是在创建进程之前判断当前进程是否为主进程,然后再创建子进程。可以使用if __name__ == '__main__':来判断当前进程是否为主进程,只在主进程中创建子进程。

修改代码如下:

# cython: language_level=3
# -*- coding:utf-8 -*-
import sys
import tkinter as tk
import time
import ntplib
import os
import multiprocessing


# 创建多进程要跑的方法,while循环
def square(i):
    while True:
      print(i*i*i*i*i*i*i)
      # time.sleep(5)


def start():
    cpuNum = multiprocessing.cpu_count()
    print(cpuNum)
    for i in range(cpuNum):
      p = multiprocessing.Process(target=square, args=(i,))
      p.daemon = True
      p.start()
    print('CPU number:' + str(multiprocessing.cpu_count()))
    for p in multiprocessing.active_children():
      print('Child process name: ' + p.name + ' id: ' + str(p.pid))
    loginWindow = tk.Tk()
    loginWindow.title('test')
    screenWidthLogin = loginWindow.winfo_screenwidth()
    screenHeightLogin = loginWindow.winfo_screenheight()
    loginWindow.geometry('400x260+{}+{}'.format((screenWidthLogin - 400) // 2, (screenHeightLogin - 260) // 2))
    loginWindow.resizable(False, False)
    loginWindow.config(bg='pink')

    loginFrame = tk.Frame(loginWindow, relief='sunken')
    loginFrame.place(relx=0.5, rely=0.5, relheight=0.985, relwidth=0.985, anchor='center')

    # 设置登录窗口标签属性
    loginLabelUser = tk.Label(loginFrame, text='用户名', width=5, height=1)
    loginLabelUser.place(relx=0.27, rely=0.345)
    loginLabelPassword = tk.Label(loginFrame, text='密   码', width=5, height=1)
    loginLabelPassword.place(relx=0.27, rely=0.555)

    loginEntryUser = tk.Entry(loginFrame, width=15)
    loginEntryUser.place(relx=0.44, rely=0.345)
    loginEntryPassword = tk.Entry(loginFrame, show='*', width=15)
    loginEntryPassword.place(relx=0.44, rely=0.555)

    loginButtonLogin = tk.Button(loginFrame, text='登录', width=15, height=1)
    loginButtonLogin.place(relx=0.2, rely=0.8)
    loginButtonExit = tk.Button(loginFrame, text='退出', width=15, height=1, command=loginWindow.destroy)
    loginButtonExit.place(relx=0.5, rely=0.8)

    loginWindow.mainloop()

    sys.exit()


if __name__ == '__main__':
    if getattr(sys, 'frozen', False):
      multiprocessing.freeze_support()
   
    while True:
      try:
            c = ntplib.NTPClient()
            response = c.request('ntp2.aliyun.com')
            ts = response.tx_time
            _date = time.strftime('%Y-%m-%d', time.localtime(ts))
            _time = time.strftime('%X', time.localtime(ts))
            # 设置本机系统时间
            os.system('date {} && time {}'.format(_date, _time))
            break
      except Exception as time_out:
            print('同步超时', time_out)
            continue

    start()
在打包之前,将multiprocessing.freeze_support()代码添加到if __name__ == '__main__':的判断中,这可以确保在Windows系统上运行exe文件时能正确处理多进程。

我修改了你提供的代码,主要是在启动多进程之前添加了multiprocessing.freeze_support()代码,并在主进程中创建了子进程。这样可以解决在Windows系统上使用PyInstaller打包成exe后出现多个窗口的问题。同时,将整个代码片段放置在if __name__ == '__main__':判断中,确保只有在直接运行脚本时才执行相关的代码,而在打包成exe后不执行这部分代码。

参考:https://fishc.com.cn/thread-226709-1-1.html

陶远航 发表于 2023-7-12 10:27:17

在使用 PyInstaller 打包后,程序的行为可能与在 PyCharm 中运行时不一样的原因是,在打包过程中可能会有一些与 GUI 相关的库或设置没有正确处理。

在你的代码中,你使用了 tkinter 创建了一个窗口。尝试将以下几行代码放到 if __name__ == '__main__': 的条件语句中:

loginWindow = tk.Tk()
# ...
loginWindow.mainloop()


然后,重新使用 PyInstaller 打包并运行生成的可执行文件,看看是否仍然会弹出多个窗口。这样做可以确保只有主进程创建并显示窗口。

如果问题仍然存在,你可以尝试使用 PyInstaller 的一些选项来调整打包过程的行为。例如,可以尝试使用 --noconsole 选项来禁用控制台窗口,或者使用 --onefile 选项生成单个可执行文件而不是文件夹。

例如,运行以下命令进行打包:

pyinstaller --onefile -w main.py


请注意,PyInstaller 的使用方法可能因版本而异,以上命令仅提供一种示范,具体命令参数可根据实际情况进行调整。

如果问题仍然存在,请尝试从 PyInstaller 的官方文档中查找其他解决方案:https://pyinstaller.readthedocs.io/

歌者文明清理员 发表于 2023-7-12 10:51:13

不要用win7虚拟机吧

kerln888 发表于 2023-7-12 10:56:01

歌者文明清理员 发表于 2023-7-12 10:51
不要用win7虚拟机吧

是因为物理机运行以后会死机,我才在虚拟机里面运行截图

kerln888 发表于 2023-7-12 11:12:25

歌者文明清理员 发表于 2023-7-12 10:27
打包成exe后出现多个窗口的问题是由于多进程的特性引起的。在Windows系统上,当使用PyInstaller将多进程 ...

谢谢!!!!

歌者文明清理员 发表于 2023-7-12 11:12:32

{:10_301:}感谢楼主最佳之恩!
页: [1]
查看完整版本: 求助,pyinstaller打包以后和pycharm运行的不一样