基于wxpython的时钟小工具

前言


基于python3.10 + wxpython 的时钟小工具

代码由chatgpt3.5生成,作者自己调试。留作后续参考。

正文


timer_ok.py

import wx
import time
import threading
import os

class ClockCountdownPanel(wx.Panel):
    def __init__(self, parent):
        super(ClockCountdownPanel, self).__init__(parent)
        
        self.background = None
        self.is_cancle = True
        
        self.clock_label = wx.StaticText(self, label="0000-00-00 00:00:00")
        self.clock_label.SetBackgroundColour(wx.Colour(135, 206, 250, 128))  # 设置背景颜色和透明度
        clock_font = wx.Font(24, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
        self.clock_label.SetFont(clock_font)
        
        self.countdown_label = wx.StaticText(self, label="00:00:00")
        self.countdown_label.SetBackgroundColour(wx.Colour(255, 182, 193, 30))
        countdown_font = wx.Font(18, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
        self.countdown_label.SetFont(countdown_font)
        
        self.countdown_text = wx.TextCtrl(self, value="00:00:00")
        self.start_button = wx.Button(self, label="Start")
        # self.start_button.SetBackgroundColour(wx.Colour(144, 238, 144, 128))
        self.cancel_button = wx.Button(self, label="Cancel")
        # self.cancel_button.SetBackgroundColour(wx.Colour(255,255,193,128))
        
        self.start_button.Bind(wx.EVT_BUTTON, self.start_countdown)
        self.cancel_button.Bind(wx.EVT_BUTTON, self.cancel_countdown)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        clock_sizer = wx.BoxSizer(wx.HORIZONTAL)
        clock_sizer.AddStretchSpacer()
        clock_sizer.Add(self.clock_label, flag=wx.ALIGN_CENTER_VERTICAL, proportion=1)
        clock_sizer.AddStretchSpacer()
        sizer.Add(clock_sizer, flag=wx.EXPAND|wx.ALL, border=10, proportion=1)
        
        sizer.Add(wx.StaticLine(self), flag=wx.EXPAND|wx.ALL, border=10)
        
        countdown_sizer = wx.BoxSizer(wx.VERTICAL)
        countdown_sizer.Add(self.countdown_label, flag=wx.ALIGN_CENTER, proportion=1)
        countdown_sizer.Add(self.countdown_text, flag=wx.ALIGN_CENTER|wx.ALL, border=10, proportion=1)
        
        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
        buttons_sizer.Add(self.start_button, flag=wx.ALIGN_CENTER|wx.ALL, border=10)
        buttons_sizer.Add(self.cancel_button, flag=wx.ALIGN_CENTER|wx.ALL, border=10)
        
        countdown_sizer.Add(buttons_sizer, flag=wx.ALIGN_CENTER, proportion=2)
        
        sizer.Add(countdown_sizer, flag=wx.EXPAND|wx.ALL, border=10, proportion=2)
        
        self.SetSizer(sizer)
        
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update_clock, self.timer)
        self.timer.Start(1000)  # 每隔一秒钟更新一次时钟
    
    def update_clock(self, event):
        current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        self.clock_label.SetLabel(current_time)
    
    def start_countdown(self, event):
        self.is_cancle = False
        countdown_time = self.countdown_text.GetValue()
        countdown_thread = threading.Thread(target=self.run_countdown, args=(countdown_time,))
        countdown_thread.start()
    
    def cancel_countdown(self, event):
        # self.timer.Stop()
        self.is_cancle = True
        self.countdown_label.SetLabel("00:00:00")
    
    def run_countdown(self, countdown_time):
        hours, minutes, seconds = countdown_time.split(":")
        total_seconds = int(hours) * 3600 + int(minutes) * 60 + int(seconds)
        
        while self.is_cancle is False and total_seconds >= 0:
            hours = total_seconds // 3600
            minutes = (total_seconds % 3600) // 60
            seconds = total_seconds % 60
            countdown_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
            self.countdown_label.SetLabel(countdown_str)
            total_seconds -= 1
            wx.MilliSleep(1000)  # 等待一秒钟
        
        wx.MessageBox("倒计时结束了, 休息一下吧")
    
    def set_background(self, image_path):
        if os.path.exists(image_path):
            image = wx.Image(image_path, wx.BITMAP_TYPE_ANY)
            image = image.Scale(self.GetSize().GetWidth(), self.GetSize().GetHeight())
            self.background = wx.Bitmap(image)
            self.Refresh()
        else:
            wx.MessageBox("未找到背景图片: 背景图片.png")
    
    def paint_background(self, event):
        if self.background:
            dc = wx.BufferedPaintDC(self)
            dc.DrawBitmap(self.background, 0, 0)

class MainFrame(wx.Frame):
    def __init__(self, *args, **kw):
        super(MainFrame, self).__init__(*args, **kw)
        # 禁止最大化和最小化图标
        self.SetWindowStyle(wx.DEFAULT_FRAME_STYLE & ~(wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX))
        
        self.panel = ClockCountdownPanel(self)
        
        self.panel.Bind(wx.EVT_PAINT, self.panel.paint_background)
        self.Bind(wx.EVT_SIZING, self.on_size)
        
        self.SetSize((640, 360))
        self.SetTitle("小工具")
        self.Centre()
        self.panel.set_background("背景图片.png")
    
    def on_size(self, event):
        self.panel.set_background("背景图片.png")
        
if __name__ == '__main__':
    app = wx.App()
    frame = MainFrame(None)
    frame.Show(True)
    app.MainLoop()

运行效果:

背景图片.png

打包指令:

# D:\python3.10\Lib\site-packages\ 换成实际的 site-packages 所在目录
pyinstaller3 -F .\timer_ok.py --icon=背景图片.png --noconsole -p D:\python3.10\Lib\site-packages\

2025-02-14 v2.0 版本 增加了记事本功能,可以实现简单的markdown渲染 (目前还有点问题,后续待优化)

import wx
import wx.html2
import markdown
import time
import threading
import os


class CountdownPanel(wx.Panel):
    def __init__(self, parent):
        super(CountdownPanel, self).__init__(parent)

        self.background = None
        self.is_cancle = True

        # 设置背景图片
        self.set_background("背景图片.png")

        # 初始化
        self.init_clock_countdown()

        self.Bind(wx.EVT_PAINT, self.paint_background)
        self.Bind(wx.EVT_SIZING, self.on_size)

    def init_clock_countdown(self):
        """初始化倒计时功能"""
        self.clock_label = wx.StaticText(self, label="0000-00-00 00:00:00")
        self.clock_label.SetBackgroundColour(wx.Colour(135, 206, 250, 128))  # 设置背景颜色和透明度
        clock_font = wx.Font(24, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
        self.clock_label.SetFont(clock_font)
        
        self.countdown_label = wx.StaticText(self, label="00:00:00")
        self.countdown_label.SetBackgroundColour(wx.Colour(255, 182, 193, 30))
        countdown_font = wx.Font(18, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
        self.countdown_label.SetFont(countdown_font)
        
        self.countdown_text = wx.TextCtrl(self, value="00:00:00")
        self.start_button = wx.Button(self, label="Start")
        # self.start_button.SetBackgroundColour(wx.Colour(144, 238, 144, 128))
        self.cancel_button = wx.Button(self, label="Cancel")
        # self.cancel_button.SetBackgroundColour(wx.Colour(255,255,193,128))
        
        self.start_button.Bind(wx.EVT_BUTTON, self.start_countdown)
        self.cancel_button.Bind(wx.EVT_BUTTON, self.cancel_countdown)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        clock_sizer = wx.BoxSizer(wx.HORIZONTAL)
        clock_sizer.AddStretchSpacer()
        clock_sizer.Add(self.clock_label, flag=wx.ALIGN_CENTER_VERTICAL, proportion=1)
        clock_sizer.AddStretchSpacer()
        sizer.Add(clock_sizer, flag=wx.EXPAND|wx.ALL, border=10, proportion=1)
        
        sizer.Add(wx.StaticLine(self), flag=wx.EXPAND|wx.ALL, border=10)
        
        countdown_sizer = wx.BoxSizer(wx.VERTICAL)
        countdown_sizer.Add(self.countdown_label, flag=wx.ALIGN_CENTER, proportion=1)
        countdown_sizer.Add(self.countdown_text, flag=wx.ALIGN_CENTER|wx.ALL, border=10, proportion=1)
        
        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
        buttons_sizer.Add(self.start_button, flag=wx.ALIGN_CENTER|wx.ALL, border=10)
        buttons_sizer.Add(self.cancel_button, flag=wx.ALIGN_CENTER|wx.ALL, border=10)
        
        countdown_sizer.Add(buttons_sizer, flag=wx.ALIGN_CENTER, proportion=2)
        
        sizer.Add(countdown_sizer, flag=wx.EXPAND|wx.ALL, border=10, proportion=2)
        
        self.SetSizer(sizer)
        
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update_clock, self.timer)
        self.timer.Start(1000)  # 每隔一秒钟更新一次时钟
        
    def on_size(self, event):
        self.set_background("背景图片.png")
    
    def set_background(self, image_path):
        """设置背景图片"""
        if os.path.exists(image_path):
            image = wx.Image(image_path, wx.BITMAP_TYPE_ANY)
            image = image.Scale(self.GetSize().GetWidth(), self.GetSize().GetHeight())
            self.background = wx.Bitmap(image)
            self.Refresh()
        else:
            wx.MessageBox("未找到背景图片: " + image_path)

    def paint_background(self, event):
        """绘制背景"""
        if self.background:
            dc = wx.BufferedPaintDC(self)
            dc.DrawBitmap(self.background, 0, 0)

    def update_clock(self, event):
        """更新时钟"""
        current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        self.clock_label.SetLabel(current_time)

    def start_countdown(self, event):
        """开始倒计时"""
        self.is_cancle = False
        countdown_time = self.countdown_text.GetValue()
        countdown_thread = threading.Thread(target=self.run_countdown, args=(countdown_time,))
        countdown_thread.start()

    def cancel_countdown(self, event):
        """取消倒计时"""
        self.is_cancle = True
        self.countdown_label.SetLabel("00:00:00")

    def run_countdown(self, countdown_time):
        """运行倒计时"""
        hours, minutes, seconds = countdown_time.split(":")
        total_seconds = int(hours) * 3600 + int(minutes) * 60 + int(seconds)

        while self.is_cancle is False and total_seconds >= 0:
            hours = total_seconds // 3600
            minutes = (total_seconds % 3600) // 60
            seconds = total_seconds % 60
            countdown_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
            wx.CallAfter(self.countdown_label.SetLabel, countdown_str)
            total_seconds -= 1
            wx.MilliSleep(1000)  # 等待一秒钟

        wx.CallAfter(wx.MessageBox, "倒计时结束了, 休息一下吧")


class MarkdownPanel(wx.Panel):
    def __init__(self, parent):
        super(MarkdownPanel, self).__init__(parent)
        self.init_markdown_preview()

    def init_markdown_preview(self):
        """初始化 Markdown 输入和预览功能"""
        sizer = wx.BoxSizer(wx.VERTICAL)

        # 输入框
        self.markdown_input = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_RICH2)
        self.markdown_input.Bind(wx.EVT_TEXT, self.on_markdown_change)

        # 预览窗口
        self.markdown_preview = wx.html2.WebView.New(self)

        sizer.Add(self.markdown_input, 2, wx.EXPAND | wx.ALL, 5)
        sizer.Add(self.markdown_preview, 8, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(sizer)

    def on_markdown_change(self, event):
        """Markdown 文本变化事件处理函数"""
        markdown_text = self.markdown_input.GetValue()
        html_content = markdown.markdown(markdown_text)
        self.markdown_preview.SetPage(html_content, "")


class MainFrame(wx.Frame):
    def __init__(self, *args, **kw):
        super(MainFrame, self).__init__(*args, **kw)
        self.SetWindowStyle(wx.DEFAULT_FRAME_STYLE & ~(wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX))

        self.splitter = wx.SplitterWindow(self)

        # 创建左侧和右侧的面板
        self.left_panel = CountdownPanel(self.splitter)
        self.right_panel = MarkdownPanel(self.splitter)
        
        self.left_panel.Bind(wx.EVT_PAINT, self.left_panel.paint_background)
        self.Bind(wx.EVT_SIZING, self.on_size)
        self.splitter.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.on_size)
        
        
        self.splitter.SplitVertically(self.left_panel, self.right_panel)
        # self.splitter.SetSashPosition(400)
        
        # 设置最小窗格大小
        # self.splitter.SetMinimumPaneSize(200)  # 设置每个面板的最小宽度
        
        self.SetSize((720, 360))
        self.SetTitle("小工具")
        self.Centre()
        
        # 设置初始分割位置,以比例控制(如40%左边,60%右边)
        self.Set_initial_sash_position()
        
        self.left_panel.set_background("背景图片.png")
        
    def on_size(self, event):
        self.left_panel.set_background("背景图片.png")
    
    def Set_initial_sash_position(self):
        """设置分割条初始位置,例如 40% 为左侧面板宽度"""
        width, _ = self.GetSize()
        left_panel_width = int(width*0.6)  # 40% 的宽度分配给左侧面板
        self.splitter.SetSashPosition(left_panel_width)


if __name__ == '__main__':
    app = wx.App()
    frame = MainFrame(None)
    frame.Show(True)
    app.MainLoop()
posted @ 2024-05-30 17:26  BrianSun  阅读(30)  评论(0编辑  收藏  举报