Skip to content

Commit

Permalink
v0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
obfusk committed May 9, 2021
1 parent 4592d31 commit 6d2952b
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 35 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ko_fi: obfusk
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI
on: [push, pull_request, workflow_dispatch]
jobs:
build:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version:
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- '3.10.0-alpha - 3.10'
- pypy3
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Test
run: make test
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include LICENSE.AGPLv3
include Makefile
34 changes: 34 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
SHELL := /bin/bash
PYTHON ?= python3
VERBOSE ?= --verbose

export PYTHONWARNINGS := default

.PHONY: all test clean cleanup install

all:

test:
$(PYTHON) -m kanjidraw.gui $(VERBOSE) --doctest
$(PYTHON) -m kanjidraw.lib $(VERBOSE) --doctest

clean: cleanup

cleanup:
find -name '*~' -delete -print
rm -fr kanjidraw/__pycache__/
rm -fr build/ dist/ kanjidraw.egg-info/
rm -fr .coverage htmlcov/

install:
$(PYTHON) -mpip install -e .

.PHONY: _package _publish

_package:
$(PYTHON) setup.py sdist bdist_wheel
twine check dist/*

_publish: cleanup _package
read -r -p "Are you sure? "; \
[[ "$$REPLY" == [Yy]* ]] && twine upload dist/*
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!-- {{{1
File : README.md
Maintainer : Felix C. Stegerman <flx@obfusk.net>
Date : 2021-05-09
Copyright : Copyright (C) 2021 Felix C. Stegerman
Version : v0.1.0
License : AGPLv3+
}}}1 -->

[![GitHub Release](https://img.shields.io/github/release/obfusk/kanjidraw.svg?logo=github)](https://github.com/obfusk/kanjidraw/releases)
[![CI](https://github.com/obfusk/kanjidraw/workflows/CI/badge.svg)](https://github.com/obfusk/kanjidraw/actions?query=workflow%3ACI)
[![AGPLv3+](https://img.shields.io/badge/license-AGPLv3+-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.html)
[![Sponsor](https://img.shields.io/badge/%E2%99%A5-support-violet.svg)](https://ko-fi.com/obfusk)

<!--
[![PyPI Version](https://img.shields.io/pypi/v/kanjidraw.svg)](https://pypi.python.org/pypi/kanjidraw)
[![Python Versions](https://img.shields.io/pypi/pyversions/kanjidraw.svg)](https://pypi.python.org/pypi/kanjidraw)
-->

## kanjidraw - handwritten kanji recognition

`kanjidraw` is a simple Python library + GUI for matching (the strokes
of a) handwritten kanji against its database.

You can use the GUI to draw and subsequently select a kanji from the
list of probable matches, which will then be copied to the clipboard.

The database is based on KanjiVG and the algorithms are based on the
[Kanji draw](https://github.com/onitake/kanjirecog) Android app.

## Requirements

* Python >= 3.5 (w/ Tk support for the GUI).

### Debian/Ubuntu

```bash
$ apt install python3-tk
```

## Installing

### Using pip

```bash
$ pip install kanjidraw
```

NB: depending on your system you may need to use e.g. `pip3 --user`
instead of just `pip`.

### From git

NB: this installs the latest development version, not the latest
release.

```bash
$ git clone https://github.com/obfusk/kanjidraw.git
$ cd kanjidraw
$ pip install -e .
```

NB: you may need to add e.g. `~/.local/bin` to your `$PATH` in order
to run `kanjidraw`.

To update to the latest development version:

```bash
$ cd kanjidraw
$ git pull --rebase
```

## License

### Code

© Felix C. Stegerman

[![AGPLv3+](https://www.gnu.org/graphics/agplv3-155x51.png)](https://www.gnu.org/licenses/agpl-3.0.html)

### KanjiVG (stroke data)

© Ulrich Apel

[![CC-BY-SA](https://licensebuttons.net/l/by-sa/3.0/88x31.png)](https://github.com/KanjiVG/kanjivg/blob/master/COPYING)

<!-- vim: set tw=70 sw=2 sts=2 et fdm=marker : -->
3 changes: 2 additions & 1 deletion kanjidraw/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .__main__ import *
from .lib import *
from .lib import __version__
60 changes: 36 additions & 24 deletions kanjidraw/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,40 @@
# {{{1
r"""
...
Tkinter GUI.
""" # }}}1

import sys

import tkinter as tk
import tkinter.font as tk_font
from tkinter import Tk, Button, Canvas, Frame, Label

from . import load_json, matches

def tkinter_main(): # {{{1
import tkinter as tk
import tkinter.font as tk_font
from tkinter import Tk, Button, Canvas, Frame, Label
NAME, TITLE = "kanjidraw", "Kanji Draw"
HEIGHT, WIDTH, BACKGROUND = 400, 400, "#ccc"
ROWS, LINEWIDTH, FONTSIZE = 5, 5, 35

def gui(): # {{{1
"""Tkinter GUI."""

win = Tk()
win.title("Kanji Draw")
win.title(TITLE)
win.columnconfigure(0, weight = 1)
win.rowconfigure(0, weight = 1)

height, width, bg, rows = 400, 400, "#ccc", 5
font = tk_font.Font(size = 35)
font = tk_font.Font(size = FONTSIZE)

data = load_json()
max_strokes = max(data.keys())
drawing, x, y, strokes, lines = False, 0, 0, [], []

def on_mousedown(event):
nonlocal drawing, x, y
if len(strokes) < max_strokes:
nonlocal drawing, x, y
drawing, x, y = True, event.x, event.y
strokes.append([x * 255.0 / height, y * 255.0 / width])
strokes.append([x * 255.0 / HEIGHT, y * 255.0 / WIDTH])
lines.append([])
enable_buttons()

Expand All @@ -60,7 +64,7 @@ def on_mouseup(event):
if drawing:
draw_line(x, y, event.x, event.y)
drawing, x, y = False, event.x, event.y
strokes[-1] += [x * 255.0 / height, y * 255.0 / width]
strokes[-1] += [x * 255.0 / HEIGHT, y * 255.0 / WIDTH]
update_strokes()

def on_undo():
Expand All @@ -81,11 +85,12 @@ def on_clear():
def on_done():
results_frame = Frame(win)
for i, (_, kanji) in enumerate(matches(strokes, data)):
results_frame.columnconfigure(i % rows, weight = 1)
results_frame.rowconfigure(i // rows, weight = 1)
col, row = i % ROWS, i // ROWS
results_frame.columnconfigure(col, weight = 1)
results_frame.rowconfigure(row, weight = 1)
btn = Button(results_frame, text = kanji, font = font,
command = on_select_kanji(results_frame, kanji))
btn.grid(column = i % rows, row = i // rows, sticky = "nsew")
btn.grid(column = col, row = row, sticky = "nsew")
results_frame.grid(row = 0, column = 0, sticky = "nsew")

def on_select_kanji(results_frame, kanji):
Expand All @@ -96,7 +101,8 @@ def handler():
return handler

def draw_line(x1, y1, x2, y2):
l = canvas.create_line(x1, y1, x2, y2, width = 5, capstyle = tk.ROUND)
l = canvas.create_line(x1, y1, x2, y2, width = LINEWIDTH,
capstyle = tk.ROUND)
lines[-1].append(l)

def disable_buttons():
Expand All @@ -114,15 +120,15 @@ def copy_to_clipboard(text):
win.clipboard_clear()
win.clipboard_append(text)

draw_frame = Frame(win)
btns = Frame(draw_frame)
btn_undo = Button(btns, text = "Undo", command = on_undo)
btn_clear = Button(btns, text = "Clear", command = on_clear)
draw_frame = Frame(win)
btns = Frame(draw_frame)
btn_undo = Button(btns, text = "Undo", command = on_undo)
btn_clear = Button(btns, text = "Clear", command = on_clear)
lbl_strokes = Label(btns, text = "Strokes: 0")
btn_done = Button(btns, text = "Done", command = on_done)
btn_done = Button(btns, text = "Done", command = on_done)
disable_buttons()

canvas = Canvas(draw_frame, height = height, width = width, bg = bg)
canvas = Canvas(draw_frame, height = HEIGHT, width = WIDTH, bg = BACKGROUND)
canvas.bind("<ButtonPress-1>", on_mousedown)
canvas.bind("<B1-Motion>", on_mousemove)
canvas.bind("<ButtonRelease-1>", on_mouseup)
Expand All @@ -132,16 +138,22 @@ def copy_to_clipboard(text):
btns.pack()
canvas.pack()
draw_frame.grid(row = 0, column = 0, sticky = "nsew")

win.mainloop()
# }}}1

def main():
if "--version" in sys.argv:
from .lib import __version__
print("{} v{}".format(NAME, __version__))
else:
gui()

if __name__ == "__main__":
if "--doctest" in sys.argv:
verbose = "--verbose" in sys.argv
import doctest
if doctest.testmod(verbose = verbose)[0]: sys.exit(1)
else:
tkinter_main()
main()

# vim: set tw=70 sw=2 sts=2 et fdm=marker :
Loading

0 comments on commit 6d2952b

Please sign in to comment.