Установка элемента меню меню приложения Mac OSX, отличного от «Python», в моем приложении Python Qt - PullRequest
15 голосов
/ 20 октября 2011

Я пишу приложение с графическим интерфейсом, используя 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?РЕДАКТИРОВАТЬ: Я бы предпочел продолжать использовать системный питон (или любой другой питон на пользовательском PATH), если это возможно.

Ответы [ 6 ]

8 голосов
/ 24 октября 2011

Похоже, вам нужен OSX .app для этого, так как файл Info.plist содержит видимое пользователем имя для приложения, которое там находится.По умолчанию это Python, название которого вы видите в меню программы. В этом блоге публикуется описание шагов, которые необходимо предпринять, в то время как OSX Developer Library содержит документы в списке свойств, которые необходимо заполнить.

5 голосов
/ 24 октября 2011

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

Я могу получить меню приложениябыть «MyApp» следующим образом:

ln -s /System/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python MyApp

./MyApp MyApp.py

Для работы этого необходимо два элемента:

  1. Символическая ссылка должна называться «MyApp» (или как угоднохотите появиться в меню приложения)
  2. Символическая ссылка должна указывать на исполняемый файл Python внутри пакета приложения Python системы.Например, он не работает, если вы ссылаетесь на / usr / bin / python.

Должен быть хитрый способ создания пакета приложения или сценария оболочки, который надежно использует этот механизм...

4 голосов
/ 08 декабря 2015

Если вы собираетесь распространять приложение, то символическая ссылка на двоичный файл python не гарантированно работает, учитывая, что обычно на машинах разработки системный python не является python по умолчанию, и обычным пользователям, скорее всего, не будут установлены Qt и PyQt..

Более надежный подход состоит в том, чтобы иметь собственный загрузочный двоичный файл OSX, который позаботится о запуске вашего приложения PyQt.И py2app , и PyInstaller могут создавать собственные оболочки.В то время как py2app хорошо работал для создания приложения с псевдонимом , то есть обертки, которая символизирует ссылки на существующие файлы в вашей системе, заставить его связывать только требуемые зависимости PyQt оказалось нетривиальным.Более того, «псевдоним приложения», сгенерированный py2app с флагом --aliased, перестанет работать, если вы переместите его в другую папку, поскольку символические ссылки относятся к папке, в которой вы изначально запускали скрипт сборки.

PyInstaller

PyInstaller работал "из коробки", и я получил пакет OSX, включающий зависимости всего около 16 МБ.

Пакет aСценарий Python для автономного приложения OSX:

pyinstaller -w --noconfirm -i=myappicon.icns --clean -F myscript.py

Это создает автономный пакет, который будет отображать все, что у вас есть в .setWindowTitle() в качестве имени приложения в строке заголовка OSX.Переключатель -w важен, поскольку он создает пакет приложений MacOS с правильным файлом .plist.

Примечание: по какой-то причине версия pyinstaller, установленная через pip, не работала длямне.Поэтому я удалил исходную версию пипса (pip uninstall pyinstaller) и установил последнюю ветку develop из github с:

pip install -v -e git+https://github.com/pyinstaller/pyinstaller.git@develop#egg=pyinstaller-github

После этого он работал как чудо.

Mac Automator

Еще одним вариантом создания оболочки приложения является использование Automator (введите имя в Spotlight), выберите «Файл»> «Создать»> «Приложение»> найти и перетащите Запустите сценарий оболочки в редакторе и в разделе Shell выберите bash, python.Вы также можете использовать Запустить Applescript и загрузить приложение с помощью сценария Apple - это может быть полезно, если вам, например, нужно запросить пароль пользователя для запуска с повышенными привилегиями.

Лучшей альтернативой Automator является Platypus - бесплатное приложение с открытым исходным кодом, которое оборачивает различные типы сценариев в приложения OSX, а также предоставляет некоторые дополнительные функции, такие как окна вывода текста графического интерфейса пользователя, запуск с правами администратора, настройка пользовательскихзначки и т. д. Код доступен на github .

. Также есть возможность создать в Xcode пустое OSX-приложение, которое запустит ваш скрипт 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, например, с помощью Утконос или Automator .

1 голос
/ 02 июля 2013

Я бы предпочел, чтобы конечные пользователи имели доступ к моему коду 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" в меню. Для этого он пытается найти пакетную версию 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)
0 голосов
/ 28 сентября 2016

Я знаю, что это не то, что ОП хочет точно, но подумал, что это может добавить некоторую ценность поиску решения этой проблемы.

Вы можете сделать так, чтобы файловые меню появлялись в вашем пользовательском интерфейсе окна вместов системной строке меню Mac OS X:

menubar.setNativeMenuBar(False)

Это приведет к тому, что строка меню будет выглядеть так, как она выглядит, например, в Windows.

0 голосов
/ 24 октября 2011

Я думаю, вам нужно сделать это:

win.setWindowTitle("MyApp")

Редактировать: это для PyQt. Я не знаю эквивалента в PySide.

...