使用 Jupyter 每天写五分钟日记

使用 Jupyter 和 Python 为您的日常写作添加一些自动化。
77 位读者喜欢这篇文章。

有些人遵循制定新年决议的传统。然而,一年时间太长了,所以我计划一个季度主题或轨迹。每个季度,我都会坐下来看看接下来三个月的季节,并决定在那段时间里我要做什么。

对于我最新的主题,我决定每天写日记。我喜欢有明确的承诺,所以我承诺每天写五分钟。我也喜欢有可观察的承诺,即使只是对我自己而言,所以我将我的条目放在 Git 中。

我决定围绕我的日记编写一些自动化程序,并转向我最喜欢的自动化工具:Jupyter。Jupyter 的一个有趣功能是 ipywidgets,这是一组用于 Jupyter Notebooks、JupyterLab 和 IPython 内核的交互式 HTML 小部件。

如果您想跟随本文中的代码进行操作,请注意使您的 Jupyter lab 实例支持小部件可能会有点令人沮丧。请按照 这些说明 进行设置。

导入 ipywidgets 模块

首先,您需要导入很多东西,例如 ipywidgets 和 Twisted。Twisted 模块有助于创建异步计时器

import twisted.internet.asyncioreactor
twisted.internet.asyncioreactor.install()
from twisted.internet import reactor, task
import ipywidgets, datetime, subprocess, functools, os

设置定时条目

使用 Twisted 实现计时器利用了 task.LoopingCall。但是,结束循环调用的唯一方法是使用异常。倒计时时钟总是会停止,因此您需要一个自定义异常来指示“一切正常;计数器已完成”

class DoneError(Exception):
    pass

现在您已经编写了异常,您可以编写计时器。第一步是创建一个带有文本标签小部件的 ipywidgets.Label。循环使用 divmod 来计算分钟和秒,然后设置标签的文本值

def time_out_counter(reactor):
    label = ipywidgets.Label("Time left: 5:00")
    current_seconds = datetime.timedelta(minutes=5).total_seconds()
    def decrement(count):
        nonlocal current_seconds
        current_seconds -= count
        time_left = datetime.timedelta(seconds=max(current_seconds, 0))
        minutes, left = divmod(time_left, minute)
        seconds = int(left.total_seconds())
        label.value = f"Time left: {minutes}:{seconds:02}"
        if current_seconds < 0:
            raise DoneError("finished")
    minute = datetime.timedelta(minutes=1)
    call = task.LoopingCall.withCount(decrement)
    call.reactor = reactor
    d = call.start(1)
    d.addErrback(lambda f: f.trap(DoneError))
    return d, label

从 Jupyter 小部件保存文本

下一步是编写一些东西,将您在日记中键入的文本保存到文件中并将其提交到 Git。此外,由于您将写日记五分钟,您需要一个小部件来为您提供书写空间(滚动始终是可能的,但一次看到更多文本会更好)。

这使用了小部件 Textarea,这是一个您可以书写的文本字段,以及 Output 来提供反馈。这很重要,因为 git push 可能需要时间或失败,具体取决于网络。如果备份失败,则向用户发出反馈警报非常重要

def editor(fname):
    textarea = ipywidgets.Textarea(continuous_update=False)
    textarea.rows = 20
    output = ipywidgets.Output()
    runner = functools.partial(subprocess.run, capture_output=True, text=True, check=True)
    def save(_ignored):
        with output:
            with open(fname, "w") as fpout:
                fpout.write(textarea.value)
            print("Sending...", end='')
            try:
                runner(["git", "add", fname])
                runner(["git", "commit", "-m", f"updated {fname}"])
                runner(["git", "push"])
            except subprocess.CalledProcessError as exc:
                print("Could not send")
                print(exc.stdout)
                print(exc.stderr)
            else:
                 print("Done")
    textarea.observe(save, names="value")
    return textarea, output, save

continuous_update=False 是为了不保存和发送到 Git 的每个字符。相反,它会在您失去焦点时保存。该函数还返回 save 函数,因此可以显式调用它。

创建布局

最后,您可以使用 ipywidgets.VBox 将所有这些放在一起。这是一个包含几个小部件并垂直显示它们的东西。还有一些其他排列小部件的方法,但这很简单并且足够好

def journal():
    date = str(datetime.date.today())
    title = f"Log: Startdate {date}"
    filename = os.path.join(f"{date}.txt")
    d, clock = time_out_counter(reactor)
    textarea, output, save = editor(filename)
    box = ipywidgets.VBox([
        ipywidgets.Label(title),
        textarea,
        clock,
        output
    ])
    d.addCallback(save)
    return box

唷!您已经定义了一个用于写日记的函数,现在是时候尝试一下了。

journal()

您有五分钟时间——开始写作吧!

接下来阅读什么
标签
Moshe sitting down, head slightly to the side. His t-shirt has Guardians of the Galaxy silhoutes against a background of sound visualization bars.
Moshe 自 1998 年以来一直参与 Linux 社区,在 Linux“安装聚会”中提供帮助。他自 1999 年以来一直在编写 Python 程序,并为核心 Python 解释器做出了贡献。Moshe 自从 DevOps/SRE 这些术语出现之前就一直从事相关工作,他非常关心软件可靠性、构建可重复性以及其他此类事情。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.