cx_Freeze 应用程序不会在命令提示符下打开并且不会打印任何错误

cx_Freeze app will not open in command prompt and prints no errors

首先,我在 Windows 10.

上使用 python 3.6.5 和 cx_Freeze 6.0b1

我有一个使用 wxPython 构建的程序,想使用 cx_Freeze 冻结代码。我已经为此工作了好几天,并且已经阅读了关于此问题的每个 post 的感受,但仍然没有取得任何成功。 运行 命令提示符或双击应用程序中的 .exe 将无法打开。我还尝试更改 NoneConsoleWin32GUI 之间的 base,但都没有成功。

此时我已经快要放弃了。我会很高兴只是得到一个错误信息追下去。

setup.py:

import os
import sys
from cx_Freeze import setup, Executable

os.environ['TCL_LIBRARY'] = r'C:\Users\Chad\Anaconda3\Library\lib\tcl8.6'
os.environ['TK_LIBRARY'] = r'C:\Users\Chad\Anaconda3\Library\lib\tk8.6'

include_lst = []
package_lst = ['numpy', 'scipy', 'pulp', 'pubsub', 'sqlite3',
               'numpy.core._methods']
exclude_lst = ['matplotlib', 'tkinter', 'PyQt4.QtSql', 'PyQt5',
               'PyQt4.QtNetwork', 'PyQt4.QtScript', 'sqlalchemy']

base = None

setup (
       name='',
       version='',
       author='',
       author_email='',
       options={'build_exe':
                   {'packages': package_lst,
                    'excludes': exclude_lst,
                   'include_files': include_lst,
                   'include_msvcr': True
                   }
                },
       executables=[Executable('main.py',  base=base, icon=None)]
       )

我可以毫无问题地冻结以下程序,所以这可能不是 cx_Freeze 问题,而是我的 main.py 文件中的问题?

 if __name__.endswith('__main__'):
        print('Hello World')

至于准备一个最小的例子,我很抱歉,但我不确定如何去做,因为这个程序确实使用了一个 sqlite3 数据库和大约 21 个内部模块。但是,在尝试时我认为问题出在连接到数据库上,所以下面是一个基本示例,但如果没有数据库,我怀疑您是否会因为数据库问题而重现。有了这段代码,一旦我冻结它,命令就不会发生任何事情,它只会转到下一行。

main.py:

import util
import wx


class MasterPage (wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.conn = util.DataBaseManager.DataBaseManager()

        self.createFrame()

    def createFrame(self):
        self.width, self.height = wx.GetDisplaySize()
        self.SetTitle('Test')
        self.SetSize(wx.Size((self.width-50, self.height-50)))
        self.SetMinSize((1080, 720))
        self.W, self.H = self.GetSize()
        self.Bind(wx.EVT_CLOSE, self.onQuit)
        self.Centre()
        self.statusbar = self.CreateStatusBar(2)
        self.statusbar.SetStatusWidths([self.W * 67, self.W * .23])

    def onQuit(self, event):
        """Checks to make sure the user wants to leave the program"""
        dlg = wx.MessageDialog(self,'Do you really want to quit?',
                               'Confirm Exit',
                                wx.ICON_QUESTION|wx.OK|wx.CANCEL )
        result = dlg.ShowModal()
        dlg.Destroy
        if result == wx.ID_OK:
                self.Destroy()

if __name__.endswith('__main__'):
    app = wx.App()
    MasterPage(None).Show()
    app.MainLoop()

util.DataBaseManager.py:

import pandas as pd
import sys
import os
import sqlite3


class DataBaseManager(object):

    def __init__(self):
        """Creates a connection to the database requested"""
        try:
            self.db = self.findDataPath('master.db')
            self.conn = sqlite3.connect(self.db)
            self.cursor = self.conn.cursor()
        except sqlite3.Error as e:
            print(e)

    def executePandasQuery(self, sql_statement):
        self.df = pd.read_sql_query(sql_statement, self.conn)
        self.conn.commit()
        return self.df

    def executeCursorQuery(self, sql_statement, values):
        if values == 'na':
            self.cursor.execute(sql_statement)
        else:
            self.cursor.execute(sql_statement, values)
        self.conn.commit()
        return self.cursor

    def __del__(self):
        self.conn.close()

    def findDataPath(self, filename):
        """
        Get the correct path for outside data works with cx_freeze
        :params str filename: the filename looking for
        :returns: the absolute path of the file.
        :rtype: string
        """
        if getattr(sys, 'frozen', False):
            # The application is frozen
            p = os.path.abspath(os.path.dirname(sys.argv[0]))
            p = '{}\{}'.format(p,filename)
        else:
            # The application is not frozen
            p = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            p = '{}\{}'.format(p,filename)
        return p

编辑#2

我认为这是导入我的自定义模块的问题 util。如果我在 main.py 中的 import util 之前放置打印语句,打印语句将 运行 但随后应用程序关闭,命令提示符转到下一行。

关于连接数据库的问题:如何在冻结的应用程序中创建数据库文件master.db,它位于何处?查看您发布的代码,我了解以下内容:

  • 非冻结应用中,master.db文件在util包目录(主应用目录)的父目录下

  • 您没有告诉 cx_Freezemaster.db 文件显式包含到构建目录中。由于这个文件在主应用程序目录中,它不会被 cx_Freeze 自动包含(查看构建目录以查看是否看到该文件)。

我看到至少有两种方法可以解决这个问题:

  1. master.db文件移动到非冻结应用程序中的util包目录中。 cx_Freeze 应该包括整个 util 目录,包括数据库文件。将 findDataPath 函数更改为:

    def findDataPath(self, filename):
        """
        Get the correct path for outside data works with cx_freeze
        :params str filename: the filename looking for
        :returns: the absolute path of the file.
        :rtype: string
        """
    
        p = os.path.dirname(os.path.abspath(__file__))
        p = os.path.join(p, filename)
        return p
    

    这应该适用于非冻结和冻结的应用程序。 (备注:使用 os.path.join 以独立于平台的方式加入路径)。

  2. master.db 文件保存在非冻结应用程序的主目录中,并告诉 cx_Freeze 将数据库文件包含到 lib 子目录中 build目录,即冻结应用中util包目录的父目录。您可以通过将元组 (sourcedestination) 传递到安装脚本中 include_files 列表选项的相应条目,让 cx_Freeze 将文件包含到特定位置:

    include_lst = [('master.db', os.path.join('lib', 'master.db'))]
    

    将您的 findDataPath 函数更改为:

    def findDataPath(self, filename):
        """
        Get the correct path for outside data works with cx_freeze
        :params str filename: the filename looking for
        :returns: the absolute path of the file.
        :rtype: string
        """
    
        p = os.path.dirname((os.path.dirname(os.path.abspath(__file__)))
        p = os.path.join(p, filename)
        return p
    

    这也适用于非冻结和冻结应用程序。