2011-10-19 28 views
14

Tôi đang viết một ứng dụng GUI sử dụng python và Qt. Khi tôi khởi chạy ứng dụng của mình trên Mac, mục menu đầu tiên trong thanh trình đơn Mac ở đầu màn hình là "Python". Tôi muốn tên ứng dụng ở đó là tên của ứng dụng của tôi. Tôi có thể lấy tên chương trình của mình ở đó bằng cách nào?Cài đặt mục menu ứng dụng Mac OSX Menu thành khác với "Python" trong ứng dụng Qt trăn của tôi

Chương trình demo sau tạo cửa sổ có hai menu: "Python" và "Foo". Tôi không thích điều đó, bởi vì nó không tạo sự khác biệt cho người dùng của tôi cho dù tôi đã viết ứng dụng bằng python hay COBOL. Thay vào đó, tôi muốn các menu "MyApp" và "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_()) 

Làm cách nào để thay đổi tên menu ứng dụng trên máy Mac? EDIT: Tôi muốn tiếp tục sử dụng hệ thống python (hoặc bất kỳ python là trên người sử dụng PATH) nếu có thể.

+0

Bạn bắt đầu chương trình của mình như thế nào?Tôi biết rất ít về mac, nhưng có vẻ như nó chỉ đặt giá trị của sys.argv [0] ở đó. – Falmarri

+0

Tôi bắt đầu chương trình bằng cách nhập "python MyApp.py" hoặc "./MyApp.py". Trong cả hai trường hợp, sys.argv [0] là "MyApp.py" hoặc "./MyApp.py". "Python" không phải là một phần của argv. –

+0

@Christpher Bruns: Hmm, bạn nói đúng. Tôi không biết tại sao tôi nghĩ python của python đã cho python như là 'argv [0]'. Tôi biết bên cạnh không có gì về mac mặc dù, vì vậy tôi tất cả ra khỏi ý tưởng. – Falmarri

Trả lời

8

Bạn có vẻ cần một OSX .app để làm việc này, vì tệp Info.plist trong đó chứa tên người dùng hiển thị cho ứng dụng được đặt ở đó. Điều này mặc định là Python, là tiêu đề bạn thấy cho menu chương trình. This blog bài viết phác thảo các bước bạn cần thực hiện, trong khi OSX Developer Library có tài liệu trên danh sách thuộc tính bạn cần phải điền.

+0

Tôi sẵn sàng tạo Gói ứng dụng nếu cần. Nhưng tôi khá không hài lòng với "bạn không thể sử dụng hệ thống python" kết luận của bài đăng trên blog đó. Có thực sự không có cách nào để sử dụng python hệ thống và không có mục trình đơn "Python"? –

+0

Rõ ràng ... để trích dẫn [một nguồn khác] (http://arstechnica.com/open-source/guides/2009/03/how-to-deploying-pyqt-applications-on-windows-and-mac-os -x.ars/2): _ "Bạn hoàn toàn bắt buộc phải cấu hình môi trường để sử dụng đúng phiên bản Python trước khi bạn xây dựng các phụ thuộc dựa trên Python." _ Từ những gì tôi có thể nhớ lại từ một bài tập cách đây một thời gian, theo cách này hoạt động chắc chắn và khi chúng tôi đã triển khai tự động, nó không thực sự là vấn đề nữa. Tôi không biết cách này, nên ... tôi đoán không có cách nào khác, không. – jro

+0

Tôi tìm thấy một cách. Xem câu trả lời của tôi. Tôi khuyến khích bạn bắt đầu một câu trả lời thứ hai nếu bạn nghĩ rằng bạn có thể tận dụng nó thành một giải pháp hoàn chỉnh hơn. –

-3

Tôi nghĩ rằng bạn cần phải làm điều này:

win.setWindowTitle("MyApp") 

Chỉnh sửa: Đây là cho PyQt. Tôi không biết tương đương trong PySide.

+2

setWindowTitle() đặt văn bản trong thanh tiêu đề ứng dụng, không nằm trong thanh trình đơn. Tôi cần phải thay đổi mục Menu ứng dụng trong thanh trình đơn Mac. setWindowTitle() không làm điều đó. –

5

Tôi đã tìm thấy hạt nhân của câu trả lời cho câu hỏi này. Bởi vì tôi muốn trao thưởng tiền thưởng cho một người khác ngoài bản thân tôi (tôi là OP), xin vui lòng, bất cứ ai, lấy hạt nhân này và xây dựng nó thành một câu trả lời hoàn chỉnh hơn của riêng bạn.

Tôi có thể lấy menu ứng dụng là "MyApp" như sau:

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

./MyApp MyApp.py 

Có hai yếu tố cần thiết để có được điều này để làm việc:

  1. Các liên kết tượng trưng phải được đặt tên là "MyApp "(hoặc bất kỳ thứ gì bạn muốn xuất hiện trong Menu Ứng dụng)
  2. Liên kết tượng trưng phải trỏ đến tệp thực thi Python bên trong gói ứng dụng python hệ thống. Nó không hoạt động nếu bạn liên kết đến/usr/bin/python, ví dụ.

Có phải là một cách thông minh để tạo ra một gói ứng dụng hoặc shell script mà khai thác cơ chế này một cách mạnh mẽ ...

+0

Nếu tôi hiểu chính xác, bạn chỉ cần thay đổi tên của tệp thực thi, vì vậy để nói (tôi hiểu các liên kết tượng trưng, ​​nhưng phải rõ ràng)? Mặc dù tôi vẫn ủng hộ việc tạo Gói ứng dụng và đặt nỗ lực tự động vào đó, bạn có thể chỉ cần tạo trình khởi chạy cho ứng dụng của mình lần đầu tiên tạo liên kết tượng trưng, ​​khởi chạy ứng dụng của bạn và sau khi đóng ứng dụng đó, xóa liên kết. Nhưng như đã nói, tôi ủng hộ việc tạo ra một gói ứng dụng phù hợp ... để ai đó có thể tự do biên dịch câu trả lời này thành câu trả lời của riêng họ. – jro

+0

@jro Tôi đồng ý rằng một gói ứng dụng có thể sẽ được yêu cầu thực hiện một cách thanh lịch chiến thuật liên kết tượng trưng. Tôi đang tìm một giải pháp cho thấy cách tạo và sử dụng liên kết tượng trưng trong một gói ứng dụng. Nếu không có câu trả lời cụ thể nào xuất hiện, câu trả lời "sử dụng gói ứng dụng" của bạn có thể trở thành tốt nhất. –

1

Xây dựng về câu trả lời Christopher Bruns, cũng như script from "How to create Mac application bundle for Python script via Python", đây là một Kịch bản Python tạo một gói (ứng dụng) cho một tập lệnh Python của người dùng, sẽ hiển thị tên ứng dụng thay vì "Python" trong các menu. Để làm điều này, nó cố gắng định vị một phiên bản Python kèm theo, và các liên kết tượng trưng đến đó với tên của ứng dụng. Tôi đã thử nghiệm nó với một kịch bản wxpython, nhưng nó cũng làm việc cho Qt.

Tập lệnh người dùng được chạy từ vị trí ban đầu của nó thay vì đặt nó trong ứng dụng. Nếu bạn muốn đặt (các) tập lệnh của mình vào một gói (cùng với python) để phân phối lại, hãy xem py2app thay thế.

#!/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) 
2

Nếu bạn có ý định phân phối ứng dụng, sau đó symlinking nhị phân python không được bảo đảm để làm việc, xem xét rằng thường trên máy phát triển hệ thống trăn không phải là trăn mặc định, và người sử dụng thường xuyên rất có thể sẽ không có Qt và PyQt được cài đặt.

Một cách tiếp cận đáng tin cậy hơn là có nhị phân khởi động OSX gốc có thể bắt đầu ứng dụng PyQt của bạn. Cả hai py2appPyInstaller đều có thể tạo trình bao bọc gốc. Trong khi py2app hoạt động tốt để tạo một ứng dụng bí danh, nghĩa là một trình bao bọc mà các liên kết tượng trưng đến các tệp hiện có trên hệ thống của bạn, làm cho nó chỉ gói các phụ thuộc PyQt bắt buộc được chứng minh là không tầm thường. Hơn nữa, 'ứng dụng bí danh' được tạo bởi py2app với cờ --aliased sẽ ngừng hoạt động nếu bạn di chuyển nó sang thư mục khác vì các liên kết tượng trưng có liên quan đến thư mục nơi bạn chạy kịch bản xây dựng ban đầu.

PyInstaller hoạt động ngoài hộp và tôi nhận được gói OSX bao gồm tất cả các phụ thuộc và mã PyQt tất cả khoảng 16MB.

tôi đã sử dụng lệnh này để chuyển đổi kịch bản PyQt của tôi đến một ứng dụng OSX độc lập:

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

này tạo ra một gói độc lập sẽ hiển thị bất cứ điều gì bạn có trong .setWindowTitle() của bạn như tên ứng dụng trên thanh tiêu đề OSX.

NB: Vì một số lý do, phiên bản pyinstaller được cài đặt qua pip không hoạt động đối với tôi. Vì vậy, tôi gỡ bỏ phiên bản pip, nhân bản git version of pyinstaller, đã làm một python setup.py install và sau đó nó làm việc như một say mê.

Tuy nhiên, một tùy chọn để tạo một wrapper ứng dụng là sử dụng Automator (gõ tên trong Spotlight), vào File> New> Ứng dụng> tìm kiếm và kéo Chạy kịch bản shell vào trình soạn thảo và dưới Shell chọn bash, python. Bạn cũng có thể sử dụng Chạy Applescript và khởi động ứng dụng của mình bằng tập lệnh táo - có thể hữu ích nếu bạn cần, ví dụ: để hỏi mật khẩu của người dùng để chạy với các đặc quyền nâng cao. Một thay thế tốt hơn cho Automator là Platypus - một ứng dụng mã nguồn mở miễn phí bao gồm nhiều loại tập lệnh vào ứng dụng OSX và cũng cung cấp một số tính năng bổ sung như cửa sổ xuất văn bản gui, chạy với đặc quyền quản trị, đặt biểu tượng tùy chỉnh, vv Mã có sẵn trên github.

Và cuối cùng, có tùy chọn tạo ứng dụng OSX barebones trong Xcode sẽ khởi chạy tập lệnh PyQt của bạn (một số ví dụ here) và thực hiện các tác vụ tùy chỉnh khác theo yêu cầu của ứng dụng của bạn.

0

Tôi biết đây không phải là những gì OP muốn chính xác, nhưng đã tìm ra nó có thể thêm một số giá trị để tìm kiếm giải pháp cho vấn đề này.

Bạn có thể tạo các menu tập tin xuất hiện trong giao diện người dùng cửa sổ của bạn thay vì trong bản Mac OS X hệ thống thanh menu:

menubar.setNativeMenuBar(False) 

này sẽ làm cho thanh menu xuất hiện vì nó sẽ xuất hiện trong ví dụ Các cửa sổ.

Các vấn đề liên quan