# -*- coding: utf-8 -*- # Copyright: Michal Krassowski # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html """ This plugin adds the function of touchscreen, similar that one implemented in AnkiDroid. It adds a "view" menu entity (if it doesn't exist) with options like: switching touchscreen modifying some of the colors If you want to contribute visit GitHub page: https://github.com/krassowski/Anki-TouchScreen Also, feel free to send me bug reports or feature requests. Copyright: Michal Krassowski License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html, Important parts of Javascript code inspired by http://creativejs.com/tutorials/painting-with-pixels/index.html """ __addon_name__ = "TouchScreen" __version__ = "0.2.6" from aqt import mw, dialogs from aqt.utils import showWarning from anki.lang import _ from anki.hooks import addHook from PyQt5.QtWidgets import QAction, QMenu, QColorDialog, QMessageBox, QInputDialog from PyQt5 import QtCore from PyQt5.QtGui import QKeySequence from PyQt5.QtGui import QColor from PyQt5.QtCore import pyqtSlot as slot # This declarations are there only to be sure that in case of troubles # with "profileLoaded" hook everything will work. ts_state_on = False ts_profile_loaded = False ts_color = "#272828" ts_line_width = 4 ts_opacity = 0.7 ts_default_review_html = mw.reviewer.revHtml @slot() def ts_change_color(): """ Open color picker and set chosen color to text (in content) """ global ts_color qcolor_old = QColor(ts_color) qcolor = QColorDialog.getColor(qcolor_old) if qcolor.isValid(): ts_color = qcolor.name() execute_js("color = '" + ts_color + "'; update_pen_settings()") ts_refresh() @slot() def ts_change_width(): global ts_line_width value, accepted = QInputDialog.getDouble(mw, "Touch Screen", "Enter the width:", ts_line_width) if accepted: ts_line_width = value execute_js("line_width = '" + str(ts_line_width) + "'; update_pen_settings()") ts_refresh() @slot() def ts_change_opacity(): global ts_opacity value, accepted = QInputDialog.getDouble(mw, "Touch Screen", "Enter the opacity (100 = transparent, 0 = opaque):", 100 * ts_opacity, 0, 100, 2) if accepted: ts_opacity = value / 100 execute_js("canvas.style.opacity = " + str(ts_opacity)) ts_refresh() @slot() def ts_about(): """ Show "about" window. """ ts_about_box = QMessageBox() ts_about_box.setText(__addon_name__ + " " + __version__ + __doc__) ts_about_box.setGeometry(300, 300, 250, 150) ts_about_box.setWindowTitle("About " + __addon_name__ + " " + __version__) ts_about_box.exec_() def ts_save(): """ Saves configurable variables into profile, so they can be used to restore previous state after Anki restart. """ mw.pm.profile['ts_state_on'] = ts_state_on mw.pm.profile['ts_color'] = ts_color mw.pm.profile['ts_line_width'] = ts_line_width mw.pm.profile['ts_opacity'] = ts_opacity def ts_load(): """ Load configuration from profile, set states of checkable menu objects and turn on night mode if it were enabled on previous session. """ global ts_state_on, ts_color, ts_profile_loaded, ts_line_width, ts_opacity try: ts_state_on = mw.pm.profile['ts_state_on'] ts_color = mw.pm.profile['ts_color'] ts_line_width = mw.pm.profile['ts_line_width'] ts_opacity = mw.pm.profile['ts_opacity'] except KeyError: ts_state_on = False ts_color = "#f0f" ts_line_width = 4 ts_opacity = 0.8 ts_profile_loaded = True if ts_state_on: ts_on() assure_plugged_in() def execute_js(code): web_object = mw.reviewer.web web_object.eval(code) def assure_plugged_in(): global ts_default_review_html if not mw.reviewer.revHtml == custom: ts_default_review_html = mw.reviewer.revHtml mw.reviewer.revHtml = custom def clear_blackboard(web_object=None): assure_plugged_in() if not web_object: web_object = mw.reviewer.web if ts_state_on: javascript = 'clear_canvas();' web_object.eval(javascript) def ts_resize(html, card, context): if ts_state_on: html += """ """ return html def ts_onload(): """ Add hooks and initialize menu. Call to this function is placed on the end of this file. """ addHook("unloadProfile", ts_save) addHook("profileLoaded", ts_load) addHook("showQuestion", clear_blackboard) addHook('prepareQA', ts_resize) ts_setup_menu() ts_blackboard = u"""
""" def custom(*args, **kwargs): global ts_state_on default = ts_default_review_html(*args, **kwargs) if not ts_state_on: return default output = ( default + ts_blackboard + "" + "" ) return output mw.reviewer.revHtml = custom def ts_on(): """ Turn on """ if not ts_profile_loaded: showWarning(TS_ERROR_NO_PROFILE) return False global ts_state_on ts_state_on = True ts_menu_switch.setChecked(True) return True def ts_off(): """ Turn off """ if not ts_profile_loaded: showWarning(TS_ERROR_NO_PROFILE) return False global ts_state_on ts_state_on = False ts_menu_switch.setChecked(False) return True @slot() def ts_switch(): """ Switch TouchScreen. """ if ts_state_on: ts_off() else: ts_on() # Reload current screen. if mw.state == "review": mw.moveToState('overview') mw.moveToState('review') if mw.state == "deckBrowser": mw.deckBrowser.refresh() if mw.state == "overview": mw.overview.refresh() def ts_refresh(): """ Refresh display by reenabling night or normal mode. """ if ts_state_on: ts_on() else: ts_off() def ts_setup_menu(): """ Initialize menu. If there is an entity "View" in top level menu (shared with other plugins, like "Zoom" of R. Sieker) options of the addon will be placed there. In other case it creates that menu. """ global ts_menu_switch try: mw.addon_view_menu except AttributeError: mw.addon_view_menu = QMenu(_(u"&View"), mw) mw.form.menubar.insertMenu(mw.form.menuTools.menuAction(), mw.addon_view_menu) mw.ts_menu = QMenu(_('&Touchscreen'), mw) mw.addon_view_menu.addMenu(mw.ts_menu) ts_menu_switch = QAction(_('&Enable touchscreen mode'), mw, checkable=True) ts_menu_color = QAction(_('Set &pen color'), mw) ts_menu_width = QAction(_('Set pen &width'), mw) ts_menu_opacity = QAction(_('Set pen &opacity'), mw) ts_menu_about = QAction(_('&About...'), mw) ts_toggle_seq = QKeySequence("Ctrl+r") ts_menu_switch.setShortcut(ts_toggle_seq) mw.ts_menu.addAction(ts_menu_switch) mw.ts_menu.addAction(ts_menu_color) mw.ts_menu.addAction(ts_menu_width) mw.ts_menu.addAction(ts_menu_opacity) mw.ts_menu.addSeparator() mw.ts_menu.addAction(ts_menu_about) ts_menu_switch.triggered.connect(ts_switch) ts_menu_color.triggered.connect(ts_change_color) ts_menu_width.triggered.connect(ts_change_width) ts_menu_opacity.triggered.connect(ts_change_opacity) ts_menu_about.triggered.connect(ts_about) TS_ERROR_NO_PROFILE = "No profile loaded" # # ONLOAD SECTION # ts_onload()