みんなの「作ってみた」

高速なデスクトップ英語辞書ツールを作った物語(英語コピ郎君)

2019/06/09

youwht
youwht
「Qiitaの殿堂」(http://youwht.ml/) 「赤の他人の対義語」 「平成の次の元号」 「写経を自動化」 「パワポリント君」etc... [email protected]

英語コピ郎君

クリップボードにコピーした英単語の意味がわかる、
高速な英語辞書ツールです。

下記から無料で入手できます。
https://www.vector.co.jp/soft/winnt/edu/se519844.html

Windows版/Mac版両方を上記ファイルに同梱しています。
(※Mac版はオマケ試作的な位置づけです)

英語を見たら片っ端から日本語にしてやりたいゼ!
お手軽に辞書をひきたいゼ!的な人にオススメです。

デモ(GIF)

GIFだけだと分かりにくいのですが、
範囲指定後に「ctrl + C」を押下しており、
それに対応して辞書検索されます。

特徴

①「超汎用」に使える。高速&ネット不要
 ・ローカルに辞書データを持ち、高速&ネット不要
 ・インプットがテキストデータならばなんでもよく、
  ブラウザ、エディタ、メール、IDE、PowerPointなど全部利用可能
 ・検索結果の再利用、再検索(結果からのジャンプ検索)が容易
 ・検索結果のコピペや保存が容易(検索履歴出力機能も)

②「無意識」に使える。作業の邪魔になりにくい
 ・自動的に表示非表示が切り替わる(オプション)
 ・いつも定位置に表示可能。色や文字サイズの変更可能
 ・原型への変換不要、複数形や過去形のままひける

③「高網羅」でなんでもひける
 ・英辞郎の230万語のデータをインポート可能
  (デフォルトでも6万5千語の辞書を搭載し、すぐ使えます)
 ・「含む」検索で関連語句や熟語、後方一致語句も出る
 ・日本語⇒英語の検索も実現

使い方

下記のページよりダウンロードしてEXEを起動するだけ。
https://www.vector.co.jp/soft/winnt/edu/se519844.html

Windows版/Mac版両方を上記ファイルに同梱しています。
(※Mac版はオマケ試作的な位置づけです)

英辞郎データがあればさらに便利に使えます。
500円で230万語のデータが使えるため大変オススメです。
https://booth.pm/ja/items/777563

辞書データ加工ツールを起動して、上記で購入した英辞郎データを選択。
生成されたファイル「dicdata.pidic」をEXEと同じパスに
置いておけば、次回起動時からそのデータが利用されます。

本記事の概要

  • 作ったもの=英語コピ郎君 の紹介
    • 上述までの内容で済
  • 本題:開発経緯の物語
    • 方針やコンセプトをどう考えたか?
  • 技術話:どのようにして実現したか悩んだ物語
    • 実は全く別に見える3つの記事とも関連が!?

の3点を語るものです。

本題:開発経緯の物語

背景①:Chrome拡張版 「Mouse Dictionary」

あれ、似たような(もっとカッコいい)ツールをどこかでみたな・・・。
という方は、下記のwtetsu氏のツールです。
Qiitaでも大きな話題になった素晴らしいツールです。

 Chrome拡張の高速な英語辞書ツールをつくりました(Mouse Dictionary)

正直に、Chromeブラウザ上のみでご使用になる場合は、
Mouse Dictionaryの方が早いしオシャレです。

本ツール「英語コピ郎君」は
「Mouse Dictionary」のデスクトップアプリ相当、
マウスオーバ ⇒ クリップボード経由 で速度も落ちた代わりに、
Chrome以外でも何でも使えるようになったり、
自動非表示切り替えや、結果再検索等が出来るようになったものです。

背景②:中国語版 「中国語よめ~る君」

数年以上ず~と昔、平成の時代から、
中国語版の、同等以上の機能&独自辞書内蔵のソフトを作っていました。

中国語よめ~る君(VectorからDL=Win版のみ)

中国語には「ピンイン」や「声調」という発音記号があり、
言語習得上とても重要な要素です。それを扱うツールです。
日本語での近い概念としては、漢字に対して、
音読み訓読みを自動判断してルビと抑揚を表示してくれるようなツールです。

中国語学習ソフト界隈(どんな小さな界隈だよw)においては
恐らく1、2を争うほど有名なツールです。

2016年に、ソースを流用してあまり手間なく作れたので
英語版も作ってあったのですが、そちらはあまり使われることは無かったです。
中国語版と違い競合が多すぎる、無料の辞書データのみで語彙数が貧弱、
そもそもUI設計がダサいし英語向きではない、など様々な理由が考えられました。

ある日、森の中クマさんに出会って、開発開始!

そんなある日、前述の「Mouse Dictionary」の記事を拝読し、
大変感銘を受けました。
近い用途のツールも開発公開済みだったのに、という一抹のくやしさも感じました。
また、英辞郎のデータが購入出来ることを初めて知りました。

私のツールでも「英辞郎」を使えるようにしてみたい、
と思うのは自然なことでしょう。

しかし、本当に作る意味があるのか?
また、どのように作るのか?
どんなコンセプトで作るのか?
など、いろいろ考慮しなければいけません。

本記事は、そうした個人開発で考慮したストーリー(物語)と、
その実現に使った技術要素を記載しておこうと思い書きました。

洗濯とか柴狩りとかの日常系から始まらずに、
開始早々にクマさんに出会う衝撃的展開!
クマさんと「お逃げなさい」のギャップ
(実は、なぜ逃げないのか?の反語表現である説も)
そして、なぜか追跡をかけるクマさん。
起承転結どころか転転転結レベルのカオス。
これに敬意を表し、ある日突然の衝撃を受けること、を
「森のクマさんに出会う」現象と呼ぶことにしました。

類似ツールとの比較検討

作成の意義を問うには、明らかに新規な場合を除き、
既存の近い手段との特徴比較が王道と言えます。

まず、Macにはデフォルトでもローカル辞書ツールがある、
という点は、私がWindowsの方を良く使うので大きな問題はなさそうです。

最大の比較対象はやはり「Mouse Dictionary」です。
「マウスオーバーのみでひける」点がとてもスゴイですね。

「Mouse Dictionary」の最大の弱点は、
Chromeブラウザ上でしか使えないことでしょう。
とはいえ、英単語を調べたい時は
Chrome上で見ている時が最も多いかもしれません。
Chrome以外でもどこでも使えます、という特徴以外にも、
もっと本ツール独自のコンセプトが欲しいところです。

そこで「Mouse Dictionary」をさらに勉強させていただき、
2点、差別化できる可能性に気付きました。

一つ目は「表示位置」です。
ブラウザのワクの中に表示する性質のため、
あるレイアウトのページでは右下にあって欲しいし、
また別のレイアウトのページでは左下にあって欲しい、
などの表示位置のわずらわしさを多少感じます。
そこで、一つ目のテーマは、
「表示位置の固定」「自動表示非表示切り替え」による、
目線的な意味での邪魔になりにくさ、を考えました。
(デスクトップ版ならブラウザのワク外の固定位置に表示できるので、
 Webページのレイアウトによらずに使えます。
 自動で表示非表示が切り替わればさらに便利です)

二つ目は「検索結果テキストの汎用性」です。
通常の「電子辞書」ではよく "ジャンプ機能" を使って、
調べた単語の結果に表示されている別の単語を
再検索するような使い方を良く行います(少なくとも自分は)。
また "単語登録" で調べた結果を保存したりもします。
PC上で辞書を引く場合は、調べた結果テキストを
コピーして別の場所に張り付けたい、などもあるでしょう。
「Mouse Dictionary」ではマウスオーバーという性質上、
そうした再検索や、結果のコピペが難しいことが分かりました。

「デスクトップ版」に比べこれらの利点があるならば、
差別化要素として十分でしょう。

コンセプトが決まった!

目指す方向性が見えてきました。
無意識」:表示位置を気にする率を減らす系
超汎用」:デスクトップ版で汎用性があり、さらに結果の再利用が容易

さらに、個人的に和英も欲しかったため、
そもそも英辞郎が使えるように、というのと合わせて、
高網羅」の概念も追加しました。
和英だけでなく「包含/後方一致」等も検索HITします。

実際はこれらの名前は、開発がほぼ終わった後に名付けましたが、
作る際の「イメージ」は早期から考えながら作っていました。
RPGなどのキャラ育成の際に、どのパラメータにステータスを振るのか?
という育成方針と同じ話です。

これらの特徴があれば、たとえ「劣化版」などと言われようとも、
別の価値観を持つ人(自分と近い価値観を持つ人)
には響くツールになるはずです。

個人開発においては何かに「勝ち」にいく必要は無いでしょう(※勝てませんし)。
"上位互換" を目指すのではなく、"独自性" が重要だと考えています。

「こっちのツールの方がいいよ」「このやり方の方がいいから使わん」
「そもそも英語を読む時に日本語の意味をあてはめてはイカン!」とかまで、
様々な意見を言う方がいらっしゃるでしょう。
その意見も否定はしませんが、全て不毛な会話です。
一部の人(最悪自分だけでも)が気に入ればいいのです!
個人開発していらっしゃる方は、全方位に完璧なものよりも、
一点突破的なものを気楽に作れば良いと思います。
Qiitaの記事についても同様のことが言えると思います。

技術話:どのようにして実現したか悩んだ物語

背景②の「中国語よめ~る君」は、
JavaScript + Electron によって作成していました。
一方、PythonでもGUIアプリやEXEを作れるということを知ったため今回は、
Python + Tikinter + Pyinstaller で開発してみることにしました。
どうせ作るならば一度失敗を認めてゼロからスタートしてみよう、
とのリセット的な心境もあったのでしょう。

この技術を使ってみたいから、という選定方法は本意ではありません。
様々な課題に直面することになりました。(若干の失敗感・・・)

一方で"知見の獲得"も開発目的の一つとしたことで、
同様技術を流用した様々な「寄り道」も楽しむことができました。

「寄り道」 = 全く無関係そうに見える以下3記事は、
実はこの記事に関連する一連の流れなのでした!!

ぱっと見ではこの記事含めて同じ人が書いていると思えないほど関連性が無いですね
本記事はこれらの記事の「裏番組」であり「本体」でもあります

「寄り道」と共通で使っている技術要素

英辞郎データの検索性能の確認

今回の開発で最も気になった点は、
英辞郎データ=230万語に対する検索性能です。

英辞郎データは、非圧縮時で130MBほどの重さがあります。
ローカル検索といっても、毎回ファイルから読みだしていては遅い気がします。
初回ロード後はオンメモリで扱う方針でいきますが、
230万語に対する検索ってどの程度早いのか、は不明でした。

その検索性能の確認/検討をしていた際の寄り道が以下の記事です。
k8sとなんの関係があるんだ?という方はぜひご一読ください。

結論としては、
230万語に対してそのまま検索しても十分に高速、というものでした。
(ただし、デフォルト辞書=数万語程度と比較するとやはり遅さは感じられる)

「英語コピ郎君」の検索時の処理時間として、2点の遅延箇所があります。

  • 単語検索の時間
  • クリップボード監視間隔(設定で変更可能=過度に短くするとCPU影響大)

クリップボード監視間隔の方が影響は大きいという印象で、
単語検索側はかなり高速でした。

一応、検索を高速化する工夫として、辞書データの持たせ方を、
頭文字のアルファベットごとにデータを分けて所持し、
毎回「全検索」ではなくて、検索対象を頭文字ごとに絞る
(例:カツオ(Katsuwonus)の検索は「K」の辞書だけに対して実施)
という案も試してみました。
が、十分早いために最終的には絞ることはやめました。
例えば「wonus」で検索をしても、「Katsuwonus」が出てきます!

結果、英辞郎データを用いる場合の課題は、
「Pyinstallerは起動が遅い」という点と合わせて、
「アプリの起動時」にファイル読み込みで立ち上がりが遅くなってしまう、
という点でしょうか。
毎回英辞郎データフォーマットからパース/加工するのではなく、
こちらが使うように加工したデータをPickleで事前保存/読み込みをする、
という方式にしていますが、それでも起動は少しもたつきます。
半常駐的に使いたいアプリなのでこの起動時数秒問題の解決はあきらめました。

いずれにせよ、
「せっかく英辞郎データを購入したので、遊んで元を取る」
という問題は十分なほど解決できたかもしれません。

クリップボード操作機能

pyperclipによるクリップボード操作は、
下記の記事でも実施しています。
写経の変換部をつかさどる重要な要素でした。

英語と写経という完全にかけ離れた世界をも
Pythonという"蛇道"な架け橋で繋ぐことが出来るのですね!

「サ道」(サウナ道) が最近とても流行っていますよね。
それにならって、
ヘンテコなPython開発のことを
「蛇道」と呼ぶことを提唱いたします。

PyinstallerによるEXE化

Pythonでもデスクトップアプリが作れる!
EXEの作成はPyinstallerを使いました。

下記の記事で実施しているのと同様の方法です。

パワポエンジニアの憂鬱を軽減する誤字/表記揺れ検出ツールを作った物語

一方で、「英語コピ郎君」では
大きな課題が二つ発生しました。

  • ①GUI対応(パワポリント君はCUIツール)
  • ②Mac&Winのマルチ環境対応

ここは一番の難所であり、
JavaScript + Electron で開発した時との比較を含めて後述します。

結論としては、もし今後同じように
デスクトップアプリを作りたいという方がいらっしゃいましたら、
インターフェース/UIにこだわりたい場合は特に、
「JavaScript + Electron」で作ることを推奨します。
 ※Pythonじゃないと実現しにくい機能も沢山あるため、
  それらの機能を用いたいということでなければ、という前提です。

JavaScriptならばスマホアプリまで、同じコードで作れます!

Android&iOS&Win&Macが同じコードで動く!超マルチアプリを作ろう[Monaca × Electron]

GUI対応 = Tkinter は、軽度なツールなら有用

PythonでGUIを作る方法としては、
Tkinter、kivy、wxPython、PyQtなど、
いくつかのライブラリ候補があります。

今回は Tikinter を選択しました。
主に以下の特徴があります。

  • Pythonの標準で付属
  • 習得が比較的容易(主観)
  • クロスプラットフォーム対応(Windows/Mac)

他を試しているわけではないのですが、
以下Tkinterに対する「個人の感想」です。

軽度なインターフェースを作るには
生産性や習得容易性的に十分すぎるほど有用。
しかし、ちょっと凝ったことをしたい場合や、
見た目を変更したい場合に難しくなります。

一方で JavaScript + Electron ならば、
HTML5系のインターフェースが全て使えるため、
ネット上の多数のノウハウがそのまま流用可能です。

今回のようにテキスト主体のちょっとしたツールレベルならば、
Tkinterもそう間違いではないと思います。
しかし、ビジュアルにこだわりたい場合、
Tkinterはやめた方がよいでしょう。
Pythonならば「kivy」の方が良いという話も聞きます。

Tkinterは軽度な業務用アプリを作るには向いていそうですね。

Mac&Winのマルチ環境対応 ⇒ ハマる

Windows/Mac両方に対応させる際には
意外と問題が多発しました。
一部、最後まで解決できていない問題
=作ったけど消去した機能、もあります。

ハマるポイントとその回避方法をいくつか書きます。

ハマり1:テーマ付きウィジットttkを使わない方が楽

Tkinterには、テーマ付きウィジェットの
Tkinter.ttkがあり、
こちらを使うと"ほぼ"同様のコードで、
より洗練されたインターフェースに出来るという触れ込みです。

間違ってはいないのですが、
WindowsとMac両方で動作させる場合に、
それぞれのOSごとで「テーマ」の違いが広がってしまい、
面倒になってしまいました。

また、Web上でTkinterについて調べた場合も、
「tk」のコードと「ttk」のコードが混ざっていて、
"ほぼ"同様にどちらでも動くのですが、
微妙にオプション指定方法などが異なる場合があり、
コピペでそのまま動いたり動かなかったり注意が必要です。

今回は洗練されたインターフェースよりも"作りきること"、
を目標として、ttkは最後の方で全て抹消しました。
このレベルのツールなら大して見た目変わらんし。
「ウィンドウのフチを消して移動できなくするオプション」などを
取り下げることにしました。

ハマり2:OSごとの使用可能フォントの違いに留意

フォントを設定する際に、それぞれのOSで
使用できるフォントが違うことにも留意した方が良いでしょう。

詳細は以下のリンク先で教えていただきました。
http://memopy.hatenadiary.jp/entry/2017/06/11/112619

使用可能フォントの確認
import tkinter as tk
import tkinter.font as font

root = tk.Tk()
print(font.families())

ハマり3:Pyinstallerの動作はOSごとに少し違う

Mac版ではPyinstallerの動作がいろいろ異なるようです。
アイコンの設定が効かなかったり、
「.app」化の際にスクリプトがそのまま露呈してしまう、
などです。(何か解決方法があるのかもしれません)

そもそもMac版はあまり重視しておらず、
同じコードで動くならこちらもコンパイルしておくか、
という程度で始めたワリに、問題が多すぎて、
いろいろ逃げまくっています。
最後には、Mac向け出力ではなくてLinux向け出力を同梱しており、
「.app」形式の配布はやめました。
Macでも使うよ、という人が増えるなら今後また考えます

JavaScript + Electron における開発でも、
Mac版で多少の違いは発生するのですが、
それよりも差異が大きいという印象です。
やはりWeb関連の技術の方がOS間の違いは少ない気がします。

その他さまざまな工夫

設定ファイルの作り方 = configparser

デスクトップアプリの設定値管理としては、
iniファイル を使いたくなります。
今回のツールでも、背景色などをiniファイルから変更可能です。

Pythonでiniファイルを扱うには、
configparser が便利です。
以下にその使い方を示します。

iniファイルの例
[VisualOption]
BgColor = #eeeeee

[Other]
INTERVAL = 10
configparserの使い方
import configparser

#設定ファイルの読み込み
try:
    config = configparser.ConfigParser()
    config.read(OPTION_SETTING_PATH)
    try:
        INTERVAL = config.getint('Other', 'INTERVAL')
        if INTERVAL < 1 :
            INTERVAL = 1
    except:
        print("INTERVAL-SET-ERR!!")
        pass
    try:
        BgColor = config.get('VisualOption', 'BgColor')
    except:
        print("BgColor-SET-ERR!!")
        pass
except:
    print("no-ok-configfile!!")
    pass

ユーザが触って良い値は、iniファイル + configparser で指定しています。
めんどくさいので設定を作りたくない 触らないで欲しい値は、
pickle でバイナリ保存/呼び出し、しています。
このようなライブラリが簡単に見つかる点はPythonは便利です!

クリップボード監視 or キーボードハンドリング

最も悩ましかった点は、
何をキック要因として検索を実行するか?です。

当初案としては、
キーボードイベントを監視して、
ctrl + C が押されたことに対応して検索する、という案でした。

もちろん、アプリケーションそのものに対する「ctrl + C」の
キーボードイベントはハンドリングできるのですが、
別のアプリを利用中であったり、最小化中であったりすると
この方法は上手くいきませんでした。
アプリケーション自体がアクティブじゃない場合、
イベントをとれない場合がありました。

「最小化」側を抑制してイベントをとる方法もあるでしょう。
例えば検索結果を「表示」する際には、
強制的にウィンドウを最前面にする処理を行っています。
アプリを常時アクティブ状態にしつつも、
「透明化」によってそれを感じなくさせる、などの小技もあり得たのですが、
"お行儀がよく無い" ので別な方法を考えることにしました。
使用時に一時的に最前面表示(アクティブ化ではない)、は自然な挙動ですが、
使用していない時にも自身を主張しすぎる挙動は良くないでしょう。
ユーザが最小化を命じたら素直に最小化されるべきです。

そこで「クリップボードの変更監視」の方法で実現することにしました。
こちらの方法は定期的に変更有無を確認する方法であったため、
バックグラウンド状態でも問題なく動いたのです。
イベントハンドリングではなく定期確認なので、
イベント着火状態によらず確実です。

英語、日本語、検索方法をどうするか?

どのような文字列が入力された場合にどんな検索を行うのか?
についてはパターンがいろいろで面倒でした。

コピペされた文字列によって様々な分岐を考える必要がありました。

  • 辞書検索ではない普通のコピペ作業の場合に検索をしないようにしたい
  • 日本語の場合は、和英として日本語側から検索をかけたい
  • 英単語の場合は、多少の空白や改行は無視して検索をかけたい
  • 英単語の場合は、過去形や複数形を原型に戻して検索をかけたい
  • (けど、毎回必ず戻すわけではなく、そのままでHITするならそちらを優先)
  • アルファベット1文字ならどうするか?
  • 「部分一致」した場合にどのように一致したものを優先度をあげるか?
  • 部分一致、原型戻し、など複数パターンがある場合にどれが優先度が高い? などなど

技術的に難しい内容ではないのですが、
様々なパターン、使い勝手を考えながら作るのは面倒でした。
「辞書をひく」って単純なようで結構複雑なんですね!

自動で隠れたり、位置を固定したりの工夫

無意識」に使えるようにするためには、
自動的に表示したり、隠れたりすることがポイントになります。
(AutoHide = ON に設定するとこの機能を試せます。
 デモ用のGIF動画においても、通常の編集的な動作時には
 英語コピ郎君がポップアップしない様子を映しています)

HIT件数が適正な値になっている場合は、検索したと判断してポップアップ。
HIT件数が0件であったり多すぎる場合は、ポップアップしない。
また、マウスカーソルの移動に従って再度隠れたり。
その間にユーザ操作で最小化やアクティブ化していた場合どうするか?
など、こちらもそれなりに様々なパターンがありました。

また、表示位置やウィンドウの大きさも、
起動のたびに再調整しなくてよいように、
変更時にはPickleを使って外部ファイルに保存するようにしています。

「辞書」ツールを作るといっても、
本当の検索関連のコードについては全体の2割くらいの手間で、
残り8割はその他こういった周辺機能の開発に費やすことになります。

おしまい。あとがき。

開発に至ったきっかけ、コンセプト、その実現方法までを
いろいろ書いてきました。
それでも
ここに記すには余白が狭すぎる (by ピエール・ド・フェルマー)
というくらいまだ書けていないことがいろいろあります。

最初にアイデアが100個くらいあったとしたら、
実際にコア機能だけ作ろうというのは5個くらいで、
周辺部までまじめに作ろうと思うのはそのうち1個くらい、でしょうか。
でもその最後の1個が一番手間がかかるんですよね。
Qiitaに書くまでをゴールとするならばコア機能(5個)段階の時点で
ネタにはなります。例:写経自動化、対義語自動生成など。
毎回全部書いているわけじゃないです。

なんかそういう個人開発のリアルな所、
綺麗じゃないごちゃごちゃダサい所を書いておこうと思いました。

技術的には全く大したことなく、
目的を達成しさえしていれば、課題に正面から向かわずに
「蛇道」で回避しながらのPythonアプリ開発でした。

世の中には新しい技術を追うのがすごい人もたくさんいらっしゃいます。
それを使って「完璧」と言えるようなツールを作る方もいらっしゃいます。
そのような人々はすごいなー、と思いつつも
個人的には そういう人には勝てないので最初から勝負しにいかない方針で
「枯れた技術の水平思考」
のほうが自分なりの創意工夫をいろいろできて楽しいのではないか?
と思うのであります。
ともに「蛇道」を歩みましょう。

以上、「英語コピ郎君」の開発物語でした。
https://www.vector.co.jp/soft/winnt/edu/se519844.html

「英語」がテーマという時点で、私の求める独自性ではなく、
既に一般側の方に近くなってしまっておりその感じがイヤで、
こんなことを書きたくなってしまった気がします。

長文お付き合いいただきありがとうございました。