」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 為 macOS 建立市政廳時鐘應用程式:綜合指南

為 macOS 建立市政廳時鐘應用程式:綜合指南

發佈於2024-11-03
瀏覽:210

Building a City Hall Clock App for macOS: A Comprehensive Guide

准备好为您的 Mac 构建一个很酷的市政厅时钟应用程序了吗?伟大的!我们将创建一个位于菜单栏中的应用程序,每 15 分钟发出一次提示音,甚至可以计算时间。让我们一步步分解,我将解释代码的每一部分,以便您能够理解发生了什么。

项目概况

我们的市政厅时钟应用程序将:

  • 在 macOS 菜单栏中显示时钟图标
  • 每 15 分钟鸣响一次
  • 在每个小时的顶部鸣响小时数
  • 在菜单栏中提供“退出”选项
  • 作为正确的 macOS 应用程序运行,无需打开终端窗口

设置项目

首先,让我们设置我们的项目:

  1. 创建一个新目录:
   mkdir CityHallClock
   cd CityHallClock
  1. 初始化一个新的Go模块:
   go mod init cityhallclock
  1. 安装所需的依赖项:
   go get github.com/getlantern/systray
   go get github.com/faiface/beep

主要代码

现在,让我们创建 main.go 文件并浏览每个函数:

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)

让我们分解一下每个功能:

1.main()函数

func main() {
    initAudio()
    systray.Run(onReady, onExit)
}

这是我们的应用程序启动的地方。它做了两件重要的事情:

  1. 调用 initAudio() 来设置我们的铃声。
  2. 运行我们的系统托盘应用程序,告诉它在准备好(onReady)和退出(onExit)时要做什么。

2.initAudio()函数

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)
    }
}

此函数设置我们的音频:

  1. 它找到我们的应用程序运行的位置并找到铃声文件。
  2. 打开 MP3 文件并对其进行解码。
  3. 创建带有铃声的音频缓冲区。
  4. 初始化音频扬声器。

如果出现任何问题(例如找不到声音文件),它将记录错误并退出。

3.onReady()函数

func onReady() {
    systray.SetIcon(getIcon())
    systray.SetTitle("City Hall Clock")
    systray.SetTooltip("City Hall Clock")

    mQuit := systray.AddMenuItem("Quit", "Quit the app")

    go func() {
        



该函数设置我们的菜单栏图标:

  1. 设置图标(使用 getIcon())。
  2. 设置标题和工具提示。
  3. 在菜单中添加“退出”选项。
  4. 单击“退出”选项时开始侦听。
  5. 开始运行我们的时钟(在一个单独的 goroutine 中,这样它就不会阻塞)。

4.onExit()函数

func onExit() {
    // Cleanup tasks go here
}

当应用程序退出时调用此函数。我们在这里不做任何事情,但您可以根据需要添加清理任务。

5.runClock()函数

func runClock() {
    ticker := time.NewTicker(time.Minute)
    defer ticker.Stop()

    for {
        select {
        case t := 



这是我们时钟的“心脏”:

  1. 它创建了一个每分钟“滴答”的股票代码。
  2. 在无限循环中,它每分钟检查一次时间。
  3. 如果是整点或一刻钟,则会触发铃声。

6. chime()函数

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 



此功能播放我们的铃声:

  1. 它计算出鸣响多少次(每刻钟一次,或整点的小时数)。
  2. 然后它会多次播放铃声,并在铃声之间短暂停顿。

7. getIcon()函数

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
}

该函数获取我们的菜单栏图标:

  1. 它找到我们的应用程序正在运行的位置。
  2. 在资源目录中找到图标文件。
  3. 读取图标文件并返回其内容。

创建 macOS 应用程序包

为了使我们的应用程序成为真正的 macOS 公民,我们需要创建一个应用程序包。这涉及创建一个 Info.plist 文件:

CFBundleExecutableCityHallClockCFBundleIconFileAppIconCFBundleIdentifiercom.yourcompany.cityhallclockCFBundleNameCity Hall ClockCFBundlePackageTypeAPPLCFBundleShortVersionString1.0CFBundleVersion1LSMinimumSystemVersion10.12LSUIElementNSHighResolutionCapable

将其另存为项目目录中的 Info.plist。

添加自定义图标

我们需要两个图标:

  1. 菜单栏图标:创建一个 22x22 像素的 PNG,命名为 icon.png。
  2. 应用程序图标:创建.icns文件:
    • 使图像大小为 16x16 到 1024x1024 像素。
    • 将它们保存在 AppIcon.iconset 中,名称如 icon_16x16.png。
    • 运行:iconutil -c icns AppIcon.iconset

建筑和包装

让我们创建一个构建脚本(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"

使用 chmod x build.sh 使其可执行,然后使用 ./build.sh 运行它。

结论

这就是你得到的!您已经为 macOS 构建了功能齐全的市政厅时钟应用程序。您已了解:

  • 使用 Go 创建菜单栏应用程序
  • 以特定间隔播放声音
  • 将 Go 应用程序打包为原生 macOS 应用程序

请随意扩展这一点。也许添加自定义铃声或不同铃声间隔的首选项。天空是极限!

您可以在这里找到完整的源代码 https://github.com/rezmoss/citychime

快乐编码,享受你的新时钟!

版本聲明 本文轉載於:https://dev.to/rezmoss/building-a-city-hall-clock-app-for-macos-a-comprehensive-guide-48k4?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何將單一輸入欄位設定為分區輸入?
    如何將單一輸入欄位設定為分區輸入?
    將輸入欄位設為分區輸入有多種方法可用於建立一系列分區輸入欄位。一種方法利用「字母間距」來分隔單一輸入欄位內的字元。此外,「background-image」和「border-bottom」樣式可以進一步增強多個輸入欄位的錯覺。 CSS Snippet以下 CSS 程式碼示範如何建立所需的效果:#pa...
    程式設計 發佈於2024-11-05
  • 用 Go 建構一個簡單的負載平衡器
    用 Go 建構一個簡單的負載平衡器
    负载均衡器在现代软件开发中至关重要。如果您曾经想知道如何在多个服务器之间分配请求,或者为什么某些网站即使在流量大的情况下也感觉更快,答案通常在于高效的负载平衡。 在这篇文章中,我们将使用 Go 中的循环算法构建一个简单的应用程序负载均衡器。这篇文章的目的是逐步了解负载均衡器的工作原理。 ...
    程式設計 發佈於2024-11-05
  • 如何以超連結方式開啟本機目錄?
    如何以超連結方式開啟本機目錄?
    透過超連結導航本地目錄嘗試在連結互動時啟動本地目錄視圖時,您可能會遇到限制。然而,有一個解決方案可以解決這個問題,並且可以在各種瀏覽器之間無縫運作。 實作方法因為從HTML 頁面直接開啟路徑或啟動瀏覽器是由於安全原因受到限制,更可行的方法是提供可下載的連結( .URL 或.LNK)。 建議路徑:.U...
    程式設計 發佈於2024-11-05
  • 為什麼 Makefile 會拋出 Go 指令的權限被拒絕錯誤?
    為什麼 Makefile 會拋出 Go 指令的權限被拒絕錯誤?
    在執行Go 時Makefile 中出現權限被拒絕錯誤透過Makefile 執行Go 指令時可能會遇到「權限被拒絕」錯誤,即使你可以直接執行它們。這種差異是由於 GNU make 中的問題引起的。 原因:當您的 PATH 上有一個目錄包含名為“go.gnu”的子目錄時,就會出現此錯誤。 ”例如,如果您...
    程式設計 發佈於2024-11-05
  • parseInt 函數中 Radix 參數的意義是什麼?
    parseInt 函數中 Radix 參數的意義是什麼?
    parseInt 函數中 Radix 的作用parseInt 函數將字串轉換為整數。然而,它並不總是採用以 10 為基數的數字系統。若要指定所需的基數,請使用基數參數。 理解基數基數是指單一數字表示的值的數量。例如,十六進制的基數為 16,八進制的基數為 8,二進制的基數為 2。 為什麼要用基數? ...
    程式設計 發佈於2024-11-05
  • 如何使用 JavaScript 將連結保留在同一選項卡中?
    如何使用 JavaScript 將連結保留在同一選項卡中?
    在同一分頁和視窗中導覽連結您可能會遇到想要在同一視窗和分頁中開啟連結的情況作為當前頁面。但是,使用 window.open 函數通常會導致在新分頁中開啟連結。為了解決這個問題,您可以使用name 屬性,如下所示:window.open("https://www.youraddress.co...
    程式設計 發佈於2024-11-05
  • 如何解決Python中的循環依賴?
    如何解決Python中的循環依賴?
    Python 中的循環依賴使用 Python 模組時遇到循環依賴可能是一個令人沮喪的問題。在這個特定場景中,我們有兩個文件,node.py 和 path.py,分別包含 Node 和 Path 類別。 最初,path.py 使用 from node.py import * 導入 node.py。但是...
    程式設計 發佈於2024-11-05
  • MariaDB 與 MySQL:開發人員需要了解什麼
    MariaDB 與 MySQL:開發人員需要了解什麼
    MariaDB 和 MySQL 是著名的開源 RDBMS,但儘管它們有著共同的歷史,但它們在功能和效能方面卻有所不同。本文快速強調了主要差異,幫助開發人員決定哪個資料庫最適合他們的需求。 差異和範例 儲存引擎,MariaDB 對 Aria 和 MyRocks 等引擎的擴充支援提供了...
    程式設計 發佈於2024-11-05
  • 為什麼我的 Goroutine 遞增變數會產生意外的結果?
    為什麼我的 Goroutine 遞增變數會產生意外的結果?
    這是編譯器最佳化的結果嗎? 在此程式碼片段中,啟動了一個 goroutine 並重複遞增變數 i:package main import "time" func main() { i := 1 go func() { for { ...
    程式設計 發佈於2024-11-05
  • 利用 AI 快速學習 Node.js - 第 4 天
    利用 AI 快速學習 Node.js - 第 4 天
    今天,借助ChatGPT繼續學習Node.js,重點是非同步程式設計。這是 Node.js 中最重要的概念之一,我很高興能夠開始掌握它。 理論 在 Node.js 中,非同步程式設計因其非阻塞、事件驅動的架構而至關重要。這意味著文件讀取、資料庫查詢或網路請求等操作在等待結果時不會阻塞其他程式碼的執...
    程式設計 發佈於2024-11-05
  • Java 可以定義帶有嵌入引號的字串而不轉義嗎?
    Java 可以定義帶有嵌入引號的字串而不轉義嗎?
    揭開Java 使用嵌入式引號定義字串的替代方法在Java 中處理字串時,您常常會在文字中遇到大量引號,導致繁瑣的轉義和可讀性挑戰。雖然其他語言提供了處理這種情況的語法,但 Java 缺乏類似的選項。 問題: Java 是否提供了另一種方法來定義帶有嵌入引號的字串而不訴諸轉義? 答案: 雖然Java ...
    程式設計 發佈於2024-11-05
  • 耐用的 Python:建立防彈的長期運作工作流程,變得簡單
    耐用的 Python:建立防彈的長期運作工作流程,變得簡單
    在现代软件开发中,创建强大的工作流程来连接来自各种服务的 API 并处理同步和异步事件是一个常见的挑战。传统方法涉及使用队列、微服务和状态管理系统的组合来构建可扩展的应用程序。虽然有效,但这种架构带来了巨大的开销:设置和维护消息队列等基础设施、运行服务器或 lambda 函数、管理数据库中的状态以及...
    程式設計 發佈於2024-11-05
  • 使用 Node.js 建立即時儀表板
    使用 Node.js 建立即時儀表板
    介紹 在當今快節奏的商業世界中,組織存取即時數據以做出明智的決策至關重要。這就是使用 Node.js 建立即時儀表板發揮作用的地方。 Node.js 是一種流行的、輕量級的、高效的 JavaScript 執行環境,近年來獲得了極大的普及。在本文中,我們將討論專門使用 Node.js...
    程式設計 發佈於2024-11-05
  • 以下是一些適合您的文章的基於問題的標題,重點關注問題和解決方案:

* 如何跨子域共享localStorage資料?
* 想要在您之間共享本機儲存數據
    以下是一些適合您的文章的基於問題的標題,重點關注問題和解決方案: * 如何跨子域共享localStorage資料? * 想要在您之間共享本機儲存數據
    如何跨子網域共享 localStorage許多網站使用 localStorage 而不是 cookie 來儲存數據,因為它提供了更好的效能和安全性。然而,localStorage 在處理子網域時可能會帶來挑戰,因為每個子網域都有自己單獨的 localStorage 物件。當使用者同時使用主網域(例如...
    程式設計 發佈於2024-11-05
  • C++ 是否像 Python 一樣有遞迴深度限制?
    C++ 是否像 Python 一樣有遞迴深度限制?
    C 是否像 Python 一樣有遞迴深度限制? 與 Python 不同,Python 由於其解釋性而具有最大遞歸深度,C 是編譯的,並且沒有直接面對這樣的限制。然而,C 確實有作業系統透過其分配的堆疊大小施加的遞歸限制。 C 中的堆疊大小通常明顯小於可用 RAM,並且可以在作業系統內進行修改(例如使...
    程式設計 發佈於2024-11-05

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3