Настройка пункта меню меню меню приложения Mac OSX, отличного от "Python" в моем приложении Qt python
Я пишу приложение GUI с использованием python и Qt. Когда я запускаю свое приложение на Mac, первым пунктом меню в строке меню Mac в верхней части экрана является "Python". Я бы предпочел, чтобы имя приложения было именем моего приложения. Как я могу получить там свое имя программы?
Следующая демо-программа создает окно с двумя меню: "Python" и "Foo". Мне это не нравится, потому что для моих пользователей не имеет значения, писал ли я приложение в python или COBOL. Вместо этого я хочу меню "MyApp" и "Foo".
#!/usr/bin/python
# This example demonstrates unwanted "Python"
# application menu name on Mac.
# Makes no difference whether we use PySide or PyQt4
from PySide.QtGui import *
# from PyQt4.QtGui import *
import sys
app = QApplication(sys.argv)
# Mac menubar application menu is always "Python".
# I want "DesiredAppTitle" instead.
# setApplicationName() does not affect Mac menu bar.
app.setApplicationName("DesiredAppTitle")
win = QMainWindow()
# need None parent for menubar on Mac to get custom menus at all
mbar = QMenuBar()
# Add a custom menu to menubar.
fooMenu = QMenu(mbar)
fooMenu.setTitle("Foo")
mbar.addAction(fooMenu.menuAction())
win.setMenuBar(mbar)
win.show()
sys.exit(app.exec_())
Как я могу изменить имя этого меню приложения на Mac? EDIT: Я бы предпочел продолжать использовать системный python (или любой другой python на PATH пользователя), если это возможно.
Ответы
Ответ 1
Кажется, вам нужен OSX.app, чтобы это работало, так как файл Info.plist
там содержит имя пользователя для приложения, которое там помещено. По умолчанию используется Python, который является названием, которое вы видите для меню программы. В этом блоге сообщается о шагах, которые вам нужно предпринять, а Библиотека разработчиков OSX содержит документы об этом свойстве который вам нужно заполнить.
Ответ 2
Я нашел ядро ответа на этот вопрос. Поскольку я хочу присудить награду кому-то, кроме меня (я - ОП), пожалуйста, кто угодно, возьмите это ядро и переработайте его в более полный ответ.
Я могу заставить меню приложения быть "MyApp" следующим образом:
ln -s /System/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python MyApp
./MyApp MyApp.py
Для этого нужно два элемента:
- Символьная ссылка должна иметь имя "MyApp" (или все, что вы хотите отобразить в меню приложения)
- Символьная ссылка должна указывать на исполняемый файл Python внутри пакета приложений python системы. Это не работает, если вы ссылаетесь на /usr/bin/python, например.
Должен быть умный способ создания пакета приложений или оболочки script, которая использует этот механизм надежным способом...
Ответ 3
Если вы намерены распространять приложение, то символическая ссылка на двоичный файл python не гарантированно будет работать, учитывая, что обычно на машинах разработки системный python не является python по умолчанию, и обычным пользователям, скорее всего, не будут установлены Qt и PyQt.
Более надежный подход состоит в том, чтобы иметь собственный загрузочный двоичный файл OSX, который позаботится о запуске вашего приложения PyQt. Как py2app, так и PyInstaller могут генерировать нативные оболочки. Хотя py2app хорошо работал для создания приложения с псевдонимами, то есть обертки, которая символически ссылается на существующие файлы в вашей системе, заставить его связывать только требуемые зависимости PyQt оказалось нетривиальным. Более того, "псевдоним приложения", сгенерированный py2app с флагом --aliased
, перестанет работать, если вы переместите его в другую папку, так как символические ссылки относятся к папке, в которой вы изначально запускали скрипт сборки.
PyInstaller
PyInstaller работал "из коробки", и я получил пакет OSX, который включал зависимости размером около 16 МБ.
Скомпилируйте скрипт на python в отдельное приложение OSX:
pyinstaller -w --noconfirm -i=myappicon.icns --clean -F myscript.py
Это создает автономный пакет, который будет отображать все, что у вас есть в вашем .setWindowTitle()
качестве имени приложения в строке заголовка OSX. Переключатель -w
важен, поскольку он создает пакет приложений MacOS с правильным файлом .plist
.
NB: почему-то версия pyinstaller
установленная через pip
, у меня не работала. Поэтому я удалил исходную версию pip (pip uninstall pyinstaller
) и установил последнюю ветвь develop
из github с:
pip install -v -e git+https://github.com/pyinstaller/[email protected]#egg=pyinstaller-github
После этого он работал как шарм.
Mac Automator
Еще одним вариантом создания оболочки приложения является использование Automator (введите имя в Spotlight), перейдите в "Файл"> "Создать"> "Поиск приложения" и перетащите сценарий "Выполнить оболочку" в редактор и в разделе "Shell" выберите bash
, python
. Вы также можете использовать "Запустить Applescript" и запустить приложение с помощью Apple-скрипта - это может быть полезно, например, если вам нужно запросить пароль пользователя для запуска с повышенными привилегиями.
Лучшей альтернативой Automator является Platypus - бесплатное приложение с открытым исходным кодом, которое включает различные типы сценариев в приложения OSX, а также предоставляет некоторые дополнительные функции, такие как окна вывода текста графического интерфейса пользователя, запуск с правами администратора, настройка пользовательских значков и т.д. Код доступен на github.
Существует также возможность создать базовое OSX-приложение в XCode, которое запустит ваш скрипт PyQt (некоторые примеры здесь) и выполнит другие пользовательские задачи, необходимые для вашего приложения.
ПРИМЕЧАНИЕ. Имейте в виду, что пакет приложения, который вызывает ваш код Python с предопределенным интерпретатором, является "мелким" пакетом приложения и будет зависеть от любых зависимостей Python, которые вы установили локально. Скорее всего не будет работать OOTB на чужом Mac.
Nuitka
Возможно, вы также захотите попробовать Nuitka (pip3 install nuitka
), который призван стать проектом для создания действительно нативных исполняемых файлов путем преобразования кода Python в C++ и последующей его компиляции.
Пример построения нативного приложения с nuitka
:
nuitka3 --follow-imports --python-flag=no_site --verbose --standalone --show-progress --show-modules --output-dir=my_build_dir myscript.py
Это создаст исполняемый файл Mac с библиотеками в папке my_build_dir
. Вам нужно будет обернуть их самостоятельно в пакет приложений MacOS, например, с помощью Platypus или с Automator.
Ответ 4
Я бы предпочел, чтобы конечные пользователи имели доступ к моему коду Python, чтобы они могли видеть и, возможно, улучшать код, поэтому меня не интересуют варианты, которые скрывают код. Я обнаружил, что могу заставить wx-приложения (еще не пробовавшиеся с Qt) запускать с указанным именем приложения, используя разные стандартные интерпретаторы Python, оборачивая их в пакет по мере их установки (например, как часть процесса установки пакета conda).). Вот краткий скрипт, который создает пакет .app с указанным именем, используя Python, выполняющий скрипт. Запуск сценария с использованием созданной программной ссылки (см. PyAlias ниже), как в
./MyApplication.app/Contents/MacOS/MyApplication /path/MyApplication.py
делает работу
from __future__ import division, print_function
import os,sys
appName = 'MyApplication' # name of app
scriptdir = '.'
iconfile = os.path.join(scriptdir,'MyApplication.icns') # optional icon file
if __name__ == '__main__':
wrapApp = os.path.join(scriptdir,appName+'.app','Contents')
os.makedirs(wrapApp)
wrapPy = os.path.join(wrapApp,'MacOS')
os.makedirs(wrapPy)
fp = open(os.path.join(wrapApp,'PkgInfo'),'w')
fp.write('APPL????\n')
fp.close()
fp = open(os.path.join(wrapApp,'Info.plist'),'w')
fp.write('''<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">\n<plist version="0.9">\n<dict>\n <key>CFBundleIconFile</key>\n <string></string>\n <key>CFBundlePackageType</key>\n <string>APPL</string>\n <key>CFBundleGetInfoString</key>\n <string>Created in makeApp.py (Brian Toby/QMake</string>\n <key>CFBundleSignature</key>\n <string>????</string>\n <key>CFBundleExecutable</key>\n <string>{:}</string>\n <key>CFBundleIdentifier</key>\n <string>com.continuum.python</string>\n <key>NSPrincipalClass</key>\n <string>NSApplication</string>\n</dict>\n</plist>\n'''.format(appName))
fp.close()
pyAlias = os.path.join(wrapPy,appName)
pythonpath = os.path.realpath(sys.executable)
os.symlink(pythonpath,pyAlias)
if os.path.exists(iconfile):
shutil.copyfile(iconfile,oldicon)
print('Use',pyAlias,'to run wxPython scripts')
Ниже приведен более старый ответ, который я оставляю для любой исторической ценности. В моем приложении теперь я использую гибрид между ними, где я создаю перетаскиваемый AppleScript-пакет (как показано ниже), где я размещаю мягкую ссылку на Python (названную так, чтобы соответствовать моему приложению), как указано выше. Основываясь на ответе Кристофера Брунса, а также на сценарии из "Как создать пакет приложений Mac для сценария Python через Python", приведен сценарий Python, который создает пакет (приложение) для пользовательского сценария Python, в котором будет отображаться имя приложения. а не "питон" в меню. Для этого он пытается найти пакетную версию Python и ссылается на нее с именем приложения. Я протестировал его с помощью скрипта wxpython, но он должен работать и для Qt.
Пользовательский скрипт запускается из своего исходного местоположения, а не помещает его в приложение. Если вы хотите поместить свои сценарии в пакет (вместе с python) для распространения, см. Вместо этого py2app.
#!/usr/bin/env python
'''This creates an app to launch a python script. The app is
created in the directory where python is called. A version of Python
is created via a softlink, named to match the app, which means that
the name of the app rather than Python shows up as the name in the
menu bar, etc, but this requires locating an app version of Python
(expected name .../Resources/Python.app/Contents/MacOS/Python in
directory tree of calling python interpreter).
Run this script with one or two arguments:
<python script>
<project name>
The script path may be specified relative to the current path or given
an absolute path, but will be accessed via an absolute path. If the
project name is not specified, it will be taken from the root name of
the script.
'''
import sys, os, os.path, stat
def Usage():
print("\n\tUsage: python "+sys.argv[0]+" <python script> [<project name>]\n")
sys.exit()
version = "1.0.0"
bundleIdentifier = "org.test.test"
if not 2 <= len(sys.argv) <= 3:
Usage()
script = os.path.abspath(sys.argv[1])
if not os.path.exists(script):
print("\nFile "+script+" not found")
Usage()
if os.path.splitext(script)[1].lower() != '.py':
print("\nScript "+script+" does not have extension .py")
Usage()
if len(sys.argv) == 3:
project = sys.argv[2]
else:
project = os.path.splitext(os.path.split(script)[1])[0]
# find the python application; must be an OS X app
pythonpath,top = os.path.split(os.path.realpath(sys.executable))
while top:
if 'Resources' in pythonpath:
pass
elif os.path.exists(os.path.join(pythonpath,'Resources')):
break
pythonpath,top = os.path.split(pythonpath)
else:
print("\nSorry, failed to find a Resources directory associated with "+str(sys.executable))
sys.exit()
pythonapp = os.path.join(pythonpath,'Resources','Python.app','Contents','MacOS','Python')
if not os.path.exists(pythonapp):
print("\nSorry, failed to find a Python app in "+str(pythonapp))
sys.exit()
apppath = os.path.abspath(os.path.join('.',project+".app"))
newpython = os.path.join(apppath,"Contents","MacOS",project)
projectversion = project + " " + version
if os.path.exists(apppath):
print("\nSorry, an app named "+project+" exists in this location ("+str(apppath)+")")
sys.exit()
os.makedirs(os.path.join(apppath,"Contents","MacOS"))
f = open(os.path.join(apppath,"Contents","Info.plist"), "w")
f.write('''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>main.sh</string>
<key>CFBundleGetInfoString</key>
<string>{:}</string>
<key>CFBundleIconFile</key>
<string>app.icns</string>
<key>CFBundleIdentifier</key>
<string>{:}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>{:}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>{:}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>{:}</string>
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
'''.format(projectversion, bundleIdentifier, project, projectversion, version)
)
f.close()
# not sure what this file does
f = open(os.path.join(apppath,'Contents','PkgInfo'), "w")
f.write("APPL????")
f.close()
# create a link to the python app, but named to match the project
os.symlink(pythonapp,newpython)
# create a script that launches python with the requested app
shell = os.path.join(apppath,"Contents","MacOS","main.sh")
# create a short shell script
f = open(shell, "w")
f.write('#!/bin/sh\nexec "'+newpython+'" "'+script+'"\n')
f.close()
os.chmod(shell, os.stat(shell).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
Ответ 5
Я знаю, что это не то, что OP хочет точно, но полагал, что это может добавить некоторое значение для поиска решения этой проблемы.
Вы можете сделать меню файлов отображаемым в пользовательском интерфейсе окна, а не в системном меню Mac OS X:
menubar.setNativeMenuBar(False)
Это приведет к тому, что панель меню появится, как она появится, например. Windows.
Ответ 6
Думаю, вам нужно это сделать:
win.setWindowTitle("MyApp")
Изменить: это для PyQt. Я не знаю эквивалента в PySide.