前ふり
Ubuntu 18.10 にアップグレードした時に困ったこととして、アナログ時計アプリ cairo clock のパッケージが無くなってしまったこと。デジタル時計アプリは幾つかパッケージが有るけれど、アナログ時計は意外と少ない。もちろん古くから有る xclock は残っているが、これはウィンドウ枠やタイトルが消さない。
そんなわけで定番システム・モニター conky でアナログ時計を表示させることとしたのを機に、conky の設定を晒す。
設定を晒すだけなので、設定内容の意味はググって。
私のデスク・トップ
幾つかの Gnome Extension を入れて標準状態と異なっているが、conky の描画部分はデスク・トップ右上部。
 
表示しているのは
	- 今日、明日、明後日の天気予報 (現在地に出来れば良いが、やり方が不明なので URL 直打ち)
	
- 直近 3 つの地震
	
- アナログ時計
	
- 先月、今月、来月のカレンダー
	
- CPU の温度と使用量
	
- メモリー使用量
	
- CPU 負荷トップ 5 のプロセス
	
- ディスク使用量とディスク・アクセス
	
- ネットワーク状況 (Wi-Fi 時はアクセスポイント)
準備
 (2022/09/06 更新)
Ubuntu 標準の conky だと、テキスト情報をしか載せられないので、アナログ時計が表示できないので、次のパッケージをインストール。ウェブデータの取得には Python 使う。
sudo apt install -y conky-all python3-bs4
~/.config/conky/conky.conf
conky の設定ファイルは以前 ~/.conkyrc だったが最近はこちら。
conky.config で始まる部分が全体の設定で、conky.text で始まる部分が表示する項目。
少し変わった設定をしているのは
conky.config = {
	alignment = 'top_left',
	background = true,
	border_width = 1,
	cpu_avg_samples = 4,
	default_color = 'black',
	default_outline_color = 'white',
	default_shade_color = 'white',
	draw_borders = false,
	draw_graph_borders = true,
	draw_outline = false,
	draw_shades = false,
	use_xft = true,
	gap_x = 1565,
	gap_y = 40,
	maximum_width = 350,
	minimum_width = 5,
	minimum_height = 5,
	net_avg_samples = 4,
	no_buffers = true,
	out_to_console = false,
	out_to_stderr = false,
	extra_newline = false,
	own_window = true,
	own_window_class = 'Conky',
	own_window_type = 'desktop',
	stippled_borders = 0,
	update_interval = 1.0,
	uppercase = false,
	use_spacer = 'none',
	show_graph_scale = false,
	show_graph_range = false,
	double_buffer = true,
	override_utf8_locale = true,
	font = 'Ricty Diminished:style=Regular:size=12',
	own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
	own_window_transparent = true,
	own_window_argb_visual = true,
	text_buffer_size = 512,
	lua_load = '~/bin/conky/clock.lua',
	lua_draw_hook_post = 'cairo_clock',
};
conky.text = [[
${execi 600 ~/bin/conky/weather.py }
$hr
${execi 600 ~/bin/conky/earthquake.sh }
$hr
${execi 600 ~/bin/conky/ncal3.sh }
$hr
CPU: ${execi 1 cat /sys/class/thermal/thermal_zone0/temp | awk '{printf("%.1f°C", ($1=$1 / 1000))}'} ${cpu cpu0}% ${color 880000}${cpubar cpu0}${color black}
   ${cpugraph cpu0 40,0 000088 000088}
RAM:  $mem/$memmax $memperc% ${color 880000}${membar}${color black}
Swap: $swap/$swapmax $swapperc% ${color 880000}${swapbar}${color black}
Name              PID    CPU%   MEM%
${top name 1} ${top pid 1} ${top cpu 1} ${top mem 1}
${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}
${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}
${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}
${top name 5} ${top pid 5} ${top cpu 5} ${top mem 5}
Disk: ${fs_used}/${fs_size} $fs_used_perc% ${color 880000}${fs_bar}${color black}
   ${diskiograph 40,0 000088 000088}#
${if_existing /proc/net/route enp0s31f6}
networking: enp0s31f6
   Up: ${upspeed enp0s31f6}${goto 200}Down: ${downspeed enp0s31f6}
   ${upspeedgraph enp0s31f6 30,160 000088 000088} ${downspeedgraph enp0s31f6 30,160 000088 000088}
${else if_existing /proc/net/route wlp2s0}
networking: ${wireless_essid wlp2s0}
   Up: ${upspeed wlp2s0}${goto 200}Down: ${downspeed wlp2s0}
   ${upspeedgraph wlp2s0 30,160 000088 000088} ${downspeedgraph wlp2s0 30,160 000088 000088}
${endif}
]];
ネットワーク・デバイスを表す enp0s31f6, wlp2s0 は使用機器によって違うので、次のコマンドなどで調べる必要が有る
ip link
~/bin/conky/clock.lua
実際にアナログ時計を描画している Lua スクリプト
初めて書いた Lua スクリプトなので、きっともっとスマートに書き直せると思う (2024/11/07 更新)
#! /usr/bin/env lua
-- conky + lua + cairo の時計
-- https://inajob.hatenablog.jp/entry/20110306/1299386373 を参考にした
require 'cairo'
require 'cairo_xlib'
function conky_string_func()
	return os.date('%m/%d %H:%M')
end
cs, cr = nil
function conky_cairo_clock()
	if conky_window == nil then
		return
	end
	if cs == nil or cairo_xlib_surface_get_width(cs) ~= conky_window.width or cairo_xlib_surface_get_height(cs) ~= conky_window.height then
		if cs then
			cairo_surface_destroy(cs)
		end
		cs = cairo_xlib_surface_create(conky_window.display, conky_window.drawable, conky_window.visual, conky_window.width, conky_window.height)
	end
	if cr then
		cairo_destroy(cr)
	end
	cr = cairo_create(cs)
	local r = math.min(conky_window.width, conky_window.height) * 0.39 -- 全体の大きさ conky を表示する縦横幅の最低値を基準とし ×0.39 倍で使用
	local origine_x = conky_window.width - r/2 -2 -- 時計の中心 x 座標 (右端から半径分+αとして2)
	local origine_y = r/2 + 1 -- 時計の中心 y 座標 (左端からの半径分+αとして1 (ピッタリくっついていると見苦しく感じる))
	-- 文字盤
	cairo_set_source_rgba(cr, 0.93, 0.93, 0.93, 1) -- 文字盤の色指定
	cairo_set_line_width(cr, 1)
	cairo_arc(cr, origine_x, origine_y, r/2, 0, 2 * math.pi) -- 円描画
	cairo_fill_preserve(cr)               -- 文字盤塗りつぶし
	cairo_set_source_rgba(cr, 0, 0, 0, 1) -- 文字盤の枠の色指定
	cairo_stroke(cr)
	local ts = os.date('*t')
	local i,tr
	cairo_set_source_rgba(cr, 0, 0, 0, 1) -- 目盛りの色指定
	-- 時間、5分毎の目盛り
	cairo_set_line_width(cr, 2.0) -- 1時間 (5分) 毎の目盛りの太さ
	for i = 0,11 do
		tr = math.pi * 2 * i / 12
		cairo_move_to(cr, origine_x + 0.46*r*math.cos(tr), origine_y + 0.46*r*math.sin(tr))
		cairo_line_to(cr, origine_x + r/2*math.cos(tr), origine_y + r/2*math.sin(tr))
		cairo_stroke(cr)
	end
	-- 1分毎の目盛り
	cairo_set_line_width(cr, 1.0) -- 1分毎の目盛りの太さ
	for i = 0,59 do
		tr = math.pi * 2 * i / 60
		cairo_move_to(cr, origine_x + 0.475*r*math.cos(tr), origine_y + 0.475*r*math.sin(tr))
		cairo_line_to(cr, origine_x + r/2*math.cos(tr), origine_y + r/2*math.sin(tr))
		cairo_stroke(cr)
	end
	-- 運針
	-- hour
	tr = math.pi*2*(ts['hour']*60 + ts['min'])/(60*12) - math.pi/2
	cairo_move_to(cr, origine_x - 0.08*r*math.cos(tr), origine_y - 0.08*r*math.sin(tr))
	cairo_line_to(cr, origine_x + 0.04*r*math.cos(tr - math.pi/2), origine_y + 0.04*r*math.sin(tr - math.pi/2))
	cairo_line_to(cr, origine_x + 0.35*r*math.cos(tr), origine_y + 0.35*r*math.sin(tr))
	cairo_line_to(cr, origine_x + 0.03*r*math.cos(tr + math.pi/2), origine_y + 0.03*r*math.sin(tr + math.pi/2))
	cairo_close_path(cr)
	cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1) -- 針内部の色
	cairo_fill_preserve(cr)
	cairo_set_source_rgba(cr, 0, 0, 0, 1) -- 針の輪郭色
	cairo_stroke(cr)
	-- min
	tr = math.pi*2*(ts['min']*60 + ts['sec'])/(60*60) - math.pi/2
	cairo_move_to(cr, origine_x - 0.08*r*math.cos(tr), origine_y - 0.08*r*math.sin(tr))
	cairo_line_to(cr, origine_x + 0.04*r*math.cos(tr - math.pi/2), origine_y + 0.04*r*math.sin(tr - math.pi/2))
	cairo_line_to(cr, origine_x + 0.44*r*math.cos(tr), origine_y + 0.44*r*math.sin(tr))
	cairo_line_to(cr, origine_x + 0.04*r*math.cos(tr + math.pi/2), origine_y + 0.04*r*math.sin(tr + math.pi/2))
	cairo_close_path(cr)
	cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1)
	cairo_fill_preserve(cr)
	cairo_set_source_rgba(cr, 0, 0, 0, 1)
	cairo_stroke(cr)
	-- sec
	cairo_set_source_rgba(cr, 1, 0, 0, 1) -- 秒針の色指定
	cairo_move_to(cr, origine_x - 0.15*r*math.cos(math.pi*2*ts['sec']/60 - math.pi/2), origine_y - 0.15*r*math.sin(math.pi*2*ts['sec']/60 - math.pi/2))
	cairo_line_to(cr, origine_x + 0.425*r*math.cos(math.pi*2*ts['sec']/60 - math.pi/2), origine_y + 0.425*r*math.sin(math.pi*2*ts['sec']/60 - math.pi/2))
	cairo_stroke(cr)
	cairo_set_source_rgba(cr, 1, 0, 0, 1) -- 中心円の色指定
	cairo_set_line_width(cr, 1)
	cairo_arc(cr, origine_x, origine_y, 0.018*r, 0, 2 * math.pi)
	cairo_fill(cr)
	cairo_destroy(cr)
	cr = nil
end
~/bin/conky/weather.py (2025/01/07 更新)
天気予報を取得するスクリプト
https://tenki.jp/forecast/x/xx/xxxx/xxxxx/10days.html の部分は一度 日本気象協会 tenki.jp/ へ実際にアクセスして自分の居住地に合わせる必要がある
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:fileencoding=utf-8 fileformat=unix
import requests
import re
from bs4 import BeautifulSoup
from requests.exceptions import Timeout
try:
    html = requests.get('https://tenki.jp/forecast/x/xx/xxxx/xxxxx/10days.html', timeout=(3.0, 7.5))
except Timeout:
    print('')
    print('')
    print('')
else:
    soup = BeautifulSoup(html.content, "html.parser")
    # print(soup)  # 全体
    for s in soup.find_all('dd', class_='forecast10days-actab')[:3]:  # 最初の3日分
        print('{}{:^4s}{} {}'.format(
            re.sub(r'(?<=\d)月', '/', re.sub(r'(?<=\d)日', '', s.find_all('div', class_='days')[0].text)),
            s.find_all('div', class_='forecast')[0].text.replace(
                '時々', '|').replace('のち', '/').replace('一時', '.'),
            s.find_all('div', class_='temp')[0].text.replace('℃', '/', 1).replace('℃', '°C'),
            s.find_all('div', class_='prob-precip')[0].text))
~/bin/conky/ncal3.sh
	- 月を跨いでも前後の週の同じ曜日を解りやすくするために ncal コマンドと似た形式で出力したい
	
- しかし ncal コマンドの出力はパイプ経由すると当日の強調表示情報が消える
	
- 出力の横幅を少なくするために、月を跨る空白要素は詰めたい
といった理由でシェル・スクリプトを使っている
#!/bin/sh
case "$(uname)" in
	Darwin|*BSD) sed="sed -E";;
	*)           sed="sed -r";;
esac
transpose(){ # 行・列を入れ替える (出力はタブ区切り)
	grep -vE "^ +$" | # スペースのみの行は除外
	$sed 's/   / @ /g' | # 空白要素をダミーに置き換え
	nawk '{ # 実際の入れ替えルーチン
			for (i=1; i<=NF; i++) {
				a[NR,i] = $i
			}
		}
		NF>p {
			p = NF
		}
		END {
			for(j=1; j<=p; j++) {
				str=a[1,j];
				for(i=2; i<=NR; i++){
					str=str"\t"a[i,j];
				}
				print str
			}
		}' |
	$sed 's/@//g' # ダミー削除
}
calendar=$( LC_ALL=ja_JP.utf8 cal -3 ) # 日本語ロケールで前後3ヶ月取得
if cat <<-_EOF_ |
	$calendar
	_EOF_
	# 出力が「何月何年」で始まる形式と先頭行に年が表示され、次の行に月だけ表示の形式が有る
	grep -Eq "^\\s*[0-9]{4}$" ; then # 西暦だけの行が有るか? 有れば calendar データは 2 行目以降を使う
	calendar=$( cat <<-_EOF_ |
	$calendar
	_EOF_
	tail --line=+2 )
fi
# 1行目の 何月 何年取得
month=$( cat <<- _EOF_ |
	$calendar
	_EOF_
	head --line=1
)
# 曜日のみ+一週間分のみ
week=$( cat <<- _EOF_ |
	$calendar
	_EOF_
	head --line=2 |
	tail --line=1 |
	cut -b1-27
)
# 実際のカレンダー・日付部分
calendar=$( cat <<- _EOF_ |
	$calendar
	_EOF_
	$sed -e 's/ _\x8([0-9])_\x8([0-9]) /<\1\2>/g' \
		-e 's/ _\x8 _\x8([0-9]) / <\1>/g' |
	tail --line=+3
)
# 先月カレンダー
last_month=$( cat <<- _EOF_ |
	$calendar
	_EOF_
	cut -c1-21
)
# 今月カレンダー
this_month=$( cat <<- _EOF_ |
	$calendar
	_EOF_
	cut -c22-43 |
	$sed 's/(<[0-9]+>)/ \1 /g'
)
# 来月カレンダー
next_month=$( cat <<- _EOF_ |
	$calendar
	_EOF_
	cut -c44-65
)
cat << _EOF_ |
$month
_EOF_
	cut -b2-20,30-40,50-64 |
	sed -E -e 's/([0-9]+)      *([0-9]+月)/\1     \2/g'
cat << _EOF_ |
$week
$last_month
$this_month
$next_month
_EOF_
	transpose |
	$sed -e 's/([日月火水木金土])\t\t/\1    /g' \
		-e 's/([日月火水木金土])\t/\1 /g' \
		-e 's/\t\t/\t/g' \
		-e 's/\t$//g' \
		-e 's/\t(<[0-9][0-9])/\1/g' \
		-e 's/\t([<0-9][0-9])/ \1/g' \
		-e 's/\t/  /g' \
		-e 's/> />/g' \
exit 0