Building a City Hall Clock App for macOS: A Comprehensive Guide
Ready to build a cool City Hall Clock app for your Mac? Great! We're going to create an app that sits in your menu bar, chimes every 15 minutes, and even counts out the hours. Let's break it down step by step, and I'll explain every part of the code so you can understand what's going on.
Project Overview
Our City Hall Clock app will:
- Display a clock icon in the macOS menu bar
- Chime every 15 minutes
- Chime the number of hours at the top of each hour
- Provide a "Quit" option in the menu bar
- Run as a proper macOS application without opening a terminal window
Setting Up the Project
First things first, let's set up our project:
- Create a new directory:
mkdir CityHallClock cd CityHallClock
- Initialize a new Go module:
go mod init cityhallclock
- Install the required dependencies:
go get github.com/getlantern/systray go get github.com/faiface/beep
The Main Code
Now, let's create our main.go file and go through each function:
package main import ( "bytes" "log" "os" "path/filepath" "time" "github.com/faiface/beep" "github.com/faiface/beep/mp3" "github.com/faiface/beep/speaker" "github.com/getlantern/systray" ) var ( audioBuffer *beep.Buffer ) func main() { initAudio() systray.Run(onReady, onExit) } // ... (other functions will go here)
Let's break down each function:
1. main() Function
func main() { initAudio() systray.Run(onReady, onExit) }
This is where our app starts. It does two important things:
- Calls initAudio() to set up our chime sound.
- Runs our systray app, telling it what to do when it's ready (onReady) and when it's quitting (onExit).
2. initAudio() Function
func initAudio() { execPath, err := os.Executable() if err != nil { log.Fatal(err) } resourcesPath := filepath.Join(filepath.Dir(execPath), "..", "Resources") chimeFile := filepath.Join(resourcesPath, "chime.mp3") f, err := os.Open(chimeFile) if err != nil { log.Fatal(err) } defer f.Close() streamer, format, err := mp3.Decode(f) if err != nil { log.Fatal(err) } defer streamer.Close() audioBuffer = beep.NewBuffer(format) audioBuffer.Append(streamer) err = speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10)) if err != nil { log.Fatal(err) } }
This function sets up our audio:
- It finds where our app is running and locates the chime sound file.
- Opens the MP3 file and decodes it.
- Creates an audio buffer with the chime sound.
- Initializes the audio speaker.
If anything goes wrong (like not finding the sound file), it'll log the error and quit.
3. onReady() Function
func onReady() { systray.SetIcon(getIcon()) systray.SetTitle("City Hall Clock") systray.SetTooltip("City Hall Clock") mQuit := systray.AddMenuItem("Quit", "Quit the app") go func() { <-mQuit.ClickedCh systray.Quit() }() go runClock() }
This function sets up our menu bar icon:
- Sets the icon (using getIcon()).
- Sets the title and tooltip.
- Adds a "Quit" option to the menu.
- Starts listening for when the "Quit" option is clicked.
- Starts running our clock (in a separate goroutine so it doesn't block).
4. onExit() Function
func onExit() { // Cleanup tasks go here }
This function is called when the app is quitting. We're not doing anything here, but you could add cleanup tasks if needed.
5. runClock() Function
func runClock() { ticker := time.NewTicker(time.Minute) defer ticker.Stop() for { select { case t := <-ticker.C: if t.Minute() == 0 || t.Minute() == 15 || t.Minute() == 30 || t.Minute() == 45 { go chime(t) } } } }
This is our clock's "heart":
- It creates a ticker that "ticks" every minute.
- In an infinite loop, it checks the time every minute.
- If it's the top of the hour or quarter past, it triggers the chime.
6. chime() Function
func chime(t time.Time) { hour := t.Hour() minute := t.Minute() var chimeTimes int if minute == 0 { chimeTimes = hour % 12 if chimeTimes == 0 { chimeTimes = 12 } } else { chimeTimes = 1 } for i := 0; i < chimeTimes; i++ { streamer := audioBuffer.Streamer(0, audioBuffer.Len()) speaker.Play(streamer) time.Sleep(time.Duration(audioBuffer.Len()) * time.Second / time.Duration(audioBuffer.Format().SampleRate)) if i < chimeTimes-1 { time.Sleep(500 * time.Millisecond) // Wait between chimes } } }
This function plays our chimes:
- It figures out how many times to chime (once for quarter-hours, or the number of the hour at the top of the hour).
- It then plays the chime sound that many times, with a short pause between chimes.
7. getIcon() Function
func getIcon() []byte { execPath, err := os.Executable() if err != nil { log.Fatal(err) } iconPath := filepath.Join(filepath.Dir(execPath), "..", "Resources", "icon.png") // Read the icon file icon, err := os.ReadFile(iconPath) if err != nil { log.Fatal(err) } return icon }
This function gets our menu bar icon:
- It finds where our app is running.
- Locates the icon file in the Resources directory.
- Reads the icon file and returns its contents.
Creating the macOS Application Bundle
To make our app a proper macOS citizen, we need to create an application bundle. This involves creating an Info.plist file:
<?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>CFBundleExecutable</key> <string>CityHallClock</string> <key>CFBundleIconFile</key> <string>AppIcon</string> <key>CFBundleIdentifier</key> <string>com.yourcompany.cityhallclock</string> <key>CFBundleName</key> <string>City Hall Clock</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleVersion</key> <string>1</string> <key>LSMinimumSystemVersion</key> <string>10.12</string> <key>LSUIElement</key> <true/> <key>NSHighResolutionCapable</key> <true/> </dict> </plist>
Save this as Info.plist in your project directory.
Adding Custom Icons
We need two icons:
- Menu Bar Icon: Create a 22x22 pixel PNG named icon.png.
- App Icon: Create an .icns file:
- Make images sized 16x16 to 1024x1024 pixels.
- Save them in AppIcon.iconset with names like icon_16x16.png.
- Run: iconutil -c icns AppIcon.iconset
Building and Packaging
Let's create a build script (build.sh):
#!/bin/bash # Build the Go application go build -o CityHallClock # Create the app bundle structure mkdir -p CityHallClock.app/Contents/MacOS mkdir -p CityHallClock.app/Contents/Resources # Move the executable to the app bundle mv CityHallClock CityHallClock.app/Contents/MacOS/ # Copy the Info.plist cp Info.plist CityHallClock.app/Contents/ # Copy the chime sound to Resources cp chime.mp3 CityHallClock.app/Contents/Resources/ # Copy the menu bar icon cp icon.png CityHallClock.app/Contents/Resources/ # Copy the application icon cp AppIcon.icns CityHallClock.app/Contents/Resources/ echo "Application bundle created: CityHallClock.app"
Make it executable with chmod +x build.sh, then run it with ./build.sh.
Conclusion
And there you have it! You've built a fully functional City Hall Clock app for macOS. You've learned about:
- Creating a menu bar app with Go
- Playing sounds at specific intervals
- Packaging a Go application as a native macOS app
Feel free to expand on this. Maybe add preferences for custom chimes or different chiming intervals. The sky's the limit!
You can find full source code here https://github.com/rezmoss/citychime
Happy coding, and enjoy your new clock!
The above is the detailed content of Building a City Hall Clock App for macOS: A Comprehensive Guide. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











Go language performs well in building efficient and scalable systems. Its advantages include: 1. High performance: compiled into machine code, fast running speed; 2. Concurrent programming: simplify multitasking through goroutines and channels; 3. Simplicity: concise syntax, reducing learning and maintenance costs; 4. Cross-platform: supports cross-platform compilation, easy deployment.

Golang is better than Python in terms of performance and scalability. 1) Golang's compilation-type characteristics and efficient concurrency model make it perform well in high concurrency scenarios. 2) Python, as an interpreted language, executes slowly, but can optimize performance through tools such as Cython.

Golang is better than C in concurrency, while C is better than Golang in raw speed. 1) Golang achieves efficient concurrency through goroutine and channel, which is suitable for handling a large number of concurrent tasks. 2)C Through compiler optimization and standard library, it provides high performance close to hardware, suitable for applications that require extreme optimization.

Goimpactsdevelopmentpositivelythroughspeed,efficiency,andsimplicity.1)Speed:Gocompilesquicklyandrunsefficiently,idealforlargeprojects.2)Efficiency:Itscomprehensivestandardlibraryreducesexternaldependencies,enhancingdevelopmentefficiency.3)Simplicity:

Golang and Python each have their own advantages: Golang is suitable for high performance and concurrent programming, while Python is suitable for data science and web development. Golang is known for its concurrency model and efficient performance, while Python is known for its concise syntax and rich library ecosystem.

The performance differences between Golang and C are mainly reflected in memory management, compilation optimization and runtime efficiency. 1) Golang's garbage collection mechanism is convenient but may affect performance, 2) C's manual memory management and compiler optimization are more efficient in recursive computing.

Golang and C each have their own advantages in performance competitions: 1) Golang is suitable for high concurrency and rapid development, and 2) C provides higher performance and fine-grained control. The selection should be based on project requirements and team technology stack.

Golang is suitable for rapid development and concurrent scenarios, and C is suitable for scenarios where extreme performance and low-level control are required. 1) Golang improves performance through garbage collection and concurrency mechanisms, and is suitable for high-concurrency Web service development. 2) C achieves the ultimate performance through manual memory management and compiler optimization, and is suitable for embedded system development.
