rcmdnk's blog

20151116_rumps_test_200_200

rumps というPythonのライブラリを使うと Macのメーニューバーアプリを簡単にPythonで作ることが出来ます。

rumps

rumpsはPythonで簡単にメニューバーアプリを作成出来るライブラリ。

基本的な使い方は適当な関数にclickedというデコレーターを 当てる事でMenuItemと言うクラスのオブジェクトを作成し、 それをAppというクラスのオブジェクトに登録してrunすると 該当のメニューを持ったメニューバーアプリが出来ます。

インストール

インストールはpipを使って

$ pip install rumps

で。

rumpsがインストールできたら、 rumpsのレポジトリ のexamplesにある物を取ってきて実行してみたりすると色々わかると思います。

Hello World

凄くシンプルなものであれば、 適当に

rumps_test.py
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
import rumps


@rumps.clicked("Hello World")
def hello_world(sender):
    rumps.alert("Hello World")

if __name__ == "__main__":
    app = rumps.App("Rumps Test", title=None, icon="icon.png",
                    menu=["Hello World"])
    app.run()

こんなスクリプトを作ってみます。 icon.pngというファイルを別途用意して同じディレクトリに置いておきます。 (16 x 16 pxの適当な画像。画像を用意するのが面倒ならiconの引数部分を消してください。)

@rumps.clicked("Hello World")というデコレーターを使っていますが、 ここで指定しているHello Worldの部分がMenuItemの名前になります。 関数はMenuItem型の引数を一つ持った物を作ります。

この様にしていつかメニューを用意して、 App型のオブジェクトにmenuを与えます。

Appの最初の引数はアプリの名前で何でも良し。 titleはメニューバー上のアイコンの右側に表示される文字列。 iconが表示されるアイコン画像になります。 どちらもデフォルト値はNoneですが、 両方共指定されない場合は最初に指定したアプリの名前が表示されます。

menuには使いたいメニューの名前の入った配列を渡します。

これで

$ python rumps_test.py

とすれば(もしくは実行権限を付与して./rumps_test.py)

20151116_rumps_test.jpg

こんな感じ(一番左)の物がメニューバーに表示され Hello Worldの所をクリックすれば

20151116_rumps_test_alert.jpg

こんな感じのWindowが出てきます。

alert/notification

hello_worldの中で使ってるalertという関数はこの様にWindowに情報を表示させます。 同様にnotificationという関数もあって、 こちらは通知センターに送られポップアップが表示されます。

alertの場合にはOKボタンの他にCancelボタンを表示せることも出来、 どちらを押したかを得られる様になってるので、 何か、実行しますか?、みたいな質問をして結果を見て処理することも可能です。

メニュータイトル

メニューの項目に表示されるのはメニュー名ですが、 この部分は

1
2
3
app = rumps.App("Rumps Test", title=None, icon="icon.png",
                menu=["Hello World"])
app.menu["Hello World"].title = "Different menu name"

の様に、各メニューに上のようにmenuを使ってアクセス出来るので、 それぞれのtitleを変更することで表示名を変える事も出来ます。

各メニューにはtitle以外にもiconの値もあり、 これに画像を指定することでメニューの左側に画像を表示させる (タイトルを省略すれば画像だけにする)ことも出来ます。

アプリタイトル、アイコン

同様にアプリ自体のタイトルも

1
2
app.title = "Different title"
app.icon = "different.png"

みたいにすれば実行中に動的に変更すること出来ます。

これを使うと、メニューバーに何らの情報を表示したり、 アイコンの色を変えて通知したりすることも出来ます。

ちょっと使ってて気づいたのが、 アイコンが表示されてる状態でアプリのタイトルを途中で変更すると、 幅の変更が行われないらしく、長いタイトルを指定すると右側が切れてしまいました。 この際、一度アイコンの方ももう一度指定してあげると上手く幅が調整されました。

なのでタイトルの変更を行う際にはアイコンを変更しなくても タイトルの後にアイコンを設定する必要があります。

日本語メニュー

メニューバー名やアプリ名等に直接日本語は使えませんでしたが、 この様にしてタイトルを日本語にすることは可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/env/bin python
# -*- coding: utf-8 -*-
import rumps


@rumps.clicked("Hello World")
def hello_world(sender):
    print type(sender)
    rumps.alert(u"こんにちは")

if __name__ == "__main__":
    app = rumps.App("Rumps Test", title=None, icon="icon.png",
                    menu=["Hello World"])
    app.menu["Hello World"].title = u"こんにちは"
    app.run()

こんな感じで。

勿論、alertなどにも日本語を使うことが出来ます。

トグルメニュー

メニューをクリックして状態をトグルしたいような場合には sender.stateの値を変更します。 最初は0になってますが、これを1にするとメニューの左側にチェックマークが付きます。

1
2
3
@rumps.clicked('Enable')
def toggle(sender):
    sender.state = not sender.state

の様なメニューを作っておくと状態を0, 1でトグルすることが出来、

20151116_rumps_test_toggle.jpg

こんな感じでクリックするたびにチェックマークが付いたり消えたりします。 これを使ってapp.menu["Enable"]の値を見て他の場所で色々と 処理すればOK。

入力させる

何らかの入力をさせたい場合にはWindowを使って

1
2
3
4
5
@rumps.clicked('Echo')
def set_labels(self, sender):
    response = rumps.Window('Put words', dimensions=(200, 20)).run()
    if response.clicked:
        rumps.alert(response.text)

こんな感じのメニューを作ると、

20151116_rumps_test_window.jpg

こんな感じの入力画面が出て来て、入力したものを 上の場合だとresponse.textで取ってくることが出来ます。 dimensionsは入力窓の大きさなので適当な大きさにします。

メニューツリー

MenuItemは関数を割り当てなくても作ることが出来ます。 また、MenuItemは、これ自体もMenuのリストを持っていて、 ここにMenuItemを加える事でツリー上のメニューを作ることが出来ます。

rumps_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
import rumps


def hello_mike():
    rumps.alert("Hello Mike")


def hello_jane():
    rumps.alert("Hello Jane")

if __name__ == "__main__":
    app = rumps.App("Rumps Test", title=None, icon="icon.png",
                    menu=["Hello"])
    app.menu["Hello"].add("Mike", callback=hello_mike)
    app.menu["Hello"].add("Jane", callback=hello_jane)
    app.run()

こんな感じ。

まず、Helloというメニューは定義されていないので 何もしないただタイトルだけのメニューが出来ます。

このままだと、このメニューは表示されますが、 薄いグレー表示でクリックしても何も起こらないものになります。

このメニューに対して、MikeJaneというMenuItemを与えています。 ここではaddという関数を使って加えています。

MenuItemはその場で、Mikeという名前のメニューで、 hello_mikeという関数をコールバックとして持ったものを作ったりして与えています。

20151116_rumps_test_tree.jpg

こんな感じになります。

Timer

メニューバーに表示させる様なアプリは、大概が何らかの事を バックグラウンドで実行してる物が多いと思います。

そのようなバックグラウンド処理を登録するには timerというデコレーターを使います。

1
2
3
@rumps.timer(1)
def hello_world_repeat(sender):
    print 'Hello World'

こんなのを書いておくと、 App.run()と同時に、timerの引数にある数字毎(秒)に実行されます。 (メニューの様に登録する必要はありません。)

勝手に始まってもらっては困る、と言う場合には,Timerというクラスの オブジェクトを自分で作ってスタートさせる事もできます。

1
2
3
4
5
6
7
def hello_world_repeat(sender):
    print 'Hello World'

my_timer = rumps.Timer(hello_world_repeat, 1)
my_timer.start()
...
my_timer.stop()

こんな感じ。 実行インターバルは途中でも

1
2
3
4
if my_timer.is_alive():
    my_timer.stop()
my_timer.interval = 10
my_timer.start()

の様にintervalの値を変更することで変更できます。

この際、タイマーが動いたままだと値が変更できないので、 上の様に一度止める必要があります。

このTimerを使って一定時間ごとに何かチェックを行って、 チェックにかかったらnotificationを出したり、 メニューバーのアプリタイトルやアイコンを変更して お知らせしたり、みたいな事が一番有り得そうな使い道だと思います。

アプリを終了する

デフォルトでQuitというメニューが追加され、 これを押すとアプリを終了します。

このメニュー名はquit_buttonという引数を Appの初期化時に与える事で変更が出来ます。 (デフォルト値はQuit。)

この値をNoneにすると、メニューから直接終了する手段がなくなります。 この場合は自分で終了させるメニューを作成するなりしないといけませんが、 終了させるには

1
rumps.quit_application()

を終了させたい所で呼ぶとAppオブジェクトのrunが終了します。

Appを継承したクラスを作って使う

ここまでの例ではグローバル関数を作り、デコレーターで装飾して Appで使う、と言う形でしたが、

rumps_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
import rumps


class RumpsTest(rumps.App):
    def __init__(self):
        super(RumpsTest, self).__init__("Rumps Test", title=None,
                                        icon="icon.png",
                                        menu=["Hello World"])
    @rumps.clicked("Hello World")
    def hello_world(self, sender):
        rumps.alert("Hello World")

if __name__ == "__main__":
    app = RumpsTest()
    app.run()

こんな感じ。 複雑な事をしようと思うとクラスを使ったほうが簡単に色々できたりすると思います。

アプリ化

py2app というライブラリを別途使うことで、 このPythonスクリプトを簡単にアプリ化することも出来ます。

py2appを入れる際、OS X10.11 El Capitanだとちょっと躓いたので、 それについては下記を参照。

インストールはpipで

$ pip install py2app

まずはpy2appletというコマンドを使ってセットアップファイルを作ります。

$ py2applet --make-setup rumps_test.py
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"""
This is a setup.py script generated by py2applet

Usage:
    python setup.py py2app
"""

from setuptools import setup

APP = ['rumps_test.py']
DATA_FILES = []
OPTIONS = {'argv_emulation': True}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

こんな感じのファイルが出来ます。

このファイルの中で、OPTIONSという項目を

1
2
3
4
5
6
7
OPTIONS = {
    'argv_emulation': True,
    'plist': {
        'LSUIElement': True,
    },
    'resources': ['icon.png'],
},

の様に変更します。

LSUIElementは、アプリのplistの中のこの値を1にすると、 アプリを立ち上げてもDockに表示されなくなる値です。 メニューバーアプリなのでDockに表示されないようにします。

resourcesはアプリの中に含む外部ファイルのリストで、 ここではメニューバーアイコンを入れています。

また、アプリのアイコンを作って、それをアプリのアイコンとして指定したい場合は iconfileという値を

'iconfile': 'app.icns',

な感じでOPTIONSに加えて上げると作ったアプリのアイコンとして使われます。

取り敢えず、これらの変更を加えて、

$ python setup.py py2app

を実行するとdistbuildというディレクトリができてると思いますが、 distの中にrumps_test.appというアプリが出来ています。

これをFinderで表示してダブルクリックで実行してみれば、 (もしくはopen -W ./dist/rumps_test.app 先ほどと同じようなメニューバーのアプリが立ち上がります。

El Capitanでpy2appが上手く動かない時

以前、Yosemiteでちょっと動かした時は何も問題なかったと思うんですが、 El Capitanになってから使おうと思ったら python setup.py py2appコマンドがエラーを出して上手くコンパイルできませんでした。

ちょっとメモが無くなってどの様なエラーだったか覚えてませんが、 pyobjec-core関連のエラーが出て(これ自体は既に入っていた状態)、 以下のページに辿り着いて、

ronaldoussoren / pyobjc / 課題 / #81 - pyobjc-core 2.5.1 install failing on OSX 10.9.2 — Bitbucket: https://bitbucket.org/ronaldoussoren/pyobjc/issues/81/pyobjc-core-251-install-failing-on-osx

以下のコマンドを実行してpyobjc関連の物を一通り入れたら 動くようになりました。

$ pip install pyobjc-core pyobjc-framework-Accounts pyobjc-framework-AddressBook pyobjc-framework-AppleScriptKit pyobjc-framework-AppleScriptObjC pyobjc-framework-Automator pyobjc-framework-CFNetwork pyobjc-framework-CalendarStore pyobjc-framework-Cocoa pyobjc-framework-Collaboration pyobjc-framework-CoreData pyobjc-framework-CoreLocation pyobjc-framework-CoreText pyobjc-framework-CoreWLAN pyobjc-framework-DictionaryServices pyobjc-framework-DiskArbitration pyobjc-framework-EventKit pyobjc-framework-ExceptionHandling pyobjc-framework-FSEvents pyobjc-framework-InputMethodKit pyobjc-framework-InstallerPlugins pyobjc-framework-InstantMessage pyobjc-framework-LatentSemanticMapping pyobjc-framework-LaunchServices pyobjc-framework-OpenDirectory pyobjc-framework-PreferencePanes pyobjc-framework-PubSub pyobjc-framework-QTKit pyobjc-framework-Quartz pyobjc-framework-ScreenSaver pyobjc-framework-ScriptingBridge pyobjc-framework-SearchKit pyobjc-framework-ServiceManagement pyobjc-framework-Social pyobjc-framework-StoreKit pyobjc-framework-SyncServices pyobjc-framework-SystemConfiguration pyobjc-framework-WebKit
Sponsored Links
Sponsored Links

« VimのNeoBundleでgitプロトコルの使用が廃止になった rumpsとGmail APIを使ってMacのGmailメニューバーアプリを作ってみた »

}