Объединение ресурсов GTK с помощью py2exe

Я использую Python 2.6 и PyGTK 2.22.6 из установщика « все-в-одном» в Windows XP, пытаясь создать однофайльный исполняемый файл (через py2exe ) для моего приложения.

Моя проблема заключается в том, что когда я запускаю свое приложение как скрипт (т. Е. Не встроенный в .exe файл, так же, как свободный набор .py файлов), он использует родную тему Windows, но когда я запускаю встроенный exe Я вижу тему GTK по умолчанию.

Я знаю, что эту проблему можно устранить, скопировав кучу файлов в каталог dist созданный py2exe, но все, что я прочитал, включает в себя ручное копирование данных, тогда как я хочу, чтобы это была автоматическая часть процесса сборки. Кроме того, все, что C:\Python2x\Lib\site-packages\gtk-2.0\runtime\... темы (включая FAQ ), устарело – PyGTK теперь сохраняет свои файлы в C:\Python2x\Lib\site-packages\gtk-2.0\runtime\... и просто копирует lib и etc каталоги не устраняют проблему.

Мои вопросы:

  1. Я хотел бы иметь возможность программно находить данные времени выполнения GTK в setup.py а не в жестких кодировках. Как мне это сделать?

  2. Каковы минимальные ресурсы, которые мне нужно включить?

Обновление: я, возможно, почти ответил # 2 методом проб и ошибок. Для темы «wimp» (т. Е. MS Windows) для работы мне нужны файлы из:

 runtime\lib\gtk-2.0\2.10.0\engines\libwimp.dll runtime\etc\gtk-2.0\gtkrc runtime\share\icons\* runtime\share\themes\MS-Windows и runtime\lib\gtk-2.0\2.10.0\engines\libwimp.dll runtime\etc\gtk-2.0\gtkrc runtime\share\icons\* runtime\share\themes\MS-Windows во runtime\lib\gtk-2.0\2.10.0\engines\libwimp.dll runtime\etc\gtk-2.0\gtkrc runtime\share\icons\* runtime\share\themes\MS-Windows 

… без префикса runtime , но в остальном с той же структурой каталогов, сидя непосредственно в каталоге dist создаваемом py2exe. Но откуда 2.10.0 , учитывая, что gtk.gtk_version (2,22,0) ?

Отвечая на мой собственный вопрос здесь, но если кто-нибудь знает лучше, не стесняйтесь отвечать тоже. Некоторые из них кажутся довольно хрупкими (например, номера версий в дорожках), поэтому комментируйте или редактируйте, если вы знаете лучший способ.

1. Поиск файлов

Во-первых, я использую этот код, чтобы найти корень среды выполнения GTK. Это очень специфично для того, как вы устанавливаете среду выполнения, и, вероятно, может быть улучшено с помощью нескольких проверок для общих мест:

 #gtk file inclusion import gtk # The runtime dir is in the same directory as the module: GTK_RUNTIME_DIR = os.path.join( os.path.split(os.path.dirname(gtk.__file__))[0], "runtime") assert os.path.exists(GTK_RUNTIME_DIR), "Cannot find GTK runtime data" 

2. Какие файлы включать

Это зависит от (a) от размера беспокойства и (б) контекста развертывания вашего приложения. Под этим я подразумеваю, развертываете ли вы его во всем мире, где любой может иметь произвольную локальную настройку, или это просто для внутреннего корпоративного использования, когда вам не нужны переводимые строки?

Если вам нужна тематика Windows, вам необходимо включить:

 GTK_THEME_DEFAULT = os.path.join("share", "themes", "Default") GTK_THEME_WINDOWS = os.path.join("share", "themes", "MS-Windows") GTK_GTKRC_DIR = os.path.join("etc", "gtk-2.0") GTK_GTKRC = "gtkrc" GTK_WIMP_DIR = os.path.join("lib", "gtk-2.0", "2.10.0", "engines") GTK_WIMP_DLL = "libwimp.dll" 

Если вы хотите значки танго:

 GTK_ICONS = os.path.join("share", "icons") 

Существуют также данные локализации (которые я пропускаю, но вы, возможно, не захотите):

 GTK_LOCALE_DATA = os.path.join("share", "locale") 

3. Собирать его вместе

Во-первых, вот функция, которая обрабатывает дерево файловой системы в заданной точке и создает выходные данные, подходящие для параметра data_files .

 def generate_data_files(prefix, tree, file_filter=None): """ Walk the filesystem starting at "prefix" + "tree", producing a list of files suitable for the data_files option to setup(). The prefix will be omitted from the path given to setup(). For example, if you have C:\Python26\Lib\site-packages\gtk-2.0\runtime\etc\... ...and you want your "dist\" dir to contain "etc\..." as a subdirectory, invoke the function as generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0\runtime", r"etc") If, instead, you want it to contain "runtime\etc\..." use: generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0", r"runtime\etc") Empty directories are omitted. file_filter(root, fl) is an optional function called with a containing directory and filename of each file. If it returns False, the file is omitted from the results. """ data_files = [] for root, dirs, files in os.walk(os.path.join(prefix, tree)): to_dir = os.path.relpath(root, prefix) if file_filter is not None: file_iter = (fl for fl in files if file_filter(root, fl)) else: file_iter = files data_files.append((to_dir, [os.path.join(root, fl) for fl in file_iter])) non_empties = [(to, fro) for (to, fro) in data_files if fro] return non_empties и def generate_data_files(prefix, tree, file_filter=None): """ Walk the filesystem starting at "prefix" + "tree", producing a list of files suitable for the data_files option to setup(). The prefix will be omitted from the path given to setup(). For example, if you have C:\Python26\Lib\site-packages\gtk-2.0\runtime\etc\... ...and you want your "dist\" dir to contain "etc\..." as a subdirectory, invoke the function as generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0\runtime", r"etc") If, instead, you want it to contain "runtime\etc\..." use: generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0", r"runtime\etc") Empty directories are omitted. file_filter(root, fl) is an optional function called with a containing directory and filename of each file. If it returns False, the file is omitted from the results. """ data_files = [] for root, dirs, files in os.walk(os.path.join(prefix, tree)): to_dir = os.path.relpath(root, prefix) if file_filter is not None: file_iter = (fl for fl in files if file_filter(root, fl)) else: file_iter = files data_files.append((to_dir, [os.path.join(root, fl) for fl in file_iter])) non_empties = [(to, fro) for (to, fro) in data_files if fro] return non_empties и def generate_data_files(prefix, tree, file_filter=None): """ Walk the filesystem starting at "prefix" + "tree", producing a list of files suitable for the data_files option to setup(). The prefix will be omitted from the path given to setup(). For example, if you have C:\Python26\Lib\site-packages\gtk-2.0\runtime\etc\... ...and you want your "dist\" dir to contain "etc\..." as a subdirectory, invoke the function as generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0\runtime", r"etc") If, instead, you want it to contain "runtime\etc\..." use: generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0", r"runtime\etc") Empty directories are omitted. file_filter(root, fl) is an optional function called with a containing directory and filename of each file. If it returns False, the file is omitted from the results. """ data_files = [] for root, dirs, files in os.walk(os.path.join(prefix, tree)): to_dir = os.path.relpath(root, prefix) if file_filter is not None: file_iter = (fl for fl in files if file_filter(root, fl)) else: file_iter = files data_files.append((to_dir, [os.path.join(root, fl) for fl in file_iter])) non_empties = [(to, fro) for (to, fro) in data_files if fro] return non_empties во def generate_data_files(prefix, tree, file_filter=None): """ Walk the filesystem starting at "prefix" + "tree", producing a list of files suitable for the data_files option to setup(). The prefix will be omitted from the path given to setup(). For example, if you have C:\Python26\Lib\site-packages\gtk-2.0\runtime\etc\... ...and you want your "dist\" dir to contain "etc\..." as a subdirectory, invoke the function as generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0\runtime", r"etc") If, instead, you want it to contain "runtime\etc\..." use: generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0", r"runtime\etc") Empty directories are omitted. file_filter(root, fl) is an optional function called with a containing directory and filename of each file. If it returns False, the file is omitted from the results. """ data_files = [] for root, dirs, files in os.walk(os.path.join(prefix, tree)): to_dir = os.path.relpath(root, prefix) if file_filter is not None: file_iter = (fl for fl in files if file_filter(root, fl)) else: file_iter = files data_files.append((to_dir, [os.path.join(root, fl) for fl in file_iter])) non_empties = [(to, fro) for (to, fro) in data_files if fro] return non_empties и def generate_data_files(prefix, tree, file_filter=None): """ Walk the filesystem starting at "prefix" + "tree", producing a list of files suitable for the data_files option to setup(). The prefix will be omitted from the path given to setup(). For example, if you have C:\Python26\Lib\site-packages\gtk-2.0\runtime\etc\... ...and you want your "dist\" dir to contain "etc\..." as a subdirectory, invoke the function as generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0\runtime", r"etc") If, instead, you want it to contain "runtime\etc\..." use: generate_data_files( r"C:\Python26\Lib\site-packages\gtk-2.0", r"runtime\etc") Empty directories are omitted. file_filter(root, fl) is an optional function called with a containing directory and filename of each file. If it returns False, the file is omitted from the results. """ data_files = [] for root, dirs, files in os.walk(os.path.join(prefix, tree)): to_dir = os.path.relpath(root, prefix) if file_filter is not None: file_iter = (fl for fl in files if file_filter(root, fl)) else: file_iter = files data_files.append((to_dir, [os.path.join(root, fl) for fl in file_iter])) non_empties = [(to, fro) for (to, fro) in data_files if fro] return non_empties 

Итак, теперь вы можете вызвать setup() следующим образом:

 setup( # Other setup args here... data_files = ( # Use the function above... generate_data_files(GTK_RUNTIME_DIR, GTK_THEME_DEFAULT) + generate_data_files(GTK_RUNTIME_DIR, GTK_THEME_WINDOWS) + generate_data_files(GTK_RUNTIME_DIR, GTK_ICONS) + # ...or include single files manually [ (GTK_GTKRC_DIR, [ os.path.join(GTK_RUNTIME_DIR, GTK_GTKRC_DIR, GTK_GTKRC) ]), (GTK_WIMP_DIR, [ os.path.join( GTK_RUNTIME_DIR, GTK_WIMP_DIR, GTK_WIMP_DLL) ]) ] ) )