On my Mac, I use the menu bar countless times per day. In this post we will go through the process of creating a custom macOS menu bar app using Python. As an example, we will create a pomodoro app, which you can use to boost your productivity and manage your time from the convenience of your menu bar. It serves as a starting point for customization to suit your individual needs – you can even use this code as a boilerplate to create a radically different application!
Check out the full source code on GitHub
We will be using the following software:
- Python 3 and PyCharm as an IDE
- Rumps → Ridiculously Uncomplicated macOS Python Statusbar apps
- py2app → For creating standalone macOS apps from Python code (how cool is that?)
Step 1: Set up project directory and install dependencies
In this step we will install all requirements on our local machine. Run the following commands in your terminal to get started and set up the project directory:
<span>export </span><span>PROJECT_NAME</span><span>=</span>pomodoro<span>mkdir</span> ~/PycharmProjects/<span>$PROJECT_NAME</span><span>cd</span> ~/PycharmProjects/<span>$PROJECT_NAME</span><span>touch </span>pomodoro.py setup.py<span>export </span><span>PROJECT_NAME</span><span>=</span>pomodoro <span>mkdir</span> ~/PycharmProjects/<span>$PROJECT_NAME</span> <span>cd</span> ~/PycharmProjects/<span>$PROJECT_NAME</span> <span>touch </span>pomodoro.py setup.pyexport PROJECT_NAME=pomodoro mkdir ~/PycharmProjects/$PROJECT_NAME cd ~/PycharmProjects/$PROJECT_NAME touch pomodoro.py setup.py
Enter fullscreen mode Exit fullscreen mode
Be sure to install both rumps
and py2app
via pip (on my macOS). Enter the following in your terminal:
pip3 <span>install</span> <span>-U</span> py2app <span># this will install py2app using pip, or to upgrade to the latest released version of py2app</span>pip3 <span>install</span> <span>-U</span> rumps <span># this will install rumps using pip, or to upgrade to the latest released version of rumps</span>pip3 <span>install</span> <span>-U</span> py2app <span># this will install py2app using pip, or to upgrade to the latest released version of py2app</span> pip3 <span>install</span> <span>-U</span> rumps <span># this will install rumps using pip, or to upgrade to the latest released version of rumps</span>pip3 install -U py2app # this will install py2app using pip, or to upgrade to the latest released version of py2app pip3 install -U rumps # this will install rumps using pip, or to upgrade to the latest released version of rumps
Enter fullscreen mode Exit fullscreen mode
Step 2: Basic example
Open the project directory in your favorite editor or IDE – in my case PyCharm – by typing:
charm <span>.</span>charm <span>.</span>charm .
Enter fullscreen mode Exit fullscreen mode
In pomodoro.py
we need to set up the following boilerplate code in order to get started:
<span>import</span> <span>rumps</span><span>class</span> <span>PomodoroApp</span><span>(</span><span>object</span><span>):</span><span>def</span> <span>__init__</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>app</span> <span>=</span> <span>rumps</span><span>.</span><span>App</span><span>(</span><span>"Pomodoro"</span><span>,</span> <span>""</span><span>)</span><span>def</span> <span>run</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>app</span><span>.</span><span>run</span><span>()</span><span>if</span> <span>__name__</span> <span>==</span> <span>'__main__'</span><span>:</span><span>app</span> <span>=</span> <span>PomodoroApp</span><span>()</span><span>app</span><span>.</span><span>run</span><span>()</span><span>import</span> <span>rumps</span> <span>class</span> <span>PomodoroApp</span><span>(</span><span>object</span><span>):</span> <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>app</span> <span>=</span> <span>rumps</span><span>.</span><span>App</span><span>(</span><span>"Pomodoro"</span><span>,</span> <span>""</span><span>)</span> <span>def</span> <span>run</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>app</span><span>.</span><span>run</span><span>()</span> <span>if</span> <span>__name__</span> <span>==</span> <span>'__main__'</span><span>:</span> <span>app</span> <span>=</span> <span>PomodoroApp</span><span>()</span> <span>app</span><span>.</span><span>run</span><span>()</span>import rumps class PomodoroApp(object): def __init__(self): self.app = rumps.App("Pomodoro", "") def run(self): self.app.run() if __name__ == '__main__': app = PomodoroApp() app.run()
Enter fullscreen mode Exit fullscreen mode
If you execute the python program using python3 pomodoro.py
, you will notice a new addition to your menu bar – albeit with limited functionality, as you can see…
Step 3: Full implementation
Now we will implement the actual functionality of our pomodoro menu bar app – see the complete code for pomodoro.py
below:
<span>import</span> <span>rumps</span><span>class</span> <span>PomodoroApp</span><span>(</span><span>object</span><span>):</span><span>def</span> <span>__init__</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>config</span> <span>=</span> <span>{</span><span>"app_name"</span><span>:</span> <span>"Pomodoro"</span><span>,</span><span>"start"</span><span>:</span> <span>"Start Timer"</span><span>,</span><span>"pause"</span><span>:</span> <span>"Pause Timer"</span><span>,</span><span>"continue"</span><span>:</span> <span>"Continue Timer"</span><span>,</span><span>"stop"</span><span>:</span> <span>"Stop Timer"</span><span>,</span><span>"break_message"</span><span>:</span> <span>"Time is up! Take a break :)"</span><span>,</span><span>"interval"</span><span>:</span> <span>1500</span><span>}</span><span>self</span><span>.</span><span>app</span> <span>=</span> <span>rumps</span><span>.</span><span>App</span><span>(</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"app_name"</span><span>])</span><span>self</span><span>.</span><span>timer</span> <span>=</span> <span>rumps</span><span>.</span><span>Timer</span><span>(</span><span>self</span><span>.</span><span>on_tick</span><span>,</span> <span>1</span><span>)</span><span>self</span><span>.</span><span>interval</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"interval"</span><span>]</span><span>self</span><span>.</span><span>set_up_menu</span><span>()</span><span>self</span><span>.</span><span>start_pause_button</span> <span>=</span> <span>rumps</span><span>.</span><span>MenuItem</span><span>(</span><span>title</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"start"</span><span>],</span> <span>callback</span><span>=</span><span>self</span><span>.</span><span>start_timer</span><span>)</span><span>self</span><span>.</span><span>stop_button</span> <span>=</span> <span>rumps</span><span>.</span><span>MenuItem</span><span>(</span><span>title</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"stop"</span><span>],</span> <span>callback</span><span>=</span><span>None</span><span>)</span><span>self</span><span>.</span><span>app</span><span>.</span><span>menu</span> <span>=</span> <span>[</span><span>self</span><span>.</span><span>start_pause_button</span><span>,</span> <span>self</span><span>.</span><span>stop_button</span><span>]</span><span>def</span> <span>set_up_menu</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>timer</span><span>.</span><span>stop</span><span>()</span><span>self</span><span>.</span><span>timer</span><span>.</span><span>count</span> <span>=</span> <span>0</span><span>self</span><span>.</span><span>app</span><span>.</span><span>title</span> <span>=</span> <span>""</span><span>def</span> <span>on_tick</span><span>(</span><span>self</span><span>,</span> <span>sender</span><span>):</span><span>time_left</span> <span>=</span> <span>sender</span><span>.</span><span>end</span> <span>-</span> <span>sender</span><span>.</span><span>count</span><span>mins</span> <span>=</span> <span>time_left</span> <span>//</span> <span>60</span> <span>if</span> <span>time_left</span> <span>>=</span> <span>0</span> <span>else</span> <span>time_left</span> <span>//</span> <span>60</span> <span>+</span> <span>1</span><span>secs</span> <span>=</span> <span>time_left</span> <span>%</span> <span>60</span> <span>if</span> <span>time_left</span> <span>>=</span> <span>0</span> <span>else</span> <span>(</span><span>-</span><span>1</span> <span>*</span> <span>time_left</span><span>)</span> <span>%</span> <span>60</span><span>if</span> <span>mins</span> <span>==</span> <span>0</span> <span>and</span> <span>time_left</span> <span><</span> <span>0</span><span>:</span><span>rumps</span><span>.</span><span>notification</span><span>(</span><span>title</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"app_name"</span><span>],</span> <span>subtitle</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"break_message"</span><span>],</span> <span>message</span><span>=</span><span>''</span><span>)</span><span>self</span><span>.</span><span>stop_timer</span><span>()</span><span>self</span><span>.</span><span>stop_button</span><span>.</span><span>set_callback</span><span>(</span><span>None</span><span>)</span><span>else</span><span>:</span><span>self</span><span>.</span><span>stop_button</span><span>.</span><span>set_callback</span><span>(</span><span>self</span><span>.</span><span>stop_timer</span><span>)</span><span>self</span><span>.</span><span>app</span><span>.</span><span>title</span> <span>=</span> <span>'{:2d}:{:02d}'</span><span>.</span><span>format</span><span>(</span><span>mins</span><span>,</span> <span>secs</span><span>)</span><span>sender</span><span>.</span><span>count</span> <span>+=</span> <span>1</span><span>def</span> <span>start_timer</span><span>(</span><span>self</span><span>,</span> <span>sender</span><span>):</span><span>if</span> <span>sender</span><span>.</span><span>title</span><span>.</span><span>lower</span><span>().</span><span>startswith</span><span>((</span><span>"start"</span><span>,</span> <span>"continue"</span><span>)):</span><span>if</span> <span>sender</span><span>.</span><span>title</span> <span>==</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"start"</span><span>]:</span><span>self</span><span>.</span><span>timer</span><span>.</span><span>count</span> <span>=</span> <span>0</span><span>self</span><span>.</span><span>timer</span><span>.</span><span>end</span> <span>=</span> <span>self</span><span>.</span><span>interval</span><span>sender</span><span>.</span><span>title</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"pause"</span><span>]</span><span>self</span><span>.</span><span>timer</span><span>.</span><span>start</span><span>()</span><span>else</span><span>:</span><span>sender</span><span>.</span><span>title</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"continue"</span><span>]</span><span>self</span><span>.</span><span>timer</span><span>.</span><span>stop</span><span>()</span><span>def</span> <span>stop_timer</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>set_up_menu</span><span>()</span><span>self</span><span>.</span><span>stop_button</span><span>.</span><span>set_callback</span><span>(</span><span>None</span><span>)</span><span>self</span><span>.</span><span>start_pause_button</span><span>.</span><span>title</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"start"</span><span>]</span><span>def</span> <span>run</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>app</span><span>.</span><span>run</span><span>()</span><span>if</span> <span>__name__</span> <span>==</span> <span>'__main__'</span><span>:</span><span>app</span> <span>=</span> <span>PomodoroApp</span><span>()</span><span>app</span><span>.</span><span>run</span><span>()</span><span>import</span> <span>rumps</span> <span>class</span> <span>PomodoroApp</span><span>(</span><span>object</span><span>):</span> <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>config</span> <span>=</span> <span>{</span> <span>"app_name"</span><span>:</span> <span>"Pomodoro"</span><span>,</span> <span>"start"</span><span>:</span> <span>"Start Timer"</span><span>,</span> <span>"pause"</span><span>:</span> <span>"Pause Timer"</span><span>,</span> <span>"continue"</span><span>:</span> <span>"Continue Timer"</span><span>,</span> <span>"stop"</span><span>:</span> <span>"Stop Timer"</span><span>,</span> <span>"break_message"</span><span>:</span> <span>"Time is up! Take a break :)"</span><span>,</span> <span>"interval"</span><span>:</span> <span>1500</span> <span>}</span> <span>self</span><span>.</span><span>app</span> <span>=</span> <span>rumps</span><span>.</span><span>App</span><span>(</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"app_name"</span><span>])</span> <span>self</span><span>.</span><span>timer</span> <span>=</span> <span>rumps</span><span>.</span><span>Timer</span><span>(</span><span>self</span><span>.</span><span>on_tick</span><span>,</span> <span>1</span><span>)</span> <span>self</span><span>.</span><span>interval</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"interval"</span><span>]</span> <span>self</span><span>.</span><span>set_up_menu</span><span>()</span> <span>self</span><span>.</span><span>start_pause_button</span> <span>=</span> <span>rumps</span><span>.</span><span>MenuItem</span><span>(</span><span>title</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"start"</span><span>],</span> <span>callback</span><span>=</span><span>self</span><span>.</span><span>start_timer</span><span>)</span> <span>self</span><span>.</span><span>stop_button</span> <span>=</span> <span>rumps</span><span>.</span><span>MenuItem</span><span>(</span><span>title</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"stop"</span><span>],</span> <span>callback</span><span>=</span><span>None</span><span>)</span> <span>self</span><span>.</span><span>app</span><span>.</span><span>menu</span> <span>=</span> <span>[</span><span>self</span><span>.</span><span>start_pause_button</span><span>,</span> <span>self</span><span>.</span><span>stop_button</span><span>]</span> <span>def</span> <span>set_up_menu</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>timer</span><span>.</span><span>stop</span><span>()</span> <span>self</span><span>.</span><span>timer</span><span>.</span><span>count</span> <span>=</span> <span>0</span> <span>self</span><span>.</span><span>app</span><span>.</span><span>title</span> <span>=</span> <span>""</span> <span>def</span> <span>on_tick</span><span>(</span><span>self</span><span>,</span> <span>sender</span><span>):</span> <span>time_left</span> <span>=</span> <span>sender</span><span>.</span><span>end</span> <span>-</span> <span>sender</span><span>.</span><span>count</span> <span>mins</span> <span>=</span> <span>time_left</span> <span>//</span> <span>60</span> <span>if</span> <span>time_left</span> <span>>=</span> <span>0</span> <span>else</span> <span>time_left</span> <span>//</span> <span>60</span> <span>+</span> <span>1</span> <span>secs</span> <span>=</span> <span>time_left</span> <span>%</span> <span>60</span> <span>if</span> <span>time_left</span> <span>>=</span> <span>0</span> <span>else</span> <span>(</span><span>-</span><span>1</span> <span>*</span> <span>time_left</span><span>)</span> <span>%</span> <span>60</span> <span>if</span> <span>mins</span> <span>==</span> <span>0</span> <span>and</span> <span>time_left</span> <span><</span> <span>0</span><span>:</span> <span>rumps</span><span>.</span><span>notification</span><span>(</span><span>title</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"app_name"</span><span>],</span> <span>subtitle</span><span>=</span><span>self</span><span>.</span><span>config</span><span>[</span><span>"break_message"</span><span>],</span> <span>message</span><span>=</span><span>''</span><span>)</span> <span>self</span><span>.</span><span>stop_timer</span><span>()</span> <span>self</span><span>.</span><span>stop_button</span><span>.</span><span>set_callback</span><span>(</span><span>None</span><span>)</span> <span>else</span><span>:</span> <span>self</span><span>.</span><span>stop_button</span><span>.</span><span>set_callback</span><span>(</span><span>self</span><span>.</span><span>stop_timer</span><span>)</span> <span>self</span><span>.</span><span>app</span><span>.</span><span>title</span> <span>=</span> <span>'{:2d}:{:02d}'</span><span>.</span><span>format</span><span>(</span><span>mins</span><span>,</span> <span>secs</span><span>)</span> <span>sender</span><span>.</span><span>count</span> <span>+=</span> <span>1</span> <span>def</span> <span>start_timer</span><span>(</span><span>self</span><span>,</span> <span>sender</span><span>):</span> <span>if</span> <span>sender</span><span>.</span><span>title</span><span>.</span><span>lower</span><span>().</span><span>startswith</span><span>((</span><span>"start"</span><span>,</span> <span>"continue"</span><span>)):</span> <span>if</span> <span>sender</span><span>.</span><span>title</span> <span>==</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"start"</span><span>]:</span> <span>self</span><span>.</span><span>timer</span><span>.</span><span>count</span> <span>=</span> <span>0</span> <span>self</span><span>.</span><span>timer</span><span>.</span><span>end</span> <span>=</span> <span>self</span><span>.</span><span>interval</span> <span>sender</span><span>.</span><span>title</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"pause"</span><span>]</span> <span>self</span><span>.</span><span>timer</span><span>.</span><span>start</span><span>()</span> <span>else</span><span>:</span> <span>sender</span><span>.</span><span>title</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"continue"</span><span>]</span> <span>self</span><span>.</span><span>timer</span><span>.</span><span>stop</span><span>()</span> <span>def</span> <span>stop_timer</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>set_up_menu</span><span>()</span> <span>self</span><span>.</span><span>stop_button</span><span>.</span><span>set_callback</span><span>(</span><span>None</span><span>)</span> <span>self</span><span>.</span><span>start_pause_button</span><span>.</span><span>title</span> <span>=</span> <span>self</span><span>.</span><span>config</span><span>[</span><span>"start"</span><span>]</span> <span>def</span> <span>run</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>app</span><span>.</span><span>run</span><span>()</span> <span>if</span> <span>__name__</span> <span>==</span> <span>'__main__'</span><span>:</span> <span>app</span> <span>=</span> <span>PomodoroApp</span><span>()</span> <span>app</span><span>.</span><span>run</span><span>()</span>import rumps class PomodoroApp(object): def __init__(self): self.config = { "app_name": "Pomodoro", "start": "Start Timer", "pause": "Pause Timer", "continue": "Continue Timer", "stop": "Stop Timer", "break_message": "Time is up! Take a break :)", "interval": 1500 } self.app = rumps.App(self.config["app_name"]) self.timer = rumps.Timer(self.on_tick, 1) self.interval = self.config["interval"] self.set_up_menu() self.start_pause_button = rumps.MenuItem(title=self.config["start"], callback=self.start_timer) self.stop_button = rumps.MenuItem(title=self.config["stop"], callback=None) self.app.menu = [self.start_pause_button, self.stop_button] def set_up_menu(self): self.timer.stop() self.timer.count = 0 self.app.title = "" def on_tick(self, sender): time_left = sender.end - sender.count mins = time_left // 60 if time_left >= 0 else time_left // 60 + 1 secs = time_left % 60 if time_left >= 0 else (-1 * time_left) % 60 if mins == 0 and time_left < 0: rumps.notification(title=self.config["app_name"], subtitle=self.config["break_message"], message='') self.stop_timer() self.stop_button.set_callback(None) else: self.stop_button.set_callback(self.stop_timer) self.app.title = '{:2d}:{:02d}'.format(mins, secs) sender.count += 1 def start_timer(self, sender): if sender.title.lower().startswith(("start", "continue")): if sender.title == self.config["start"]: self.timer.count = 0 self.timer.end = self.interval sender.title = self.config["pause"] self.timer.start() else: sender.title = self.config["continue"] self.timer.stop() def stop_timer(self): self.set_up_menu() self.stop_button.set_callback(None) self.start_pause_button.title = self.config["start"] def run(self): self.app.run() if __name__ == '__main__': app = PomodoroApp() app.run()
Enter fullscreen mode Exit fullscreen mode
By executing the python program again using python3 pomodoro.py
, you can see our menu bar app in its full glory:
In the next step we will go ahead and create a macOS application using py2app
so you don’t have to execute it via Python every single time. It needs to be ready for every day use, and that means adding it to the login items of your Mac!
Step 4: Create macOS app from our python code
In setup.py
we need to add the following code, which provides all the necessary instructions to create the application bundle (app name, app version, app icon, etc.) to py2app
in form of a setup script:
<span>from</span> <span>setuptools</span> <span>import</span> <span>setup</span><span>APP</span> <span>=</span> <span>[</span><span>'pomodoro.py'</span><span>]</span><span>DATA_FILES</span> <span>=</span> <span>[]</span><span>OPTIONS</span> <span>=</span> <span>{</span><span>'argv_emulation'</span><span>:</span> <span>True</span><span>,</span><span>'iconfile'</span><span>:</span> <span>'icon.icns'</span><span>,</span><span>'plist'</span><span>:</span> <span>{</span><span>'CFBundleShortVersionString'</span><span>:</span> <span>'0.2.0'</span><span>,</span><span>'LSUIElement'</span><span>:</span> <span>True</span><span>,</span><span>},</span><span>'packages'</span><span>:</span> <span>[</span><span>'rumps'</span><span>],</span><span>}</span><span>setup</span><span>(</span><span>app</span><span>=</span><span>APP</span><span>,</span><span>name</span><span>=</span><span>'Pomodoro'</span><span>,</span><span>data_files</span><span>=</span><span>DATA_FILES</span><span>,</span><span>options</span><span>=</span><span>{</span><span>'py2app'</span><span>:</span> <span>OPTIONS</span><span>},</span><span>setup_requires</span><span>=</span><span>[</span><span>'py2app'</span><span>],</span> <span>install_requires</span><span>=</span><span>[</span><span>'rumps'</span><span>]</span><span>)</span><span>from</span> <span>setuptools</span> <span>import</span> <span>setup</span> <span>APP</span> <span>=</span> <span>[</span><span>'pomodoro.py'</span><span>]</span> <span>DATA_FILES</span> <span>=</span> <span>[]</span> <span>OPTIONS</span> <span>=</span> <span>{</span> <span>'argv_emulation'</span><span>:</span> <span>True</span><span>,</span> <span>'iconfile'</span><span>:</span> <span>'icon.icns'</span><span>,</span> <span>'plist'</span><span>:</span> <span>{</span> <span>'CFBundleShortVersionString'</span><span>:</span> <span>'0.2.0'</span><span>,</span> <span>'LSUIElement'</span><span>:</span> <span>True</span><span>,</span> <span>},</span> <span>'packages'</span><span>:</span> <span>[</span><span>'rumps'</span><span>],</span> <span>}</span> <span>setup</span><span>(</span> <span>app</span><span>=</span><span>APP</span><span>,</span> <span>name</span><span>=</span><span>'Pomodoro'</span><span>,</span> <span>data_files</span><span>=</span><span>DATA_FILES</span><span>,</span> <span>options</span><span>=</span><span>{</span><span>'py2app'</span><span>:</span> <span>OPTIONS</span><span>},</span> <span>setup_requires</span><span>=</span><span>[</span><span>'py2app'</span><span>],</span> <span>install_requires</span><span>=</span><span>[</span><span>'rumps'</span><span>]</span> <span>)</span>from setuptools import setup APP = ['pomodoro.py'] DATA_FILES = [] OPTIONS = { 'argv_emulation': True, 'iconfile': 'icon.icns', 'plist': { 'CFBundleShortVersionString': '0.2.0', 'LSUIElement': True, }, 'packages': ['rumps'], } setup( app=APP, name='Pomodoro', data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], install_requires=['rumps'] )
Enter fullscreen mode Exit fullscreen mode
Are you missing the icon file? You can download the file I used from the GitHub repository.
Now you can go ahead and create your Pomodoro.app
bundle using py2app
. Type the following in your terminal:
python3 setup.py py2apppython3 setup.py py2apppython3 setup.py py2app
Enter fullscreen mode Exit fullscreen mode
The application bundle will be created in your project directory under /dist/Pomodoro.app
.
Conclusion
I’ve implemented a similar menu bar app with some additional functionality. It’s called Timebox. Check it out and let me know what you think!
I hope you enjoyed this article – please let me know if you have any questions or if you run into any issues.
原文链接:Create a macOS Menu Bar App with Python (Pomodoro Timer)
暂无评论内容