2.0 版アドオンの移植
- Python 3
- Qt5 / PyQt5
- 単一の .py アドオンには専用のフォルダが必要
- アップグレード時にフォルダが削除される
- 1つのコードベースで 2.0 と 2.1 の両方に対応する
- Webview の変更
- Reviewer の変更
- アドオンの設定
Python 3
Anki 2.1 には Python 3 以降が必要です。Python 3 をマシンにインストールした後、2to3 ツールを使用して、以下のように既存のスクリプトをフォルダごとに Python 3 コードに自動的に変換することができます:
2to3-3.8 --output-dir=aqt3 -W -n aqt
mv aqt aqt-old
mv aqt3 aqt
単純なコードのほとんどは自動的に変換できますが、手動で修正する必要がある部分があるかもしれません。
Qt5 / PyQt5
PyQt5では、シグナルとスロットを接続するための構文が変更されました。最近の PyQt4 バージョンでは新しい構文もサポートしているので、Anki 2.0 と 2.1 の両方のアドオンに同じ構文を使用することができます。
詳細は、http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html にあります。
あるアドオン作者は、コードを自動変換するために、以下のツールが便利であったと報告しています。 https://github.com/rferrazz/pyqt4topyqt5
Qt モジュールは PyQt4 ではなく PyQt5 になっています。条件付きでインポートすることもできますが、より簡単な方法は、次のように aqt.qt からインポートすることです:
from aqt.qt import *
これにより、Qt のバージョンを指定することなく、QDialog のようなすべての Qt オブジェクトをインポートすることができます。
単一の .py アドオンには専用のフォルダが必要
各アドオンは独自のフォルダに格納されるようになりました。もし、あなたのアドオンが以前は demo.py
という名前だった場合、demo
フォルダを作成して __init__.py
ファイルを作成する必要があります。
2.0 との互換性を気にしないのであれば、 demo.py
を demo/__init__.py
にリネームすればよいでしょう。
もし、同じファイルで 2.0 に対応するつもりなら、元のファイルをフォルダにコピーし(demo.py
→ demo/demo.py
)、demo/__init__.py
に以下を追加して相対的にインポートすることが可能です:
from . import demo
AnkiWeb にアップロードする際は、フォルダを ZIP で圧縮する必要があります。詳しくは、アドオンの共有を参照してください。
アップグレード時にフォルダが削除される
アドオンをアップグレードすると、アドオンフォルダ内のファイルはすべて削除されます。唯一の例外は、特別な user_files folder です。アドオンが単純なキー/値設定以上のものを必要とする場合、関連するファイルを user_files フォルダに保存していることを確認してください。そうしなければ、アップグレード時に失われるでしょう。
1つのコードベースで 2.0 と 2.1 の両方に対応する
Python 3 のコードのほとんどは Python 2 でも実行できるため、Anki 2.0 と 2.1 の両方で実行できるようにアドオンを更新することができます。その価値があるかどうかは、必要な変更によります。
スケジューラに影響を与えるほとんどのアドオンは、2.1 で動作させるためにわずかな変更で済むはずです。レビューア、ブラウザ、エディタの動作を変更するアドオンは、より多くの作業を必要とする可能性があります。
最も難しいのは、サポートされていないQtWebKit から QtWebEngine への変更です。WebView を使用する場合、Anki 2.1 にコードを移植する作業が必要になり、1 つのコード ベースで両方の Anki バージョンをサポートすることが難しくなる可能性があります。
アドオンが修正なしで動作する場合や、わずかな変更で済む場合は、コードに if 文を追加して、2.0.x と 2.1.x の両方に同じファイルをアップロードするのが最も簡単でしょう。
アドオンに大幅な変更が必要な場合は、2.0.x の更新を停止するか、2 つの Anki バージョン用に別々のファイルを維持する方が簡単かもしれません。
Webview の変更
Qt 5 では WebKit が廃止され、Chromium ベースの WebEngine が採用されたため、Anki のウェブビューは WebEngine を使用するようになりました。注目すべきは以下の点です :
-
Anki を起動する前に環境変数 QTWEBENGINE_REMOTE_DEBUGGING を 8080 に設定し、Chrome で localhost:8080 にアクセスすると、外部の Chrome インスタンスを使用して WebView をデバッグすることができるようになりました。
-
WebEngine は、Python に戻る通信に別の方法を使用します。AnkiWebView() はウェブビューのラッパーで、Javascript で pycmd(str) 関数を提供し、ankiwebview の onBridgeCmd(str) メソッドを呼び出すことができます。reviewer.py や deckbrowser.py など、Anki の UI のさまざまな部分は、これを使用するために修正する必要がありました。
-
Javascript は非同期に評価されるので、JS 式の結果が必要な場合は、ankiwebview の evalWithCallback() を使用します。
-
この非同期動作の結果、editor.saveNow()はコールバックを必要とするようになりました。アドオンがブラウザ上でアクションを実行する場合、まず editor.saveNow() を呼び出し、その後コールバックで残りのコードを実行する必要があるでしょう。.onSearch()への呼び出しは、同様に .search()/.onSearchActivated() に変更する必要があります。例として、ブラウザの .deleteNotes() を参照してください。
-
setScrollPosition() のように WebKit でサポートされていたさまざまな操作は、javascript で実装する必要があります。
-
mw.web.triggerPageAction(QWebEnginePage.Copy) のようなページアクションも非同期なので、JavaScript や遅延を使うように書き直す必要があります。
-
WebEngine は WebKit のようにkeyPressEvent() を提供しないので、メニューやボタンに付属しないショートカットをキャッチするコードを変更する必要がありました。 setStateShortcuts() は、与えられた状態に対するショートカットを調整するために使用するフックを発生させます。
Reviewer の変更
Anki は、前のカードをフェードアウトしてから次のカードをフェードインするようになったため、showQuestion フックが発生したときに次のカードが DOM に表示されなくなります。適切なタイミングで Javascript を実行するために使用できる新しいフックがいくつかあります - 詳しくは こちら をご覧ください。
アドオンの設定
2.0 の小さなアドオンの多くは、ユーザーがソースコードを編集してカスタマイズすることに依存していました。これは、2.1 ではもはや良いアイデアではありません。なぜなら、ユーザーによってなされた変更は、アップデートをチェックしダウンロードするときに上書きされるからです。2.1 では、これを回避するために 設定 のシステムを提供しています。2.0 もサポートし続ける必要がある場合は、以下のようなコードを使用することができます:
if getattr(getattr(mw, "addonManager", None), "getConfig", None):
config = mw.addonManager.getConfig(__name__)
else:
config = dict(optionA=123, optionB=456)