Skip to content

Instantly share code, notes, and snippets.

@xylcbd
Created January 9, 2022 10:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xylcbd/cbb7608e28254ca43470a2961ac4f784 to your computer and use it in GitHub Desktop.
Save xylcbd/cbb7608e28254ca43470a2961ac4f784 to your computer and use it in GitHub Desktop.
# coding: utf-8
import sys
import uuid
import json
import traceback
import winreg
from PyQt5.QtCore import QFile, QTimer, Qt, QDateTime, QSettings, QPoint, QSize
from PyQt5.QtWidgets import QApplication, QMainWindow, QGroupBox, QLineEdit, QTextEdit, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QDateTimeEdit, QMenuBar, QAction
class TimerAPP(QMainWindow):
def __init__(self):
super(TimerAPP, self).__init__()
self.timers = []
self.settings = QSettings('TimerAPP', 'user_settings')
self.init_UI()
# 计时器,刷新当前时间,以及按秒计时
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.start()
self.timer.timeout.connect(self.onTimerOut)
def init_UI(self):
self.setWindowTitle('计时器 -- 神气@知乎')
self.setMinimumWidth(240)
# 计时器详情
self.layout = QVBoxLayout()
datas = json.loads(self.settings.value('timers', '[]'))
print(f'[init_UI] load datas: {datas}')
for obj in datas:
label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame = self.add_timer()
obj_name = str(uuid.uuid4())
del_timer_btn.setObjectName(obj_name)
date_target.setDateTime(QDateTime.fromString(obj['timer_date'], 'yyyy-MM-dd HH:mm:ss'))
edit_note.setText(obj['timer_note'])
self.timers.append([obj_name, label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame])
if len(self.timers) <= 0:
self.setMinimumSize(320, 320)
# 菜单
self.menubar = QMenuBar(self)
self.menu = self.menubar.addMenu('菜单')
############## 窗口模式:置顶/普通 #############
menu_window_on_top = self.menu.addMenu('窗口模式')
menu_window_on_top.addAction('置顶')
menu_window_on_top.addAction('普通')
menu_window_on_top.triggered[QAction].connect(self.onWindowOnTop)
self.window_on_top = self.settings.value('window_on_top', 'normal')
print(f'[init_UI] load window_on_top: {self.window_on_top}')
self.switch_window_on_top(self.window_on_top)
############## 开机启动:是/否 ##############
self.menu.addSeparator()
menu_run_on_boot = self.menu.addMenu('开机启动')
menu_run_on_boot.addAction('是')
menu_run_on_boot.addAction('否')
menu_run_on_boot.triggered[QAction].connect(self.onRunOnBoot)
self.run_on_boot = self.settings.value('run_on_boot', 'no')
print(f'[init_UI] load run_on_boot: {self.run_on_boot}')
self.switch_run_on_boot(self.run_on_boot)
############## UI模式:只读/编辑 #############
self.menu.addSeparator()
menu_ui_mode = self.menu.addMenu('显示模式')
menu_ui_mode.addAction('编辑')
menu_ui_mode.addAction('只读')
menu_ui_mode.triggered[QAction].connect(self.onSwitchUIMode)
self.ui_mode = self.settings.value('ui_mode', 'edit')
print(f'[init_UI] load ui_mode: {self.ui_mode}')
############## 新增计时器 #############
self.menu.addSeparator()
self.add_new_timer = self.menu.addAction('新增计时器')
self.add_new_timer.triggered.connect(self.onAddTimer)
self.switch_ui_mode(self.ui_mode)
# 其他
app.aboutToQuit.connect(self.onCloseEvent)
wid = QWidget(self)
self.setMenuBar(self.menubar)
self.setCentralWidget(wid)
wid.setLayout(self.layout)
# 窗口位置
pos = self.settings.value('pos', QPoint(200, 200))
size = self.settings.value('size', QSize(400, 400))
self.resize(size)
self.move(pos)
########################## UI模式:只读/编辑 ##########################
def switch_ui_mode(self, ui_mode):
for obj_name, label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame in self.timers:
if ui_mode == 'edit':
label_target.setVisible(True)
date_target.setVisible(True)
edit_note.setEnabled(True)
del_timer_btn.setVisible(True)
label_diff.setVisible(False)
edit_diff.setVisible(False)
self.add_new_timer.setEnabled(True)
elif ui_mode == 'readonly':
label_target.setVisible(False)
date_target.setVisible(False)
edit_note.setEnabled(False)
del_timer_btn.setVisible(False)
label_diff.setVisible(True)
edit_diff.setVisible(True)
self.add_new_timer.setEnabled(False)
else:
assert False
def onSwitchUIMode(self, action):
if action.text() == '编辑':
self.ui_mode = 'edit'
elif action.text() == '只读':
self.ui_mode = 'readonly'
else:
assert False
print(f'[onSwitchUIMode] switched ui_mode: {self.ui_mode}')
self.switch_ui_mode(self.ui_mode)
self.save_settings()
########################## 窗口模式:置顶/普通 ##########################
def switch_window_on_top(self, window_on_top):
if window_on_top == 'top':
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
self.show()
else:
self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
self.show()
def onWindowOnTop(self, action):
if action.text() == '置顶':
self.window_on_top = 'top'
else:
self.window_on_top = 'normal'
print(f'[onWindowOnTop] switched window_on_top: {self.window_on_top}')
self.switch_window_on_top(self.window_on_top)
self.save_settings()
########################## 开机启动:是/否 ##########################
def switch_run_on_boot(self, run_on_boot):
try:
f = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Run', 0, winreg.KEY_ALL_ACCESS)
run_path = sys.argv[0]
if run_on_boot == 'yes':
winreg.SetValueEx(f, 'TimerAPP', 0, winreg.REG_SZ, run_path)
print(f'[onRunOnBoot] add path: {run_path}')
else:
winreg.DeleteValue(f, 'TimerAPP')
print(f'[onRunOnBoot] remove path: {run_path}')
winreg.CloseKey(f)
except:
print(f'[switch_run_on_boot] except: {traceback.format_exc()}')
def onRunOnBoot(self, action):
if action.text() == '是':
self.run_on_boot = 'yes'
else:
self.run_on_boot = 'no'
print(f'[onRunOnBoot] switched run_on_boot: {self.run_on_boot}')
self.switch_run_on_boot(self.run_on_boot)
self.save_settings()
########################## 工具函数:时间差等 ##########################
def diff_time(self, from_time, to_time):
year_diff = to_time.date().year() - from_time.date().year()
month_diff = to_time.date().month() - from_time.date().month()
if month_diff < 0:
year_diff -= 1
month_diff += 12
total_day_diff = from_time.daysTo(to_time)
day_diff = to_time.date().day() - from_time.date().day()
if day_diff < 0:
month_diff -= 1
if month_diff < 0:
year_diff -= 1
month_diff += 12
day_diff += from_time.date().daysInMonth()
sec_diff = (from_time.msecsTo(to_time) // (1000)) % 60
min_diff = (from_time.msecsTo(to_time) // (60 * 1000)) % 60
hour_diff = (from_time.msecsTo(to_time) // (60 * 60 * 1000)) % 24
return year_diff, month_diff, total_day_diff, day_diff, hour_diff, min_diff, sec_diff
def diff2str(self, prefix, diff):
year_diff, month_diff, total_day_diff, day_diff, hour_diff, min_diff, sec_diff = diff
msg = f'{prefix}(大约): {year_diff}年{month_diff}月{day_diff}天'
msg += '\n'
msg += f'{prefix}(精确): {total_day_diff}天{hour_diff}时{min_diff}分{sec_diff}秒'
return msg
########################## 计时器回调 ##########################
def onTimerOut(self):
current_datetime = QDateTime.currentDateTime()
for obj_name, label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame in self.timers:
if current_datetime >= date_target.dateTime():
diff = self.diff_time(date_target.dateTime(), current_datetime)
msg = self.diff2str('已过去', diff)
edit_diff.setText(msg)
else:
diff = self.diff_time(current_datetime, date_target.dateTime())
msg = self.diff2str('还剩余', diff)
edit_diff.setText(msg)
########################## 新增计时器 ##########################
def add_timer(self):
timer_layout = QVBoxLayout()
# 目标时间
row_layout = QHBoxLayout()
label_target = QLabel('目标时间: ', self)
row_layout.addWidget(label_target)
date_target = QDateTimeEdit(QDateTime.currentDateTime(), self)
date_target.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
date_target.setCalendarPopup(True)
date_target.setFocus()
row_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
row_layout.addWidget(date_target)
timer_layout.addLayout(row_layout)
# 计时备注
row_layout = QHBoxLayout()
label_note = QLabel('目标备注: ', self)
row_layout.addWidget(label_note)
edit_note = QLineEdit(self)
edit_note.setPlaceholderText('计时器的备注')
row_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
row_layout.addWidget(edit_note)
timer_layout.addLayout(row_layout)
# 时间差-标签
row_layout = QHBoxLayout()
label_diff = QLabel('时间差异: ', self)
row_layout.addWidget(label_diff)
edit_diff = QLabel('...')
edit_diff.setEnabled(False)
row_layout.addWidget(edit_diff)
row_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
timer_layout.addLayout(row_layout)
# 删除计时-按钮
row_layout = QHBoxLayout()
del_timer_btn = QPushButton('删除计时')
del_timer_btn.clicked.connect(self.onDelTimerButtonClick)
row_layout.addWidget(del_timer_btn)
row_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
timer_layout.addLayout(row_layout)
timer_frame = QGroupBox(self)
if len(self.timers) % 2 == 0:
timer_frame.setStyleSheet('QGroupBox { border: 2px solid blue;}')
else:
timer_frame.setStyleSheet('QGroupBox { border: 2px solid green;}')
timer_frame.setLayout(timer_layout)
self.layout.addWidget(timer_frame)
return label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame
def onAddTimer(self, action):
label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame = self.add_timer()
obj_name = str(uuid.uuid4())
del_timer_btn.setObjectName(obj_name)
self.timers.append([obj_name, label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame])
print(f'[onAddTimer] add timer: {self.timers[-1]}')
self.switch_ui_mode(self.ui_mode)
self.save_settings()
########################## 删除计时器 ##########################
def clear_layout(self, layout):
while layout.count():
child = layout.takeAt(0)
if child.widget() is not None:
child.widget().deleteLater()
elif child.layout() is not None:
self.clear_layout(child.layout())
def onDelTimerButtonClick(self):
sender_obj_name = self.sender().objectName()
for i, (obj_name, label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame) in enumerate(self.timers):
if sender_obj_name == obj_name:
self.clear_layout(timer_frame.layout())
self.layout.removeWidget(timer_frame)
print(f'[onDelTimerButtonClick] remove timer: {self.timers[i]}')
del self.timers[i]
break
self.save_settings()
########################## 退出程序:保存所有设置 ##########################
def save_settings(self):
# 保存计时器
root = []
for i, (obj_name, label_target, date_target, label_note, edit_note, del_timer_btn, label_diff, edit_diff, timer_frame) in enumerate(self.timers):
root.append({
'timer_date': date_target.dateTime().toString('yyyy-MM-dd HH:mm:ss'),
'timer_note': edit_note.text()
})
self.settings.setValue('timers', json.dumps(root, indent=4, ensure_ascii=False))
self.settings.setValue('ui_mode', self.ui_mode)
self.settings.setValue('window_on_top', self.window_on_top)
self.settings.setValue('run_on_boot', self.run_on_boot)
self.settings.setValue('pos', self.pos())
self.settings.setValue('size', self.size())
def onCloseEvent(self):
self.save_settings()
print(f'[onCloseEvent] saving settings: {self.settings}')
if __name__ == '__main__':
app = QApplication(sys.argv)
page = TimerAPP()
page.show()
sys.exit(app.exec_())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment