”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 为 macOS 构建市政厅时钟应用程序:综合指南

为 macOS 构建市政厅时钟应用程序:综合指南

发布于2024-11-03
浏览:226

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 文件:





    CFBundleExecutable
    CityHallClock
    CFBundleIconFile
    AppIcon
    CFBundleIdentifier
    com.yourcompany.cityhallclock
    CFBundleName
    City Hall Clock
    CFBundlePackageType
    APPL
    CFBundleShortVersionString
    1.0
    CFBundleVersion
    1
    LSMinimumSystemVersion
    10.12
    LSUIElement
    
    NSHighResolutionCapable
    


将其另存为项目目录中的 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]删除
最新教程 更多>
  • C++20 Consteval函数中模板参数能否依赖于函数参数?
    C++20 Consteval函数中模板参数能否依赖于函数参数?
    [ consteval函数和模板参数依赖于函数参数在C 17中,模板参数不能依赖一个函数参数,因为编译器仍然需要对非contexexpr futcoriations contim at contexpr function进行评估。 compile time。 C 20引入恒定函数,必须在编译时进行...
    编程 发布于2025-07-22
  • Spark DataFrame添加常量列的妙招
    Spark DataFrame添加常量列的妙招
    在Spark Dataframe ,将常数列添加到Spark DataFrame,该列具有适用于所有行的任意值的Spark DataFrame,可以通过多种方式实现。使用文字值(SPARK 1.3)在尝试提供直接值时,用于此问题时,旨在为此目的的使用column方法可能会导致错误。 df.with...
    编程 发布于2025-07-22
  • 为什么在我的Linux服务器上安装Archive_Zip后,我找不到“ class \” class \'ziparchive \'错误?
    为什么在我的Linux服务器上安装Archive_Zip后,我找不到“ class \” class \'ziparchive \'错误?
    class'ziparchive'在Linux Server上安装Archive_zip时找不到错误 commant in lin ins in cland ins in lin.11 on a lin.1 in a lin.11错误:致命错误:在... cass中找不到类z...
    编程 发布于2025-07-22
  • 如何避免Go语言切片时的内存泄漏?
    如何避免Go语言切片时的内存泄漏?
    ,a [j:] ...虽然通常有效,但如果使用指针,可能会导致内存泄漏。这是因为原始的备份阵列保持完整,这意味着新切片外部指针引用的任何对象仍然可能占据内存。 copy(a [i:] 对于k,n:= len(a)-j i,len(a); k
    编程 发布于2025-07-22
  • eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    称量()和ast.literal_eval()中的Python Security 在使用用户输入时,必须优先确保安全性。强大的Python功能Eval()通常是作为潜在解决方案而出现的,但担心其潜在风险。 This article delves into the differences betwee...
    编程 发布于2025-07-22
  • 如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    将pandas dataframe列转换为dateTime格式示例:使用column(mycol)包含以下格式的以下dataframe,以自定义格式:})指定的格式参数匹配给定的字符串格式。转换后,MyCol列现在将包含DateTime对象。 date oped filtering > = p...
    编程 发布于2025-07-22
  • 切换到MySQLi后CodeIgniter连接MySQL数据库失败原因
    切换到MySQLi后CodeIgniter连接MySQL数据库失败原因
    Unable to Connect to MySQL Database: Troubleshooting Error MessageWhen attempting to switch from the MySQL driver to the MySQLi driver in CodeIgniter,...
    编程 发布于2025-07-22
  • Python中何时用"try"而非"if"检测变量值?
    Python中何时用"try"而非"if"检测变量值?
    使用“ try“ vs.” if”来测试python 在python中的变量值,在某些情况下,您可能需要在处理之前检查变量是否具有值。在使用“如果”或“ try”构建体之间决定。“ if” constructs result = function() 如果结果: 对于结果: ...
    编程 发布于2025-07-22
  • 在UTF8 MySQL表中正确将Latin1字符转换为UTF8的方法
    在UTF8 MySQL表中正确将Latin1字符转换为UTF8的方法
    在UTF8表中将latin1字符转换为utf8 ,您遇到了一个问题,其中含义的字符(例如,“jáuòiñe”)在utf8 table tabled tablesset中被extect(例如,“致电。为了解决此问题,您正在尝试使用“ mb_convert_encoding”和“ iconv”转换受...
    编程 发布于2025-07-22
  • FastAPI自定义404页面创建指南
    FastAPI自定义404页面创建指南
    response = await call_next(request) if response.status_code == 404: return RedirectResponse("https://fastapi.tiangolo.com") else: ...
    编程 发布于2025-07-22
  • Java中假唤醒真的会发生吗?
    Java中假唤醒真的会发生吗?
    在Java中的浪费唤醒:真实性或神话?在Java同步中伪装唤醒的概念已经是讨论的主题。尽管存在这种行为的潜力,但问题仍然存在:它们实际上是在实践中发生的吗? Linux的唤醒机制根据Wikipedia关于伪造唤醒的文章,linux实现了pthread_cond_wait()功能的Linux实现,利用...
    编程 发布于2025-07-22
  • 为什么不使用CSS`content'属性显示图像?
    为什么不使用CSS`content'属性显示图像?
    在Firefox extemers属性为某些图像很大,&& && && &&华倍华倍[华氏华倍华氏度]很少见,却是某些浏览属性很少,尤其是特定于Firefox的某些浏览器未能在使用内容属性引用时未能显示图像的情况。这可以在提供的CSS类中看到:。googlepic { 内容:url(&#...
    编程 发布于2025-07-22
  • CSS可以根据任何属性值来定位HTML元素吗?
    CSS可以根据任何属性值来定位HTML元素吗?
    靶向HTML元素,在CSS 但是,出现一个常见的问题:元素可以根据任何属性值而定位吗?本文探讨了此主题。的目标元素有任何任何属性值,属性selector可以使用。通过省略属性名称之后的值,可以选择具有该属性的所有元素。例如:此行为反映默认的光标:使用定义的Href属性的标记的指针样式。非空属性值。...
    编程 发布于2025-07-22
  • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    postgresql:为每个唯一标识符在postgresql中提取最后一行,您可能需要遇到与数据集合中每个不同标识的信息相关的信息。考虑以下数据:[ 1 2014-02-01 kjkj 在数据集中的每个唯一ID中检索最后一行的信息,您可以在操作员上使用Postgres的有效效率: id dat...
    编程 发布于2025-07-22
  • 如何实时捕获和流媒体以进行聊天机器人命令执行?
    如何实时捕获和流媒体以进行聊天机器人命令执行?
    在开发能够执行命令的chatbots的领域中,实时从命令执行实时捕获Stdout,一个常见的需求是能够检索和显示标准输出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    编程 发布于2025-07-22

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3