每天用 Jupyter 寫 5 分鐘的日記
用 Jupyter 和 Python 在你的日常寫作背后實現(xiàn)一些自動化。
有些人會遵循傳統(tǒng),制定一年的計劃。不過,一年的時間很長,所以我以季節(jié)性的主題或軌跡來規(guī)劃。每個季度,我都會坐下來,看看即將到來的三個月的季節(jié),并決定在這段時間里我將努力做什么。
對于我最新的主題,我決定要每天寫一篇日記。我喜歡有明確的承諾,所以我承諾每天寫 5 分鐘。我也喜歡有可觀察的承諾,哪怕只是對我而言,所以我把我的記錄放在 Git 里。
我決定在寫日記的過程中實現(xiàn)一些自動化,于是我使用了我最喜歡的自動化工具:Jupyter。Jupyter 有一個有趣的功能 ipywidgets,這是一套用于 Jupyter Notebooks、JupyterLab 和 IPython 內(nèi)核的交互式 HTML 組件。
如果你想跟著本文的代碼走,請注意,讓你的 JupyterLab 實例支持組件可能有點復雜,請按照這些說明來進行設(shè)置。
導入 ipywidgets 模塊
首先,你需要導入一堆東西,比如 ipywidgets 和 Twisted。Twisted 模塊可以用來創(chuàng)建一個異步時間計數(shù)器:
import twisted.internet.asyncioreactortwisted.internet.asyncioreactor.install()from twisted.internet import reactor, taskimport ipywidgets, datetime, subprocess, functools, os
設(shè)置定時條目
用 Twisted 實現(xiàn)時間計數(shù)器是利用了 task.LoopingCall。然而,結(jié)束循環(huán)調(diào)用的唯一方法是用一個異常。倒計時時鐘總會停止,所以你需要一個自定義的異常來指示“一切正常;計數(shù)器結(jié)束”:
class DoneError(Exception):pass
現(xiàn)在你已經(jīng)寫好了異常,你可以寫定時器了。第一步是創(chuàng)建一個 ipywidgets.Label 的文本標簽組件。循環(huán)使用 divmod 計算出分和秒,然后設(shè)置標簽的文本值:
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_secondscurrent_seconds -= counttime_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 = reactord = call.start(1)d.addErrback(lambda f: f.trap(DoneError))return d, label
從 Jupyter 組件中保存文本
下一步是寫一些東西,將你輸入的文字保存到一個文件中,并提交到 Git。另外,由于你要寫 5 分鐘的日記,你需要一個能給你提供寫字區(qū)域的組件(滾動肯定是可以的,但一次能看到更多的文字就更好了)。
這就用到了組件 Textarea,這是一個你可以書寫的文本字段,而 Output 則是用來給出反饋的。這一點很重要,因為 git push 可能會花點時間或失敗,這取決于網(wǎng)絡(luò)。如果備份失敗,用反饋提醒用戶很重要:
def editor(fname):textarea = ipywidgets.Textarea(continuous_update=False)textarea.rows = 20output = 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 是為了避免每個字符都保存一遍并發(fā)送至 Git。相反,只要脫離輸入焦點,它就會保存。這個函數(shù)也返回 save 函數(shù),所以可以明確地調(diào)用它。
創(chuàng)建一個布局
最后,你可以使用 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
biu!你已經(jīng)定義了一個寫日記的函數(shù)了,所以是時候試試了。
journal()
Jupyter journal
你現(xiàn)在可以寫 5 分鐘了!



























