Python-基于PyQt5,json和playsound的通用闹钟
前言:刚刚结束2024年秋季学期的学习,接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装,配置。所以接下来我们一起深入PyQt5,学习如何利用PyQt5进行实际开发-基于PyQt5,json和playsound的通用闹钟。本次编程我们将会调用Python中的第三方库(如PyQt5和playsound),大家需要提前下再好。此外我们也将会利用到Python的众多标准库实现整个程序的正常运行(如datetime,json,warning,sys等)。好,话不多说,我们直接开始今天的学习。
第一步:导入库
我们需要sys,以便我们对PyCharm相关的操作和变量的访问。json,它提供了对JSON数据的编码和解码功能。接着我们导入标准库datetime。datetime类用于处理日期和时间(这个在后面的非重复,单日还是单周,月闹钟提醒设置里面非常重要),timedelta类用于表示时间间隔(这个为实现倒计时和设置计时功能提供了可能)。接下来是PyQt5的相关类和模块:QTimer类用于创建定时器;QTime类用于处理时间;QtWidgets模块包含了所有的GUI组件,如按钮、标签、文本框等;QIcon类用于创建图标。我们导入playsound函数用来播放音频文件(mp3格式的音频文件)。最后我们导入Python的标准库warnings来提供了对警告信息的控制(这里主要是我在后续调试代码过程中老是出现一些无关紧要的控制台警告,干脆保持静默得了)。
#导入必要库
import sys
import json
from datetime import datetime, timedelta
from PyQt5.QtCore import Qt, QTimer, QTime
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon
from playsound import playsound
import warnings
第二步:静默警告信息(禁用部分函数)
采用忽略警告的方式静默警告。
warnings.filterwarnings("ignore", category=DeprecationWarning)
第三步:搭建环境并创建闹钟类
这里我们需要保证解释器安装有必要的Python环境:1,PyQt5,playsound等。2, 准备铃声文件:alarm.mp3(这里我给大家介绍一款可录屏,录音的免费软件ocam,我的mp3文件就是用ocam制作的)。3,准备图标文件:alarm.png(这个也需要大家自行准备,png格式的闹钟图片,示例如下)

#闹钟类
class AdvancedAlarmClock(QMainWindow):def __init__(self):super().__init__()self.alarms = []self.timers = []self.current_alarm = Noneself.snooze_time = 5self.initUI()self.load_alarms()def initUI(self):self.setWindowTitle('高级闹钟')self.setGeometry(300, 300, 216, 286)self.setWindowIcon(QIcon('alarm.png'))# 主控件main_widget = QWidget()self.setCentralWidget(main_widget)layout = QVBoxLayout()# 闹钟设置区域alarm_setting = QGroupBox("新建闹钟")alarm_layout = QHBoxLayout()self.time_edit = QTimeEdit()self.time_edit.setDisplayFormat("HH:mm")alarm_layout.addWidget(self.time_edit)self.repeat_combo = QComboBox()self.repeat_combo.addItems(["不重复", "每天", "工作日", "周末", "自定义..."])alarm_layout.addWidget(self.repeat_combo)self.sound_combo = QComboBox()self.sound_combo.addItems(["默认铃声", "铃声1", "铃声2"])alarm_layout.addWidget(self.sound_combo)add_btn = QPushButton("添加闹钟")add_btn.clicked.connect(self.add_alarm)alarm_layout.addWidget(add_btn)alarm_setting.setLayout(alarm_layout)layout.addWidget(alarm_setting)# 闹钟列表self.alarm_list = QListWidget()layout.addWidget(self.alarm_list)# 控制按钮control_layout = QHBoxLayout()del_btn = QPushButton("删除闹钟")del_btn.clicked.connect(self.delete_alarm)control_layout.addWidget(del_btn)snooze_btn = QPushButton("贪睡 (%d分钟)" % self.snooze_time)snooze_btn.clicked.connect(self.snooze_alarm)control_layout.addWidget(snooze_btn)layout.addLayout(control_layout)# 倒计时和计时器timer_group = QGroupBox("计时功能")timer_layout = QHBoxLayout()self.countdown_spin = QSpinBox()self.countdown_spin.setRange(1, 120)self.countdown_spin.setSuffix(" 分钟")timer_layout.addWidget(self.countdown_spin)countdown_btn = QPushButton("开始倒计时")countdown_btn.clicked.connect(self.start_countdown)timer_layout.addWidget(countdown_btn)self.timer_label = QLabel("00:00:00")timer_layout.addWidget(self.timer_label)timer_btn = QPushButton("启动计时器")timer_btn.clicked.connect(self.start_timer)timer_layout.addWidget(timer_btn)timer_group.setLayout(timer_layout)layout.addWidget(timer_group)main_widget.setLayout(layout)# 定时检查闹钟self.check_timer = QTimer()self.check_timer.timeout.connect(self.check_alarms)self.check_timer.start(1000) # 每秒检查一次def add_alarm(self):alarm_time = self.time_edit.time().toString("HH:mm")repeat_mode = self.repeat_combo.currentText()sound = self.sound_combo.currentText()alarm = {"time": alarm_time,"repeat": repeat_mode,"sound": sound,"enabled": True}self.alarms.append(alarm)self.update_alarm_list()self.save_alarms()def delete_alarm(self):selected = self.alarm_list.currentRow()if selected >= 0:del self.alarms[selected]self.update_alarm_list()self.save_alarms()def update_alarm_list(self):self.alarm_list.clear()for alarm in self.alarms:status = "✓" if alarm["enabled"] else "✗"item = QListWidgetItem(f"{alarm['time']} | {alarm['repeat']} | {alarm['sound']} {status}")self.alarm_list.addItem(item)def check_alarms(self):now = datetime.now().strftime("%H:%M")for alarm in self.alarms:if alarm["enabled"] and alarm["time"] == now:self.trigger_alarm(alarm)def trigger_alarm(self, alarm):self.current_alarm = alarmalarm["enabled"] = False# 播放声音try:playsound('alarm.mp3')except:pass# 显示窗口msg = QMessageBox()msg.setWindowTitle("闹钟提醒")msg.setText(f"时间到!当前时间 {alarm['time']}")msg.setStandardButtons(QMessageBox.Ok)msg.exec_()self.update_alarm_list()self.save_alarms()def snooze_alarm(self):if self.current_alarm:snooze_time = datetime.now() + timedelta(minutes=self.snooze_time)self.alarms.append({"time": snooze_time.strftime("%H:%M"),"repeat": "不重复","sound": self.current_alarm["sound"],"enabled": True})self.update_alarm_list()self.save_alarms()def start_countdown(self):minutes = self.countdown_spin.value()end_time = datetime.now() + timedelta(minutes=minutes)timer = QTimer()timer.timeout.connect(lambda: self.update_countdown(timer, end_time))timer.start(1000)self.timers.append(timer)def update_countdown(self, timer, end_time):remaining = end_time - datetime.now()if remaining.total_seconds() <= 0:timer.stop()self.timer_label.setText("00:00:00")playsound('alarm.mp3')else:self.timer_label.setText(str(remaining).split('.')[0])def start_timer(self):self.timer_start_time = datetime.now()timer = QTimer()timer.timeout.connect(self.update_timer)timer.start(1000)self.timers.append(timer)def update_timer(self):elapsed = datetime.now() - self.timer_start_timeself.timer_label.setText(str(elapsed).split('.')[0])def save_alarms(self):with open("alarms.json", "w") as f:json.dump(self.alarms, f)def load_alarms(self):try:with open("alarms.json", "r") as f:self.alarms = json.load(f)self.update_alarm_list()except:pass
第四步:创建驱动单元
最后,我们将会用一个初始化单元来驱动整个程序运行。
#驱动单元
if __name__ == '__main__':app = QApplication(sys.argv)clock = AdvancedAlarmClock()clock.show()sys.exit(app.exec_())
第五步:完整代码展示
#导入必要库
import sys
import json
from datetime import datetime, timedelta
from PyQt5.QtCore import Qt, QTimer, QTime
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon
from playsound import playsound
import warningswarnings.filterwarnings("ignore", category=DeprecationWarning)#闹钟类
class AdvancedAlarmClock(QMainWindow):def __init__(self):super().__init__()self.alarms = []self.timers = []self.current_alarm = Noneself.snooze_time = 5self.initUI()self.load_alarms()def initUI(self):self.setWindowTitle('高级闹钟')self.setGeometry(300, 300, 216, 286)self.setWindowIcon(QIcon('alarm.png'))# 主控件main_widget = QWidget()self.setCentralWidget(main_widget)layout = QVBoxLayout()# 闹钟设置区域alarm_setting = QGroupBox("新建闹钟")alarm_layout = QHBoxLayout()self.time_edit = QTimeEdit()self.time_edit.setDisplayFormat("HH:mm")alarm_layout.addWidget(self.time_edit)self.repeat_combo = QComboBox()self.repeat_combo.addItems(["不重复", "每天", "工作日", "周末", "自定义..."])alarm_layout.addWidget(self.repeat_combo)self.sound_combo = QComboBox()self.sound_combo.addItems(["默认铃声", "铃声1", "铃声2"])alarm_layout.addWidget(self.sound_combo)add_btn = QPushButton("添加闹钟")add_btn.clicked.connect(self.add_alarm)alarm_layout.addWidget(add_btn)alarm_setting.setLayout(alarm_layout)layout.addWidget(alarm_setting)# 闹钟列表self.alarm_list = QListWidget()layout.addWidget(self.alarm_list)# 控制按钮control_layout = QHBoxLayout()del_btn = QPushButton("删除闹钟")del_btn.clicked.connect(self.delete_alarm)control_layout.addWidget(del_btn)snooze_btn = QPushButton("贪睡 (%d分钟)" % self.snooze_time)snooze_btn.clicked.connect(self.snooze_alarm)control_layout.addWidget(snooze_btn)layout.addLayout(control_layout)# 倒计时和计时器timer_group = QGroupBox("计时功能")timer_layout = QHBoxLayout()self.countdown_spin = QSpinBox()self.countdown_spin.setRange(1, 120)self.countdown_spin.setSuffix(" 分钟")timer_layout.addWidget(self.countdown_spin)countdown_btn = QPushButton("开始倒计时")countdown_btn.clicked.connect(self.start_countdown)timer_layout.addWidget(countdown_btn)self.timer_label = QLabel("00:00:00")timer_layout.addWidget(self.timer_label)timer_btn = QPushButton("启动计时器")timer_btn.clicked.connect(self.start_timer)timer_layout.addWidget(timer_btn)timer_group.setLayout(timer_layout)layout.addWidget(timer_group)main_widget.setLayout(layout)# 定时检查闹钟self.check_timer = QTimer()self.check_timer.timeout.connect(self.check_alarms)self.check_timer.start(1000) # 每秒检查一次def add_alarm(self):alarm_time = self.time_edit.time().toString("HH:mm")repeat_mode = self.repeat_combo.currentText()sound = self.sound_combo.currentText()alarm = {"time": alarm_time,"repeat": repeat_mode,"sound": sound,"enabled": True}self.alarms.append(alarm)self.update_alarm_list()self.save_alarms()def delete_alarm(self):selected = self.alarm_list.currentRow()if selected >= 0:del self.alarms[selected]self.update_alarm_list()self.save_alarms()def update_alarm_list(self):self.alarm_list.clear()for alarm in self.alarms:status = "✓" if alarm["enabled"] else "✗"item = QListWidgetItem(f"{alarm['time']} | {alarm['repeat']} | {alarm['sound']} {status}")self.alarm_list.addItem(item)def check_alarms(self):now = datetime.now().strftime("%H:%M")for alarm in self.alarms:if alarm["enabled"] and alarm["time"] == now:self.trigger_alarm(alarm)def trigger_alarm(self, alarm):self.current_alarm = alarmalarm["enabled"] = False# 播放声音try:playsound('alarm.mp3')except:pass# 显示窗口msg = QMessageBox()msg.setWindowTitle("闹钟提醒")msg.setText(f"时间到!当前时间 {alarm['time']}")msg.setStandardButtons(QMessageBox.Ok)msg.exec_()self.update_alarm_list()self.save_alarms()def snooze_alarm(self):if self.current_alarm:snooze_time = datetime.now() + timedelta(minutes=self.snooze_time)self.alarms.append({"time": snooze_time.strftime("%H:%M"),"repeat": "不重复","sound": self.current_alarm["sound"],"enabled": True})self.update_alarm_list()self.save_alarms()def start_countdown(self):minutes = self.countdown_spin.value()end_time = datetime.now() + timedelta(minutes=minutes)timer = QTimer()timer.timeout.connect(lambda: self.update_countdown(timer, end_time))timer.start(1000)self.timers.append(timer)def update_countdown(self, timer, end_time):remaining = end_time - datetime.now()if remaining.total_seconds() <= 0:timer.stop()self.timer_label.setText("00:00:00")playsound('alarm.mp3')else:self.timer_label.setText(str(remaining).split('.')[0])def start_timer(self):self.timer_start_time = datetime.now()timer = QTimer()timer.timeout.connect(self.update_timer)timer.start(1000)self.timers.append(timer)def update_timer(self):elapsed = datetime.now() - self.timer_start_timeself.timer_label.setText(str(elapsed).split('.')[0])def save_alarms(self):with open("alarms.json", "w") as f:json.dump(self.alarms, f)def load_alarms(self):try:with open("alarms.json", "r") as f:self.alarms = json.load(f)self.update_alarm_list()except:pass#驱动单元
if __name__ == '__main__':app = QApplication(sys.argv)clock = AdvancedAlarmClock()clock.show()sys.exit(app.exec_())
第六步:操作指南
1.设置新闹钟:
- 选择时间
- 设置重复模式
- 选择铃声
- 点击"添加闹钟"
2.管理闹钟:
- 双击列表项启用/禁用
- 选择后点击"删除闹钟"
- 响铃时点击"贪睡"延迟提醒
3.计时工具**:
- 倒计时:设置分钟数 → 开始倒计时
- 秒表:直接启动计时器
第七步:运行效果展示
正常状态:

最大化:

最小化:

相关文章:
Python-基于PyQt5,json和playsound的通用闹钟
前言:刚刚结束2024年秋季学期的学习,接下来我们继续来学习PyQt5。由于之前我们已经学习了PyQt5以及PyUIC,Pyrcc和QtDesigner的安装,配置。所以接下来我们一起深入PyQt5,学习如何利用PyQt5进行实际开发-基于PyQt5,json和…...
关于数字地DGND和模拟地AGND隔离
文章目录 前言一、1、为什么要进行数字地和模拟地隔离二、隔离元件1.①0Ω电阻:2.②磁珠:3.电容:4.④电感: 三、隔离方法①单点接地②数字地与模拟地分开布线,最后再PCB板上一点接到电源。③电源隔离④、其他隔离方法 …...
小识Java死锁是否会造成CPU100%?
死锁或者大量的死锁不一定会直接导致CPU占用率达到100%。以下是详细分析: 一、死锁对CPU的影响 资源占用:死锁是指两个或多个线程(或进程)在相互等待对方释放资源,导致所有涉及的线程都无法继续执行。在死锁状态下&a…...
DeepSeek R1学习
0.回顾: https://blog.csdn.net/Together_CZ/article/details/144431432?ops_request_misc%257B%2522request%255Fid%2522%253A%25226574a586f0850d0329fbb720e5b8d5a9%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id…...
激光线扫相机无2D图像的标定方案
方案一:基于运动控制平台的标定 适用场景:若激光线扫相机安装在可控运动平台(如机械臂、平移台、旋转台)上,且平台的运动精度已知(例如通过编码器或高精度步进电机控制)。 步骤: 标…...
12 款开源OCR发 PDF 识别框架
2024 年 12 款开源文档解析框架的选型对比评测:PDF解析、OCR识别功能解读、应用场景分析及优缺点比较 这是该系列的第二篇文章,聚焦于智能文档处理(特别是 PDF 解析)。无论是在模型预训练的数据收集阶段,还是基于 RAG…...
【反悔堆】【hard】力扣871. 最低加油次数
汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。 沿途有加油站,用数组 stations 表示。其中 stations[i] [positioni, fueli] 表示第 i 个加油站位于出发位置东面 positioni 英里处,并且有 fueli 升汽油。 假设汽车油…...
为什么应用程序是特定于操作系统的?[计算机原理]
你把WINDOWS程序复制到MAC上使用,会发现无法运行。你可能会说,MAC是arm处理器,而WINDWOS是X86 处理器。但是在2019年,那时候MAC电脑还全是Intel处理器,在同样的X86芯片上,运行MAC和WINDOWS 程序还是无法互相…...
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude 注:因为考虑到绝大部分人的使用,我这里所用的模型均为免费模型。官方可访问的。ChatGPT这里用的是4o Ai对话,编程一直以来都是人们所讨论的话题。Ai的出现…...
什么是循环神经网络?
一、概念 循环神经网络(Recurrent Neural Network, RNN)是一类用于处理序列数据的神经网络。与传统的前馈神经网络不同,RNN具有循环连接,可以利用序列数据的时间依赖性。正因如此,RNN在自然语言处理、时间序列预测、语…...
Flink运行时架构
一、系统架构 1)作业管理器(JobManager) JobManager是一个Flink集群中任务管理和调度的核心,是控制应用执行的主进程。也就是说,每个应用都应该被唯一的JobManager所控制执行。 JobManger又包含3个不同的组件。 &am…...
网络工程师 (6)操作系统概述
一、操作系统的定义 (一)基本定义 操作系统(Operating System,简称OS)是计算机系统中至关重要的基础性系统软件。它是计算机硬件与上层软件之间的桥梁,负责管理和控制整个计算机系统的硬件和软件资源&…...
【2025年数学建模美赛C题】第1-5问F奖解题思路+高级绘图+可运行代码
基于多模型分析的奥运会奖牌预测与影响因素研究 解题思路一、问题重述二、问题分析三、模型假设与符号说明四、数据预处理五、奖牌榜预测5.1 基于LSTM长短期记忆循环神经网络的预测模型的建立5.2 模型预测结果 六、首枚奖牌预测6.1 BP神经网络的建立6.2 模型预测结果 七、各国奖…...
StarRocks 安装部署
StarRocks 安装部署 StarRocks端口: 官方《配置检查》有服务端口详细描述: https://docs.starrocks.io/zh/docs/deployment/environment_configurations/ StarRocks架构:https://docs.starrocks.io/zh/docs/introduction/Architecture/ Sta…...
RoboMaster- RDK X5能量机关实现案例(一)识别
作者:SkyXZ CSDN:https://blog.csdn.net/xiongqi123123 博客园:https://www.cnblogs.com/SkyXZ 在RoboMaster的25赛季,我主要负责了能量机关的视觉方案开发,目前整体算法已经搭建完成,实际方案上我使用的上…...
llama.cpp LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK2
llama.cpp LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK2 1. LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK22. LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK23. struct ggml_cgraph * build_deepseek() and struct ggml_cgraph * build_deepseek2()References 不宜吹捧中国大语言模型的同…...
检测到联想鼠标自动调出运行窗口,鼠标自己作为键盘操作
联想鼠标会自动时不时的调用“运行”窗口 然后鼠标自己作为键盘输入 然后打开这个网页 (不是点击了什么鼠标外加按键,这个鼠标除了左右和中间滚轮,没有其他按键了)...
-bash: ./uninstall.command: /bin/sh^M: 坏的解释器: 没有那个文件或目录
终端报错: -bash: ./uninstall.command: /bin/sh^M: 坏的解释器: 没有那个文件或目录原因:由于文件行尾符不匹配导致的。当脚本文件在Windows环境中创建或编辑后,行尾符为CRLF(即回车和换行,\r\n)…...
15天基础内容总复习
总复习 一.day01内容 1.JVM,JRE,JDK的关系 JVM: java虚拟机,用来运行java程序的,JVM本身是不夸平台的,每个操作系统都需要安装针对本操作系统的JVM所以: java通过jvm的不夸平台实现了java的跨平台JRE:java运行环境,包含jvm和核心类库JDK:java开发工具包,包含开发工具和JRE三…...
星火大模型接入及文本生成HTTP流式、非流式接口(JAVA)
文章目录 一、接入星火大模型二、基于JAVA实现HTTP非流式接口1.配置2.接口实现(1)分析接口请求(2)代码实现 3.功能测试(1)测试对话功能(2)测试记住上下文功能 三、基于JAVA实现HTTP流…...
如何将电脑桌面默认的C盘设置到D盘?详细操作步骤!
将电脑桌面默认的C盘设置到D盘的详细操作步骤! 本博文介绍如何将电脑桌面(默认为C盘)设置在D盘下。 首先,在D盘建立文件夹Desktop,完整的路径为D:\Desktop。winR,输入Regedit命令。(或者单击【…...
toRow和markRow的用法以及使用场景
Vue3 Raw API 完整指南 1. toRaw vs markRaw 1.1 基本概念 toRaw: 返回响应式对象的原始对象,用于临时获取原始数据结构,标记过后将会失去响应式markRaw: 标记一个对象永远不会转换为响应式对象,返回对象本身 1.2 使用对比 // toRaw 示例…...
Java中ExecutorService接口介绍、应用场景和示例代码
概述 ExecutorService 是 Java 中用于管理线程池的接口,它属于 java.util.concurrent 包。它提供了用于管理并发任务的功能,包括任务的提交、执行和线程池的生命周期管理。以下是对 ExecutorService 的详细讲解、应用场景和示例代码。 1. 详细讲解 1.…...
java 判断Date是上午还是下午
我要用Java生成表格统计信息,如下图所示: 所以就诞生了本文的内容。 在 Java 里,判断 Date 对象代表的时间是上午还是下午有多种方式,下面为你详细介绍不同的实现方法。 方式一:使用 java.util.Calendar Calendar 类…...
开源 CSS 框架 Tailwind CSS v4.0
开源 CSS 框架 Tailwind CSS v4.0 于 1 月 22 日正式发布,除了显著提升性能、简化配置体验外,还增强了功能特性,具体如下1: 性能提升 采用全新的高性能引擎 Oxide,带来了构建速度的巨大飞跃: 全量构建速度…...
微信小程序中实现进入页面时数字跳动效果(自定义animate-numbers组件)
微信小程序中实现进入页面时数字跳动效果 1. 组件定义,新建animate-numbers组件1.1 index.js1.2 wxml1.3 wxss 2. 使用组件 1. 组件定义,新建animate-numbers组件 1.1 index.js // components/animate-numbers/index.js Component({properties: {number: {type: Number,value…...
Kafka生产者ACK参数与同步复制
目录 生产者的ACK参数 ack等于0 ack等于1(默认) ack等于-1或all Kafka的同步复制 使用误区 生产者的ACK参数 Kafka的ack机制可以保证生产者发送的消息被broker接收成功。 Kafka producer有三种ack机制 ,分别是 0,1…...
C语言------数组从入门到精通
1.一维数组 目标:通过思维导图了解学习一维数组的核心知识点: 1.1定义 使用 类型名 数组名[数组长度]; 定义数组。 // 示例: int arr[5]; 1.2一维数组初始化 数组的初始化可以分为静态初始化和动态初始化两种方式。 它们的主要区别在于初始化的时机和内存分配的方…...
FLTK - FLTK1.4.1 - 搭建模板,将FLTK自带的实现搬过来做实验
文章目录 FLTK - FLTK1.4.1 - 搭建模板,将FLTK自带的实现搬过来做实验概述笔记my_fltk_test.cppfltk_test.hfltk_test.cxx用adjuster工程试了一下,好使。END FLTK - FLTK1.4.1 - 搭建模板,将FLTK自带的实现搬过来做实验 概述 用fluid搭建UI…...
postgres基准测试工具pgbench如何使用自定义的表结构和自定义sql
使用 pgbench 进行 PostgreSQL 性能测试时,可以自定义表结构和测试脚本来更好地模拟实际使用场景。以下是一个示例,说明如何自定义表结构和测试脚本。 自定义表结构 创建自定义表结构的 SQL 脚本。例如,创建一个名为 custom_schema.sql 的文…...
