This commit is contained in:
2025-12-31 16:00:29 +00:00
commit 3688f93d5d
310 changed files with 93400 additions and 0 deletions

472
2022/09/05/trayicon.html Normal file
View File

@@ -0,0 +1,472 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Begin Jekyll SEO tag v2.8.0 -->
<title>制作程序运行提示标志的历程 | Mayx的博客</title>
<meta name="generator" content="Jekyll v3.9.5" />
<meta property="og:title" content="制作程序运行提示标志的历程" />
<meta name="author" content="mayx" />
<meta property="og:locale" content="zh_CN" />
<meta name="description" content="有图形界面的程序可真是难做啊……" />
<meta property="og:description" content="有图形界面的程序可真是难做啊……" />
<meta property="og:site_name" content="Mayx的博客" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2022-09-05T00:00:00+08:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="制作程序运行提示标志的历程" />
<meta name="google-site-verification" content="huTYdEesm8NaFymixMNqflyCp6Jfvd615j5Wq1i2PHc" />
<meta name="msvalidate.01" content="0ADFCE64B3557DC4DC5F2DC224C5FDDD" />
<meta name="yandex-verification" content="fc0e535abed800be" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"mayx"},"dateModified":"2022-09-05T00:00:00+08:00","datePublished":"2022-09-05T00:00:00+08:00","description":"有图形界面的程序可真是难做啊……","headline":"制作程序运行提示标志的历程","mainEntityOfPage":{"@type":"WebPage","@id":"/2022/09/05/trayicon.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://avatars0.githubusercontent.com/u/17966333"},"name":"mayx"},"url":"/2022/09/05/trayicon.html"}</script>
<!-- End Jekyll SEO tag -->
<link rel="canonical" href="https://mabbs.github.io/2022/09/05/trayicon.html" />
<link type="application/atom+xml" rel="alternate" href="/atom.xml" title="Mayx的博客" />
<link rel="alternate" type="application/rss+xml" title="Mayx的博客(RSS)" href="/rss.xml" />
<link rel="alternate" type="application/json" title="Mayx的博客(JSON Feed)" href="/feed.json" />
<link rel="stylesheet" href="/assets/css/style.css?v=1767196818" />
<!--[if !IE]> -->
<link rel="stylesheet" href="/Live2dHistoire/live2d/css/live2d.css" />
<!-- <![endif]-->
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mayx的博客" />
<link rel="webmention" href="https://webmention.io/mabbs.github.io/webmention" />
<link rel="pingback" href="https://webmention.io/mabbs.github.io/xmlrpc" />
<link rel="preconnect" href="https://summary.mayx.eu.org" crossorigin="anonymous" />
<link rel="prefetch" href="https://www.blogsclub.org/badge/mabbs.github.io" as="image" />
<link rel="blogroll" type="text/xml" href="/blogroll.opml" />
<link rel="me" href="https://github.com/Mabbs" />
<script src="/assets/js/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.3/jquery.xdomainrequest.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script>
var lastUpdated = new Date("Thu, 01 Jan 2026 00:00:18 +0800");
var BlogAPI = "https://summary.mayx.eu.org";
</script>
<script src="/assets/js/main.js"></script>
<!--[if !IE]> -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-137710294-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-137710294-1');
</script>
<script src="/assets/js/instant.page.js" type="module"></script>
<!-- <![endif]-->
</head>
<body>
<!--[if !IE]> --><noscript><marquee style="top: -15px; position: relative;"><small>发现当前浏览器没有启用JavaScript这不影响你的浏览但可能会有一些功能无法使用……</small></marquee></noscript><!-- <![endif]-->
<!--[if IE]><marquee style="top: -15px; position: relative;"><small>发现当前浏览器为Internet Explorer这不影响你的浏览但可能会有一些功能无法使用……</small></marquee><![endif]-->
<div class="wrapper">
<header class="h-card">
<h1><a class="u-url u-uid p-name" rel="me" href="/">Mayx的博客</a></h1>
<img src="https://avatars0.githubusercontent.com/u/17966333" fetchpriority="high" class="u-photo" alt="Logo" style="width: 90%; max-width: 300px; max-height: 300px;" />
<p class="p-note">Mayx's Home Page</p>
<form action="/search.html">
<input type="text" name="keyword" id="search-input-all" placeholder="Search blog posts.." />&#160;<input type="submit" value="搜索" />
</form>
<br />
<p class="view"><a class="u-url" href="/Mabbs/">About me</a></p>
<ul class="downloads">
<li style="width: 270px; border-right: none;"><a href="/MayxBlog.tgz">Download <strong>TGZ File</strong></a></li>
</ul>
</header>
<section class="h-entry">
<small><time class="date dt-published" datetime="2022-09-05T00:00:00+08:00">5 September 2022</time> - 字数统计4273 - 阅读大约需要14分钟 - Hits: <span id="/2022/09/05/trayicon.html" class="visitors">Loading...</span></small>
<h1 class="p-name">制作程序运行提示标志的历程</h1>
<p class="view">by <a class="p-author h-card" href="//github.com/Mabbs">mayx</a></p>
<div id="outdate" style="display:none;">
<hr /><p>
这是一篇创建于 <span id="outime"></span> 天前的文章,其中的信息可能已经有所发展或是发生改变。
</p>
</div>
<script>
daysold = Math.floor((new Date().getTime() - new Date("Mon, 05 Sep 2022 00:00:00 +0800").getTime()) / (24 * 60 * 60 * 1000));
if (daysold > 90) {
document.getElementById("outdate").style.display = "block";
document.getElementById("outime").innerHTML = daysold;
}
</script>
<hr />
<b>AI摘要</b>
<p id="ai-output">这篇文章讲述了作者从使用Python的PyQt5库制作程序运行提示图标遇到体积过大问题到尝试Pystray库结果依然不理想再到最终选择Tkinter和PyQt5实现跨平台悬浮图标过程中的经历。作者在寻求轻量级解决方案时虽然Tkinter在Windows上表现尚可但在Linux上遇到透明度问题而Qt5虽能提供跨平台一致性但学习和使用起来较为复杂。作者对跨平台开发的复杂性以及选择不同工具的权衡有了深入的认识。</p>
<hr />
<ul><li><a href="#起因">起因</a></li><li><a href="#制作的历程">制作的历程</a><ul><li><a href="#使用托盘区图标">使用托盘区图标</a><ul><li><a href="#使用pyqt5库实现">使用PyQt5库实现</a></li><li><a href="#使用pystray库实现">使用pystray库实现</a></li></ul></li><li><a href="#制作悬浮图标">制作悬浮图标</a><ul><li><a href="#通过tkinter实现">通过Tkinter实现</a></li><li><a href="#使用pyqt5库实现-1">使用PyQt5库实现</a></li></ul></li></ul></li><li><a href="#感想">感想</a></li></ul>
<hr />
<main class="post-content e-content" role="main"><p>有图形界面的程序可真是难做啊……<!--more--></p>
<h1 id="起因">
<a href="#起因"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 起因
</h1>
<p>最近我做了一个程序类似于守护进程那样的一个用Python制作的脚本。脚本做出来很简单可是我做出来之后需要向其他人证明我做的脚本正在运行而且是给一个不懂电脑的人知道。在这个前提下我不可能让其他人去看任务管理器、或者执行<code class="language-plaintext highlighter-rouge">ps -ef | grep xxx</code>这种东西吧。所以还是得让它在运行的时候在桌面这样的图形界面显示一些东西才行。</p>
<h1 id="制作的历程">
<a href="#制作的历程"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 制作的历程
</h1>
<h2 id="使用托盘区图标">
<a href="#使用托盘区图标"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用托盘区图标
</h2>
<p>像一般的后台程序证明自己存在的方式就是任务栏的托盘区显示一个图标。于是首先我就按照这个想法先用PyQt5然后在网上找了一段代码然后自己改了改</p>
<h3 id="使用pyqt5库实现">
<a href="#使用pyqt5库实现"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用PyQt5库实现
</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">PyQt5</span> <span class="kn">import</span> <span class="n">QtCore</span><span class="p">,</span> <span class="n">QtGui</span><span class="p">,</span> <span class="n">QtWidgets</span>
<span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Thread</span>
<span class="k">class</span> <span class="nc">SystemTrayIcon</span><span class="p">(</span><span class="n">QtWidgets</span><span class="p">.</span><span class="n">QSystemTrayIcon</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">icon</span><span class="p">,</span> <span class="n">parent</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="n">QtWidgets</span><span class="p">.</span><span class="n">QSystemTrayIcon</span><span class="p">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">icon</span><span class="p">,</span> <span class="n">parent</span><span class="p">)</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">QtWidgets</span><span class="p">.</span><span class="n">QApplication</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span>
<span class="n">w</span> <span class="o">=</span> <span class="n">QtWidgets</span><span class="p">.</span><span class="n">QWidget</span><span class="p">()</span>
<span class="n">trayIcon</span> <span class="o">=</span> <span class="n">SystemTrayIcon</span><span class="p">(</span><span class="n">QtGui</span><span class="p">.</span><span class="n">QIcon</span><span class="p">(</span><span class="s">"pic.ico"</span><span class="p">),</span> <span class="n">w</span><span class="p">)</span>
<span class="n">trayIcon</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
<span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">some_random_function</span><span class="p">).</span><span class="n">start</span><span class="p">()</span> <span class="c1"># 我的脚本函数
</span><span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">exec_</span><span class="p">())</span>
</code></pre></div></div>
<p>这样做完之后东西确实可以运行了不过我的代码还要在其他人的电脑上运行所以我先用Pyinstaller打包了一下不过打包出来的程序很大就几十行代码就算加上Python解析器最多也就不到10MiB结果这加上PyQt库之后直接上升到40MiB左右就算用upx压缩完也有30多MiB实在是让人无法忍受于是我就考虑看看能不能用其他方式来制作这个图标。 </p><p>
后来我在网上找到有一个叫做pystray的库似乎是专门干这个活的。那既然是专门干这个的那大小肯定比什么Qt库要小得多吧于是我按照示例的代码随便写了一段</p>
<h3 id="使用pystray库实现">
<a href="#使用pystray库实现"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用pystray库实现
</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Thread</span>
<span class="kn">import</span> <span class="nn">pystray</span>
<span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="s">"pic.png"</span><span class="p">)</span>
<span class="n">icon</span> <span class="o">=</span> <span class="n">pystray</span><span class="p">.</span><span class="n">Icon</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"SomeRandom"</span><span class="p">,</span> <span class="n">icon</span><span class="o">=</span><span class="n">image</span><span class="p">,</span> <span class="n">title</span><span class="o">=</span><span class="s">"SomeRandom"</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span>
<span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">some_random_function</span><span class="p">).</span><span class="n">start</span><span class="p">()</span> <span class="c1"># 我的脚本函数
</span><span class="n">icon</span><span class="p">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div></div>
<p>这个代码我没有测试过不过在我写完之后首先用Pyinstaller打包了一下结果大小比用PyQt5还要大😂打包完大小要60多MiB所以没办法我就只能继续用Qt的那个版本了……</p>
<h2 id="制作悬浮图标">
<a href="#制作悬浮图标"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 制作悬浮图标
</h2>
<p>后来我的脚本由于应用面广泛需要在Ubuntu上使用最新的Ubuntu使用的GNOME桌面<a href="https://blogs.gnome.org/aday/2017/08/31/status-icons-and-gnome/">不再支持托盘区</a>了😓,所以没办法,只能想别的办法了。 </p><p>
现在的程序除了托盘区图标证明自己的存在可能还有就是悬浮球了吧国产很多软件喜欢把自己程序整成悬浮球那样放在桌面上吸引用户的注意力所以我的程序也要这样搞。我想了想用PyQt库实在是太重了我想整个轻量的图形引擎像Python自带的Tkinter就挺不错的所以我首先用Tkinter做了一个版本出来</p>
<h3 id="通过tkinter实现">
<a href="#通过tkinter实现"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 通过Tkinter实现
</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">tkinter</span>
<span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Thread</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">tkinter</span><span class="p">.</span><span class="n">Tk</span><span class="p">()</span>
<span class="n">height</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">width</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">root</span><span class="p">.</span><span class="n">overrideredirect</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">attributes</span><span class="p">(</span><span class="s">'-transparentcolor'</span><span class="p">,</span> <span class="s">"white"</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">attributes</span><span class="p">(</span><span class="s">"-alpha"</span><span class="p">,</span> <span class="mf">0.9</span><span class="p">)</span> <span class="c1"># 窗口透明度10 %
</span><span class="n">root</span><span class="p">.</span><span class="n">attributes</span><span class="p">(</span><span class="s">"-topmost"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">geometry</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">height</span><span class="si">}</span><span class="s">x</span><span class="si">{</span><span class="n">width</span><span class="si">}</span><span class="s">-40+60"</span><span class="p">)</span>
<span class="n">canvas</span> <span class="o">=</span> <span class="n">tkinter</span><span class="p">.</span><span class="n">Canvas</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="n">height</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="n">width</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s">"white"</span><span class="p">)</span>
<span class="n">canvas</span><span class="p">.</span><span class="n">config</span><span class="p">(</span><span class="n">highlightthickness</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">image_file</span> <span class="o">=</span> <span class="n">tkinter</span><span class="p">.</span><span class="n">PhotoImage</span><span class="p">(</span><span class="nb">file</span><span class="o">=</span><span class="sa">r</span><span class="s">'pic.png'</span><span class="p">)</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">canvas</span><span class="p">.</span><span class="n">create_image</span><span class="p">(</span>
<span class="n">height</span><span class="o">//</span><span class="mi">2</span><span class="p">,</span> <span class="n">width</span><span class="o">//</span><span class="mi">2</span><span class="p">,</span> <span class="n">anchor</span><span class="o">=</span><span class="n">tkinter</span><span class="p">.</span><span class="n">CENTER</span><span class="p">,</span> <span class="n">image</span><span class="o">=</span><span class="n">image_file</span><span class="p">)</span>
<span class="n">canvas</span><span class="p">.</span><span class="n">pack</span><span class="p">()</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="n">show_menu</span> <span class="o">=</span> <span class="n">tkinter</span><span class="p">.</span><span class="n">Menu</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">show_menu</span><span class="p">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s">"SomeRandom"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="n">event</span><span class="p">):</span>
<span class="k">global</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="n">new_x</span> <span class="o">=</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">x</span><span class="o">-</span><span class="n">x</span><span class="p">)</span><span class="o">+</span><span class="n">root</span><span class="p">.</span><span class="n">winfo_x</span><span class="p">()</span><span class="o">-</span><span class="n">height</span><span class="o">//</span><span class="mi">2</span>
<span class="n">new_y</span> <span class="o">=</span> <span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">y</span><span class="o">-</span><span class="n">y</span><span class="p">)</span><span class="o">+</span><span class="n">root</span><span class="p">.</span><span class="n">winfo_y</span><span class="p">()</span><span class="o">-</span><span class="n">width</span><span class="o">//</span><span class="mi">2</span>
<span class="n">s</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">height</span><span class="si">}</span><span class="s">x</span><span class="si">{</span><span class="n">width</span><span class="si">}</span><span class="s">+"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">new_x</span><span class="p">)</span><span class="o">+</span><span class="s">"+"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">new_y</span><span class="p">)</span>
<span class="n">root</span><span class="p">.</span><span class="n">geometry</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="n">canvas</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">"&lt;B1-Motion&gt;"</span><span class="p">,</span> <span class="n">move</span><span class="p">)</span>
<span class="n">canvas</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">"&lt;Enter&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">event</span><span class="p">:</span> <span class="n">show_menu</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">x_root</span><span class="p">,</span> <span class="n">event</span><span class="p">.</span><span class="n">y_root</span><span class="p">))</span>
<span class="n">canvas</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">"&lt;Leave&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">e</span><span class="p">:</span> <span class="n">show_menu</span><span class="p">.</span><span class="n">unpost</span><span class="p">())</span>
<span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">some_random_function</span><span class="p">).</span><span class="n">start</span><span class="p">()</span> <span class="c1"># 我的脚本函数
</span>
<span class="n">root</span><span class="p">.</span><span class="n">mainloop</span><span class="p">()</span>
</code></pre></div></div>
<p>这个代码在Windows上工作还算可以问题不是很多但是在Linux上就出现了很糟糕的问题根据tcl/tk <a href="https://wiki.tcl-lang.org/page/wm+attributes">documentation</a> <del>好像也没写🤣</del> ,“-transparentcolor”属性只能在Windows等系统使用貌似MacOS也能用因此在Linux中会报错。如果不能使用透明背景效果就会很差我看Stack Overflow上有人说可以安装一个pqiv图片查看器然后使用<code class="language-plaintext highlighter-rouge">os.popen()</code>或者<code class="language-plaintext highlighter-rouge">subprocess.Popen()</code>执行<code class="language-plaintext highlighter-rouge">pqiv -c -c -i pic.png</code>也能达到类似的效果不过这种东西先不说还要安装而且这个东西点两下就能看见它的窗口还能关闭那肯定是不符合我们的要求的。所以没办法……只能再考虑Qt的办法了。</p>
<h3 id="使用pyqt5库实现-1">
<a href="#使用pyqt5库实现-1"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 使用PyQt5库实现
</h3>
<p>我在网上又找了些资料把PyQt的版本也做出来了而且还加了支持Gif动态图片的效果</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">PyQt5.QtWidgets</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">PyQt5.QtGui</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">PyQt5.QtCore</span> <span class="kn">import</span> <span class="o">*</span>
<span class="k">class</span> <span class="nc">Example</span><span class="p">(</span><span class="n">QWidget</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
<span class="bp">self</span><span class="p">.</span><span class="n">initUI</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">mouseMoveEvent</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="n">QMouseEvent</span><span class="p">):</span> <span class="c1"># 重写移动事件
</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">_tracking</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">_endPos</span> <span class="o">=</span> <span class="n">e</span><span class="p">.</span><span class="n">pos</span><span class="p">()</span> <span class="o">-</span> <span class="bp">self</span><span class="p">.</span><span class="n">_startPos</span>
<span class="bp">self</span><span class="p">.</span><span class="n">move</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">pos</span><span class="p">()</span> <span class="o">+</span> <span class="bp">self</span><span class="p">.</span><span class="n">_endPos</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">mousePressEvent</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="n">QMouseEvent</span><span class="p">):</span>
<span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="n">button</span><span class="p">()</span> <span class="o">==</span> <span class="n">Qt</span><span class="p">.</span><span class="n">LeftButton</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">_startPos</span> <span class="o">=</span> <span class="n">QPoint</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">x</span><span class="p">(),</span> <span class="n">e</span><span class="p">.</span><span class="n">y</span><span class="p">())</span>
<span class="bp">self</span><span class="p">.</span><span class="n">_tracking</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">mouseReleaseEvent</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span> <span class="n">QMouseEvent</span><span class="p">):</span>
<span class="k">if</span> <span class="n">e</span><span class="p">.</span><span class="n">button</span><span class="p">()</span> <span class="o">==</span> <span class="n">Qt</span><span class="p">.</span><span class="n">LeftButton</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">_tracking</span> <span class="o">=</span> <span class="bp">False</span>
<span class="bp">self</span><span class="p">.</span><span class="n">_startPos</span> <span class="o">=</span> <span class="bp">None</span>
<span class="bp">self</span><span class="p">.</span><span class="n">_endPos</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">initUI</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">layout</span> <span class="o">=</span> <span class="n">QStackedLayout</span><span class="p">()</span>
<span class="bp">self</span><span class="p">.</span><span class="n">lbl1</span> <span class="o">=</span> <span class="n">QLabel</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">movie</span> <span class="o">=</span> <span class="n">QMovie</span><span class="p">(</span><span class="s">"pic.gif"</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">lbl1</span><span class="p">.</span><span class="n">setMovie</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">movie</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">movie</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="bp">self</span><span class="p">.</span><span class="n">lbl1</span><span class="p">.</span><span class="n">setToolTip</span><span class="p">(</span><span class="s">"SomeRandom"</span><span class="p">)</span>
<span class="n">layout</span><span class="p">.</span><span class="n">addWidget</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">lbl1</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">setAttribute</span><span class="p">(</span><span class="n">Qt</span><span class="p">.</span><span class="n">WA_TranslucentBackground</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">setWindowFlags</span><span class="p">(</span><span class="n">Qt</span><span class="p">.</span><span class="n">WindowStaysOnTopHint</span> <span class="o">|</span> <span class="n">Qt</span><span class="p">.</span><span class="n">FramelessWindowHint</span> <span class="o">|</span> <span class="n">Qt</span><span class="p">.</span><span class="n">Tool</span><span class="p">)</span>
<span class="c1">#layout area for widgets
</span> <span class="n">layout</span><span class="p">.</span><span class="n">setCurrentIndex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">setLayout</span><span class="p">(</span><span class="n">layout</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">setGeometry</span><span class="p">(</span><span class="n">QApplication</span><span class="p">.</span><span class="n">desktop</span><span class="p">().</span><span class="n">width</span><span class="p">()</span><span class="o">-</span><span class="mi">130</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span><span class="mi">100</span><span class="p">,</span><span class="mi">100</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">QApplication</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span>
<span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">some_random_function</span><span class="p">).</span><span class="n">start</span><span class="p">()</span> <span class="c1"># 我的脚本函数
</span> <span class="n">ex</span> <span class="o">=</span> <span class="n">Example</span><span class="p">()</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="n">app</span><span class="p">.</span><span class="n">exec_</span><span class="p">())</span>
</code></pre></div></div>
<p>最终做出来效果还不错说不定加点功能放组简单的立绘动画就能做一个像我博客左下角的看板娘一样的东西呢🤣虽然我也见过用Electron写的<a href="https://github.com/zenghongtu/PPet">PPet</a>不过用Python写的话可能对更多人更友好吧 <del>Gif哪能和Live2D比😂</del></p>
<h1 id="感想">
<a href="#感想"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 感想
</h1>
<p>这次做这么一个Python程序运行的提示标志还真是复杂啊尤其是为了跨平台其实专门对应一个平台做起来可能也没有很复杂不过想能在各个平台上都能使用还是挺难的。这次来看Qt的跨平台性确实很强无论是在哪个平台上都能获得不错的体验就是用起来感觉比较麻烦其实说来如果能用C++之类的语言去开发Qt程序应该更好Python这个基本上也就只能当作一个玩具算是熟悉一下Qt的各种功能了。</p></main>
<small style="display: block">tags: <a rel="category tag" class="p-category" href="/search.html?keyword=Python"><em>Python</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E7%A8%8B%E5%BA%8F"><em>程序</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E6%A0%87%E5%BF%97"><em>标志</em></a> <span style="float: right;"><a href="https://gitlab.com/mayx/mayx.gitlab.io/tree/master/_posts/2022-09-05-trayicon.md">查看原始文件</a></span></small>
<h4 style="border-bottom: 1px solid #e5e5e5;margin: 2em 0 5px;">推荐文章</h4>
<p id="suggest-container">Loading...</p>
<script>
var suggest = $("#suggest-container");
$.get(BlogAPI + "/suggest?id=/2022/09/05/trayicon.html&update=" + lastUpdated.valueOf(), function (data) {
if (data.length) {
getSearchJSON(function (search) {
suggest.empty();
var searchMap = {};
for (var i = 0; i < search.length; i++) {
searchMap[search[i].url] = search[i];
}
var tooltip = $('<div class="content-tooltip"></div>').appendTo('body').hide();
for (var j = 0; j < data.length; j++) {
var item = searchMap[data[j].id];
if (item) {
var link = $('<a href="' + item.url + '">' + item.title + '</a>');
var contentPreview = item.content.substring(0, 100);
if (item.content.length > 100) {
contentPreview += "……";
}
link.hover(
function(e) {
tooltip.text($(this).data('content'))
.css({
top: e.pageY + 10,
left: e.pageX + 10
})
.show();
},
function() {
tooltip.hide();
}
).mousemove(function(e) {
tooltip.css({
top: e.pageY + 10,
left: e.pageX + 10
});
}).data('content', contentPreview);
suggest.append(link);
suggest.append(' - ' + item.date + '<br />');
}
}
});
} else {
suggest.html("暂无推荐文章……");
}
});
</script>
<br />
<div class="pagination">
<span class="prev">
<a href="/2022/08/15/cdn-verify.html">
上一篇如何避免Cloudflare背后的源站被恶意访问
</a>
</span>
<br />
<span class="next">
<a href="/2022/09/21/cron.html">
下一篇使用Python制作可热载的定时调度器
</a>
</span>
</div>
<!--[if !IE]> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="/assets/js/gitalk.min.js"></script>
<div id="gitalk-container"></div>
<script>
var gitalk = new Gitalk({
clientID: '36557aec4c3cb04f7ac6',
clientSecret: 'ac32993299751cb5a9ba81cf2b171cca65879cdb',
repo: 'mabbs.github.io',
owner: 'Mabbs',
admin: ['Mabbs'],
id: '/2022/09/05/trayicon', // Ensure uniqueness and length less than 50
distractionFreeMode: false, // Facebook-like distraction free mode
proxy: "https://cors-anywhere.mayx.eu.org/?https://github.com/login/oauth/access_token"
})
gitalk.render('gitalk-container')
</script>
<!-- <![endif]-->
</section>
<!--[if !IE]> -->
<div id="landlord" style="left:5px;bottom:0px;">
<div class="message" style="opacity:0"></div>
<canvas id="live2d" width="500" height="560" class="live2d"></canvas>
<div class="live_talk_input_body">
<form id="live_talk_input_form">
<div class="live_talk_input_name_body" >
<input type="checkbox" id="load_this" />
<input type="hidden" id="post_id" value="/2022/09/05/trayicon.html" />
<label for="load_this">
<span style="font-size: 11px; color: #fff;">&#160;想问这篇文章</span>
</label>
</div>
<div class="live_talk_input_text_body">
<input name="talk" type="text" class="live_talk_talk white_input" id="AIuserText" autocomplete="off" placeholder="要和我聊什么呀?" />
<button type="submit" class="live_talk_send_btn" id="talk_send">发送</button>
</div>
</form>
</div>
<input name="live_talk" id="live_talk" value="1" type="hidden" />
<div class="live_ico_box" style="display:none;">
<div class="live_ico_item type_info" id="showInfoBtn"></div>
<div class="live_ico_item type_talk" id="showTalkBtn"></div>
<div class="live_ico_item type_music" id="musicButton"></div>
<div class="live_ico_item type_youdu" id="youduButton"></div>
<div class="live_ico_item type_quit" id="hideButton"></div>
<input name="live_statu_val" id="live_statu_val" value="0" type="hidden" />
<audio src="" style="display:none;" id="live2d_bgm" data-bgm="0" preload="none"></audio>
<input id="duType" value="douqilai" type="hidden" />
</div>
</div>
<div id="open_live2d">召唤伊斯特瓦尔</div>
<!-- <![endif]-->
<footer>
<p>
<small>Made with ❤ by Mayx<br />Last updated at 2026-01-01 00:00:18<br /> 总字数614622 - 文章数178 - <a href="/atom.xml" >Atom</a> - <a href="/README.html" >About</a></small>
</p>
</footer>
</div>
<script src="/assets/js/scale.fix.js"></script>
<!--[if !IE]> -->
<script src="/assets/js/main_new.js"></script>
<script src="/Live2dHistoire/live2d/js/live2d.js"></script>
<script src="/Live2dHistoire/live2d/js/message.js"></script>
<!-- <![endif]-->
</body>
</html>

383
2022/09/21/cron.html Normal file
View File

@@ -0,0 +1,383 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Begin Jekyll SEO tag v2.8.0 -->
<title>使用Python制作可热载的定时调度器 | Mayx的博客</title>
<meta name="generator" content="Jekyll v3.9.5" />
<meta property="og:title" content="使用Python制作可热载的定时调度器" />
<meta name="author" content="mayx" />
<meta property="og:locale" content="zh_CN" />
<meta name="description" content="定时任务用CRON难道不够吗" />
<meta property="og:description" content="定时任务用CRON难道不够吗" />
<meta property="og:site_name" content="Mayx的博客" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2022-09-21T00:00:00+08:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="使用Python制作可热载的定时调度器" />
<meta name="google-site-verification" content="huTYdEesm8NaFymixMNqflyCp6Jfvd615j5Wq1i2PHc" />
<meta name="msvalidate.01" content="0ADFCE64B3557DC4DC5F2DC224C5FDDD" />
<meta name="yandex-verification" content="fc0e535abed800be" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"mayx"},"dateModified":"2022-09-21T00:00:00+08:00","datePublished":"2022-09-21T00:00:00+08:00","description":"定时任务用CRON难道不够吗","headline":"使用Python制作可热载的定时调度器","mainEntityOfPage":{"@type":"WebPage","@id":"/2022/09/21/cron.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://avatars0.githubusercontent.com/u/17966333"},"name":"mayx"},"url":"/2022/09/21/cron.html"}</script>
<!-- End Jekyll SEO tag -->
<link rel="canonical" href="https://mabbs.github.io/2022/09/21/cron.html" />
<link type="application/atom+xml" rel="alternate" href="/atom.xml" title="Mayx的博客" />
<link rel="alternate" type="application/rss+xml" title="Mayx的博客(RSS)" href="/rss.xml" />
<link rel="alternate" type="application/json" title="Mayx的博客(JSON Feed)" href="/feed.json" />
<link rel="stylesheet" href="/assets/css/style.css?v=1767196818" />
<!--[if !IE]> -->
<link rel="stylesheet" href="/Live2dHistoire/live2d/css/live2d.css" />
<!-- <![endif]-->
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mayx的博客" />
<link rel="webmention" href="https://webmention.io/mabbs.github.io/webmention" />
<link rel="pingback" href="https://webmention.io/mabbs.github.io/xmlrpc" />
<link rel="preconnect" href="https://summary.mayx.eu.org" crossorigin="anonymous" />
<link rel="prefetch" href="https://www.blogsclub.org/badge/mabbs.github.io" as="image" />
<link rel="blogroll" type="text/xml" href="/blogroll.opml" />
<link rel="me" href="https://github.com/Mabbs" />
<script src="/assets/js/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-ajaxtransport-xdomainrequest/1.0.3/jquery.xdomainrequest.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script>
var lastUpdated = new Date("Thu, 01 Jan 2026 00:00:18 +0800");
var BlogAPI = "https://summary.mayx.eu.org";
</script>
<script src="/assets/js/main.js"></script>
<!--[if !IE]> -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-137710294-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-137710294-1');
</script>
<script src="/assets/js/instant.page.js" type="module"></script>
<!-- <![endif]-->
</head>
<body>
<!--[if !IE]> --><noscript><marquee style="top: -15px; position: relative;"><small>发现当前浏览器没有启用JavaScript这不影响你的浏览但可能会有一些功能无法使用……</small></marquee></noscript><!-- <![endif]-->
<!--[if IE]><marquee style="top: -15px; position: relative;"><small>发现当前浏览器为Internet Explorer这不影响你的浏览但可能会有一些功能无法使用……</small></marquee><![endif]-->
<div class="wrapper">
<header class="h-card">
<h1><a class="u-url u-uid p-name" rel="me" href="/">Mayx的博客</a></h1>
<img src="https://avatars0.githubusercontent.com/u/17966333" fetchpriority="high" class="u-photo" alt="Logo" style="width: 90%; max-width: 300px; max-height: 300px;" />
<p class="p-note">Mayx's Home Page</p>
<form action="/search.html">
<input type="text" name="keyword" id="search-input-all" placeholder="Search blog posts.." />&#160;<input type="submit" value="搜索" />
</form>
<br />
<p class="view"><a class="u-url" href="/Mabbs/">About me</a></p>
<ul class="downloads">
<li style="width: 270px; border-right: none;"><a href="/MayxBlog.tgz">Download <strong>TGZ File</strong></a></li>
</ul>
</header>
<section class="h-entry">
<small><time class="date dt-published" datetime="2022-09-21T00:00:00+08:00">21 September 2022</time> - 字数统计2238 - 阅读大约需要8分钟 - Hits: <span id="/2022/09/21/cron.html" class="visitors">Loading...</span></small>
<h1 class="p-name">使用Python制作可热载的定时调度器</h1>
<p class="view">by <a class="p-author h-card" href="//github.com/Mabbs">mayx</a></p>
<div id="outdate" style="display:none;">
<hr /><p>
这是一篇创建于 <span id="outime"></span> 天前的文章,其中的信息可能已经有所发展或是发生改变。
</p>
</div>
<script>
daysold = Math.floor((new Date().getTime() - new Date("Wed, 21 Sep 2022 00:00:00 +0800").getTime()) / (24 * 60 * 60 * 1000));
if (daysold > 90) {
document.getElementById("outdate").style.display = "block";
document.getElementById("outime").innerHTML = daysold;
}
</script>
<hr />
<b>AI摘要</b>
<p id="ai-output">这篇文章介绍了如何使用Python制作一个可热载的定时调度器作者开始时考虑了使用CRON表达式但发现它不够直观且不支持Windows。接着作者发现了schedule库用其以更自然的语言结构编写定时任务比APScheduler易于理解和使用。在创建调度器时作者开始用轮询监控文件变化但性能不佳转而引入了watchdog库以提高监测效率。通过FileEventHandler和Observer程序可以实时检测并重载修改的脚本。作者以一个简单的示例脚本和管理器函数阐述了整个过程并表达了对编写简洁、功能单一的程序的偏好。</p>
<hr />
<ul><li><a href="#起因">起因</a></li><li><a href="#探索过程">探索过程</a></li><li><a href="#代码">代码</a><ul><li><a href="#管理器">管理器</a></li><li><a href="#被管理脚本示例">被管理脚本示例</a></li></ul></li><li><a href="#感想">感想</a></li></ul>
<hr />
<main class="post-content e-content" role="main"><p>定时任务用CRON难道不够吗<!--more--></p>
<h1 id="起因">
<a href="#起因"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 起因
</h1>
<p>最近因为写的Python脚本比较多另外也有好多脚本都是定时运行的脚本然后Linux自带的CRON可能不是很直观不然为什么那么多人开发CRON表达式生成器😂另外CRON不能和脚本绑定到一起也不能在Windows上使用所以就想用Python来实现一个。 </p><p>
当然如果只是想用Python为一个程序做一个定时任务还是很简单的不想用CRON也能直接用一个死循环if判断时间来做比如想要每小时什么时候执行那就不停的判断直到那个时间到了以后然后去执行就好了。不过很多个脚本都用这种方法的话就显得太凌乱了想要停止哪个脚本还不太好整所以写一个定时调度器来统一管理所有的定时脚本也许会更好一些。</p>
<h1 id="探索过程">
<a href="#探索过程"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 探索过程
</h1>
<p>其实我最开始是想着把CRON表达式放到脚本里然后让管理器像配置文件那样读取它不过我自己解析CRON表达式有点麻烦一时也没找到好的办法。不过在找这个东西的时候找到了一个有意思的库叫做<a href="https://github.com/dbader/schedule">schedule</a>用pip就能安装。它可以用类似自然语言的语法结构去写定时语句看起来很有意思于是我就打算用这个库来写我的调度器。 </p><p>
写它不算很复杂这个库还是挺方便理解的比据说Python常用的什么APScheduler那个库好用多了那个玩意看着就不怎么人性化。功能很快就写好了但是有个问题就是既然我要写一个可以热载自动重加载的调度器用什么办法监视文件比较好呢最开始我实现的时候是想着用列出目录和stat方法来读取文件的元数据然后轮询如果内容有变化就进行重载。不过这样写起来也麻烦准确来说也不算准确而且性能也不怎么好还要轮询文件一多整个程序的执行效率就会变低。然后我想着之前我用的Django好像就有这样的功能它到底用的是什么方法呢一般我们运行Django项目的时候它第一句会写“Watching for file changes with StatReloader”那看来StatReloader就是它用来监视文件的模块听这名字怎么和我之前想的差不多😂另外也没有找到叫这个名字的库所以就算了我还是搜一下找找别的库吧。后来我找到了一个看起来不错的库叫做watchdog看起来好像用法也不算很复杂而且据说是用的内核的什么东西来监测性能比轮询好很多所以我就整了这个库感觉效果还不错可靠性也很好文件一有修改程序就会检测到然后进行重载。</p>
<h1 id="代码">
<a href="#代码"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 代码
</h1>
<h2 id="管理器">
<a href="#管理器"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 管理器
</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">schedule</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">importlib</span>
<span class="kn">from</span> <span class="nn">watchdog.observers</span> <span class="kn">import</span> <span class="n">Observer</span>
<span class="kn">from</span> <span class="nn">watchdog.events</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">reload_status</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">run_threaded</span><span class="p">(</span><span class="n">job_func</span><span class="p">):</span>
<span class="n">job_thread</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">job_func</span><span class="p">)</span>
<span class="n">job_thread</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">class</span> <span class="nc">FileEventHandler</span><span class="p">(</span><span class="n">FileSystemEventHandler</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">reload_status</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">reload_status</span> <span class="o">=</span> <span class="n">reload_status</span>
<span class="k">def</span> <span class="nf">on_any_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">event</span><span class="p">.</span><span class="n">is_directory</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">reload_status</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">observer</span> <span class="o">=</span> <span class="n">Observer</span><span class="p">()</span>
<span class="n">event_handler</span> <span class="o">=</span> <span class="n">FileEventHandler</span><span class="p">(</span><span class="n">reload_status</span><span class="p">)</span>
<span class="n">observer</span><span class="p">.</span><span class="n">schedule</span><span class="p">(</span><span class="n">event_handler</span><span class="p">,</span> <span class="s">"tasks"</span><span class="p">,</span> <span class="n">recursive</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">observer</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">reload_status</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">taskList</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="s">"tasks"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">task</span> <span class="ow">in</span> <span class="n">taskList</span><span class="p">:</span>
<span class="k">if</span> <span class="s">"__"</span> <span class="ow">in</span> <span class="n">task</span> <span class="ow">or</span> <span class="n">task</span><span class="p">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s">"."</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="s">"py"</span><span class="p">:</span>
<span class="k">continue</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">importlib</span><span class="p">.</span><span class="nb">reload</span><span class="p">(</span><span class="n">importlib</span><span class="p">.</span><span class="n">import_module</span><span class="p">(</span><span class="s">"tasks."</span> <span class="o">+</span> <span class="n">task</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"."</span><span class="p">)[</span><span class="mi">0</span><span class="p">])).</span><span class="n">run</span><span class="p">(</span><span class="n">run_threaded</span><span class="p">,</span> <span class="n">schedule</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Task </span><span class="si">{</span><span class="n">task</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s"> import failure"</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">if</span> <span class="n">reload_status</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Task change, reloading..."</span><span class="p">)</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">schedule</span><span class="p">.</span><span class="n">clear</span><span class="p">()</span>
<span class="k">break</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">schedule</span><span class="p">.</span><span class="n">run_pending</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="被管理脚本示例">
<a href="#被管理脚本示例"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 被管理脚本示例
</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">run_threaded</span><span class="p">,</span> <span class="n">schedule</span><span class="p">):</span>
<span class="n">schedule</span><span class="p">.</span><span class="n">every</span><span class="p">().</span><span class="n">second</span><span class="p">.</span><span class="n">do</span><span class="p">(</span><span class="n">run_threaded</span><span class="p">,</span> <span class="n">job</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">job</span><span class="p">():</span>
<span class="k">print</span><span class="p">(</span><span class="s">"The job is running."</span><span class="p">)</span>
</code></pre></div></div>
<p>脚本应该放到管理器所在文件夹下的“tasks”文件夹具体定时的写法可以看看<a href="https://schedule.readthedocs.io/en/stable/examples.html">schedule官方示例</a></p>
<h1 id="感想">
<a href="#感想"><svg class='octicon' viewBox='0 0 16 16' version='1.1' width='16' height='32' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg></a> 感想
</h1>
<p>感觉程序果然还是写的越简单越好功能也是越单一越好。据说APScheduler是用Python实现的像Java的Quartz那样的东西看着就很难受像我这个50行写的管理器看起来还挺不错的吧😁。</p></main>
<small style="display: block">tags: <a rel="category tag" class="p-category" href="/search.html?keyword=Python"><em>Python</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E7%A8%8B%E5%BA%8F"><em>程序</em></a> - <a rel="category tag" class="p-category" href="/search.html?keyword=%E6%A0%87%E5%BF%97"><em>标志</em></a> <span style="float: right;"><a href="https://gitlab.com/mayx/mayx.gitlab.io/tree/master/_posts/2022-09-21-cron.md">查看原始文件</a></span></small>
<h4 style="border-bottom: 1px solid #e5e5e5;margin: 2em 0 5px;">推荐文章</h4>
<p id="suggest-container">Loading...</p>
<script>
var suggest = $("#suggest-container");
$.get(BlogAPI + "/suggest?id=/2022/09/21/cron.html&update=" + lastUpdated.valueOf(), function (data) {
if (data.length) {
getSearchJSON(function (search) {
suggest.empty();
var searchMap = {};
for (var i = 0; i < search.length; i++) {
searchMap[search[i].url] = search[i];
}
var tooltip = $('<div class="content-tooltip"></div>').appendTo('body').hide();
for (var j = 0; j < data.length; j++) {
var item = searchMap[data[j].id];
if (item) {
var link = $('<a href="' + item.url + '">' + item.title + '</a>');
var contentPreview = item.content.substring(0, 100);
if (item.content.length > 100) {
contentPreview += "……";
}
link.hover(
function(e) {
tooltip.text($(this).data('content'))
.css({
top: e.pageY + 10,
left: e.pageX + 10
})
.show();
},
function() {
tooltip.hide();
}
).mousemove(function(e) {
tooltip.css({
top: e.pageY + 10,
left: e.pageX + 10
});
}).data('content', contentPreview);
suggest.append(link);
suggest.append(' - ' + item.date + '<br />');
}
}
});
} else {
suggest.html("暂无推荐文章……");
}
});
</script>
<br />
<div class="pagination">
<span class="prev">
<a href="/2022/09/05/trayicon.html">
上一篇:制作程序运行提示标志的历程
</a>
</span>
<br />
<span class="next">
<a href="/2022/10/05/rules.html">
下一篇Cloudflare规则新功能体验
</a>
</span>
</div>
<!--[if !IE]> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="/assets/js/gitalk.min.js"></script>
<div id="gitalk-container"></div>
<script>
var gitalk = new Gitalk({
clientID: '36557aec4c3cb04f7ac6',
clientSecret: 'ac32993299751cb5a9ba81cf2b171cca65879cdb',
repo: 'mabbs.github.io',
owner: 'Mabbs',
admin: ['Mabbs'],
id: '/2022/09/21/cron', // Ensure uniqueness and length less than 50
distractionFreeMode: false, // Facebook-like distraction free mode
proxy: "https://cors-anywhere.mayx.eu.org/?https://github.com/login/oauth/access_token"
})
gitalk.render('gitalk-container')
</script>
<!-- <![endif]-->
</section>
<!--[if !IE]> -->
<div id="landlord" style="left:5px;bottom:0px;">
<div class="message" style="opacity:0"></div>
<canvas id="live2d" width="500" height="560" class="live2d"></canvas>
<div class="live_talk_input_body">
<form id="live_talk_input_form">
<div class="live_talk_input_name_body" >
<input type="checkbox" id="load_this" />
<input type="hidden" id="post_id" value="/2022/09/21/cron.html" />
<label for="load_this">
<span style="font-size: 11px; color: #fff;">&#160;想问这篇文章</span>
</label>
</div>
<div class="live_talk_input_text_body">
<input name="talk" type="text" class="live_talk_talk white_input" id="AIuserText" autocomplete="off" placeholder="要和我聊什么呀?" />
<button type="submit" class="live_talk_send_btn" id="talk_send">发送</button>
</div>
</form>
</div>
<input name="live_talk" id="live_talk" value="1" type="hidden" />
<div class="live_ico_box" style="display:none;">
<div class="live_ico_item type_info" id="showInfoBtn"></div>
<div class="live_ico_item type_talk" id="showTalkBtn"></div>
<div class="live_ico_item type_music" id="musicButton"></div>
<div class="live_ico_item type_youdu" id="youduButton"></div>
<div class="live_ico_item type_quit" id="hideButton"></div>
<input name="live_statu_val" id="live_statu_val" value="0" type="hidden" />
<audio src="" style="display:none;" id="live2d_bgm" data-bgm="0" preload="none"></audio>
<input id="duType" value="douqilai" type="hidden" />
</div>
</div>
<div id="open_live2d">召唤伊斯特瓦尔</div>
<!-- <![endif]-->
<footer>
<p>
<small>Made with ❤ by Mayx<br />Last updated at 2026-01-01 00:00:18<br /> 总字数614622 - 文章数178 - <a href="/atom.xml" >Atom</a> - <a href="/README.html" >About</a></small>
</p>
</footer>
</div>
<script src="/assets/js/scale.fix.js"></script>
<!--[if !IE]> -->
<script src="/assets/js/main_new.js"></script>
<script src="/Live2dHistoire/live2d/js/live2d.js"></script>
<script src="/Live2dHistoire/live2d/js/message.js"></script>
<!-- <![endif]-->
</body>
</html>