This post is part of a series
In this article, I’ll describe how I integrated my tasks with SwayWM using Wofi.
Introduction
When tasks started pilling up and I constantly found myself in a bad situation because I forgot something had to be done, I went looking for a simple, CLI-based task management system to keep a log of simple items I needed to do.
I found the excellent Taskwarrior. I won’t go into details of how to use it, because it’s website has all this information and honestly it’s pretty simple.
Instead, I’ll focus on describing the custom scripts I developed to quickly interact with my tasks with a few keystrokes.
Check the video below to understand what I’m talking about.
%[https://youtu.be/8Y69oHxUNzk]
Screenshots
Keybindings
Keybinding | Effect | Mnemonic |
---|---|---|
$mod + t | Enter task mode | T is for task |
$mod + t + n | Create a new task | N is for new |
$mod + t + v | Create a new task pre-filled with the clipboard contents | V as in Ctrl+V |
$mod + t + d | Mark a task as Done | D is for done |
$mod + t + s | Sync tasks with server running task sync
|
S is for sync |
$mod + t + a | Annotate a task with multi-line text | A is for annotate |
$mod + t + i | Get full task information by running task info
|
I is for info |
$mod + t + r | Remove a task | R is for remove |
Sway Config
## Task
set $task_create exec ~/.config/wofi/scripts/task_create.py
set $task_create_from_clipboard exec ~/.config/wofi/scripts/task_create.py clipboard
set $task_done exec ~/.config/wofi/scripts/task_done.py
set $task_annotate exec ~/.config/wofi/scripts/task_annotate.py
set $task_info exec ~/.config/wofi/scripts/task_info.py
set $task_remove exec ~/.config/wofi/scripts/task_remove.py
set $task_sync exec notify-send "Task Sync" "\n$(task sync 2>&1)"
set $pomodoro_toggle exec ~/.config/waybar/modules/modules/pomodoro/pomodoro.sh toggle
set $pomodoro_end exec ~/.config/waybar/modules/modules/pomodoro/pomodoro.sh end
## task
mode "task" {
bindsym --to-code --no-repeat {
n $task_create; $dismiss; $default
v $task_create_from_clipboard; $dismiss; $default
d $task_done; $dismiss; $default
s $task_sync; $dismiss; $default
a $task_annotate; $dismiss; $default
i $task_info; $dismiss; $default
r $task_remove; $dismiss; $default
p $pomodoro_toggle; $dismiss; $default
b $pomodoro_end; $dismiss; $default
# back to normal: Enter or Escape
Return $dismiss; $default
Escape $dismiss; $default
}
}
bindsym $mod+t $mode_notify task; mode "task"
Waybar Config
"custom/pomodoro": {
"format": "{}",
"tooltip": true,
"exec": "cat $HOME/.cache/waybar/output/pomodoro",
"return-type": "json",
"interval": 1,
"on-click": "$HOME/.config/waybar/modules/modules/pomodoro/pomodoro.py toggle",
"on-click-right": "$HOME/.config/waybar/modules/modules/pomodoro/pomodoro.py end",
"on-click-middle": "$HOME/.config/waybar/modules/modules/pomodoro/pomodoro.py lock",
"on-scroll-up": "$HOME/.config/waybar/modules/modules/pomodoro/pomodoro.py time +60",
"on-scroll-down": "$HOME/.config/waybar/modules/modules/pomodoro/pomodoro.py time -60"
},
"custom/task": {
"format": "{}",
"tooltip": true,
"interval": 2,
"exec": "cat $HOME/.cache/waybar/output/task",
"return-type": "json"
},
The Scripts
task_create.py
This script is responsible for opening a Wofi dialog and creating a new task.
#!/usr/bin/env python
import sys
import json
import subprocess
paste = False
if len(sys.argv) == 2 and sys.argv[1] == 'clipboard':
paste = True
tasks = json.loads(subprocess.check_output(['task', 'export', 'status:pending']))
projects = {x['project'] for x in tasks if 'project' in x}
projects = sorted(list(projects))
projects.insert(0, 'none')
wofi = subprocess.Popen(['wofi', '-d', '-p', 'Select Project', '-k', '/dev/null'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proj = wofi.communicate('\n'.join(projects).encode())[0].decode()
proj = proj.strip()
if len(proj) > 0:
content = None
if paste:
try:
content = subprocess.check_output(['wl-paste', '-n', '-p']).decode()
except:
pass
if content:
desc = subprocess.check_output(['wofi', '-d', '--lines', '1', '-p', 'Task Description', '--search', content])
else:
desc = subprocess.check_output(['wofi', '-d', '--lines', '1', '-p', 'Task Description'])
desc = desc.strip()
if len(desc) > 0:
print(subprocess.check_output(['task', 'add', 'project:{}'.format(proj), desc]))
task_info.py
Displays full information about the select task.
#!/usr/bin/env python
import os
import json
import tempfile
import subprocess
tasks = json.loads(subprocess.check_output(['task', 'export', 'status:pending']))
sorted_tasks = sorted(tasks, key=lambda x: x['id'])
task_string = []
for t in sorted_tasks:
id = str(t['id']).zfill(3)
project = '{}'.format(t.get('project', 'none').strip())
description = t['description'].strip()
s = '<b>[{}]</b> - <b>{}</b>\n{}'.format(id, project, description)
task_string.append(s)
wofi = subprocess.Popen(['wofi', '-d', '-p', 'Select Task to Describe', '-k', '/dev/null', '-Ddmenu-separator=|'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = wofi.communicate('|'.join(task_string).encode())[0].decode()
if len(out) > 0:
tid = int(out[4:7])
fd, filename = tempfile.mkstemp()
try:
info = subprocess.check_output(['task', str(tid), 'info'])
with os.fdopen(fd, 'w') as f:
f.write(info.decode())
subprocess.check_output(['kitty', 'vim', filename])
except Exception as e:
print(str(e), flush=True)
finally:
os.remove(filename)
task_annotate.py
This script annotates a task with multi-line text using vim.
#!/usr/bin/env python
import os
import json
import tempfile
import subprocess
tasks = json.loads(subprocess.check_output(['task', 'export', 'status:pending']))
sorted_tasks = sorted(tasks, key=lambda x: x['id'])
task_string = []
for t in sorted_tasks:
id = str(t['id']).zfill(3)
project = '{}'.format(t.get('project', 'none').strip())
description = t['description'].strip()
s = '<b>[{}]</b> - <b>{}</b>\n{}'.format(id, project, description)
task_string.append(s)
wofi = subprocess.Popen(['wofi', '-d', '-p', 'Select Task to Annotate', '-k', '/dev/null', '-Ddmenu-separator=|'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = wofi.communicate('|'.join(task_string).encode())[0].decode()
if len(out) > 0:
tid = int(out[4:7])
fd, filename = tempfile.mkstemp()
try:
subprocess.check_output(['kitty', 'vim', filename])
with os.fdopen(fd, 'r') as f:
data = f.read()
if len(data) > 0:
print(subprocess.check_output(['task', 'annotate', str(tid), data]))
except Exception as e:
print(str(e), flush=True)
finally:
os.remove(filename)
task_done.py
This script marks a task as done.
#!/usr/bin/env python
import json
import subprocess
tasks = json.loads(subprocess.check_output(['task', 'export', 'status:pending']))
sorted_tasks = sorted(tasks, key=lambda x: x['id'])
task_string = []
for t in sorted_tasks:
id = str(t['id']).zfill(3)
project = '{}'.format(t.get('project', 'none').strip())
description = t['description'].strip()
s = '<b>[{}]</b> - <b>{}</b>\n{}'.format(id, project, description)
task_string.append(s)
wofi = subprocess.Popen(['wofi', '-d', '-p', 'Select Task to Complete', '-k', '/dev/null', '-Ddmenu-separator=|'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = wofi.communicate('|'.join(task_string).encode())[0].decode()
if len(out) > 0:
tid = int(out[4:7])
print(subprocess.check_output(['task', 'done', str(tid)])[0].decode())
task_remove.py
This script removes a task.
#!/usr/bin/env python
import json
import subprocess
tasks = json.loads(subprocess.check_output(['task', 'export', 'status:pending']))
sorted_tasks = sorted(tasks, key=lambda x: x['id'])
task_string = []
for t in sorted_tasks:
id = str(t['id']).zfill(3)
project = '{}'.format(t.get('project', 'none').strip())
description = t['description'].strip()
s = '<b>[{}]</b> - <b>{}</b>\n{}'.format(id, project, description)
task_string.append(s)
wofi = subprocess.Popen(['wofi', '-d', '-p', 'Select Task to Delete', '-k', '/dev/null', '-Ddmenu-separator=|'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = wofi.communicate('|'.join(task_string).encode())[0].decode()
if len(out) > 0:
tid = int(out[4:7])
wofi = subprocess.Popen(['wofi', '-d', '-p', 'Are you sure you want to remove task {}?'.format(tid), '-k', '/dev/null'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = wofi.communicate('No\nYes'.encode())[0].decode().strip()
if out == 'Yes':
rm = subprocess.Popen(['task', 'rm', str(tid)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = rm.communicate(input='yes\n'.encode())
print(out[0].decode())
所有评论(0)