Skip to content

Commit

Permalink
v1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
ccz-2 committed Mar 10, 2020
1 parent 75bb41d commit 6e04d39
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 122 deletions.
160 changes: 42 additions & 118 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,30 @@
# Anki Zoom
# v1.0 3/1/2020
# v1.1 3/2/2020
# Copyright © 2020 Quip13 ([email protected])
# Based in part on code by Damien Elmes <[email protected]>, Roland Sieker <[email protected]>
# Forked from https://github.com/krassowski/Anki-Zoom
# Based in part on code by Damien Elmes <[email protected]>, Roland Sieker <[email protected]> and github.com/krassowski
# Big thanks to u/Glutanimate and u/yumenogotoshi for code suggestions
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html

from aqt import *
from aqt import mw
from aqt.webview import AnkiWebView, QWebEngineView
from aqt.qt import QEvent, Qt
from anki.hooks import addHook, runHook, wrap
from anki.hooks import *
from anki.lang import _

zoom_step = 1.1

# After changing states, the reported mw.zoomFactor() is inaccurate
# When changing states, the zoom value (Z) is applied twice, essential applying a Z^2 zoom
# This value tracks the correct zoom displayed
zoomActual = 1

# Reflects the desired actual zoom level (what the user expects to see).
# Will deviate from zoomActual during state changes, and used to apply zoom correction
userZoomActual = 1
config = mw.addonManager.getConfig(__name__)
zoom_step = config[ 'zoom_step' ]

def zoom_in(step=None):
if not step:
step = zoom_step
change_zoom(zoomActual * step)
change_zoom_by(step)

def zoom_out(step=None):
if not step:
step = zoom_step
change_zoom(zoomActual / step)
change_zoom_by(1 / step)

def reset_zoom(state=None, *args):
config = mw.addonManager.getConfig(__name__)
Expand All @@ -44,33 +37,29 @@ def reset_zoom(state=None, *args):
elif state == 'review':
change_zoom( config[ 'review_zoom_default' ] )

def change_zoom(desired_zoomActual, fromHook=False):
global zoomActual
global userZoomActual
pZoom = mw.web.zoomFactor()
i = desired_zoomActual/zoomActual
zoomFactor = pZoom*i
if zoomFactor < 0.2: #documented qt bug where zoomFactor cannot exceed 5 or 1/5
zoomFactor = 0.2
elif zoomFactor > 5:
zoomFactor = 5
mw.web.setZoomFactor(zoomFactor)
zoomActual = zoomActual * (mw.web.zoomFactor()/pZoom)
if not fromHook:
userZoomActual = zoomActual
debugPrint('change_zoom, ' + str(fromHook))
def change_zoom_by(interval):
currZoom = QWebEngineView.zoomFactor(mw.web)
change_zoom(currZoom * interval)

def change_zoom(zoom_lvl):
mw.web.setZoomFactor(zoom_lvl)

def set_save_zoom(new_state, old_state, *args):
def unpause():
mw.setUpdatesEnabled(True)

mw.setUpdatesEnabled(False)
old_state_zoom = QWebEngineView.zoomFactor(mw.web)
if old_state == new_state: #skips if reset
return
config = mw.addonManager.getConfig(__name__)

if old_state == 'deckBrowser':
config[ 'deckBrowser_zoom' ] = userZoomActual
config[ 'deckBrowser_zoom' ] = old_state_zoom
elif old_state == 'overview':
config[ 'overview_zoom' ] = userZoomActual
config[ 'overview_zoom' ] = old_state_zoom
elif old_state == 'review':
config[ 'review_zoom' ] = userZoomActual
config[ 'review_zoom' ] = old_state_zoom
mw.addonManager.writeConfig(__name__, config)

config = mw.addonManager.getConfig(__name__)
Expand All @@ -81,81 +70,30 @@ def set_save_zoom(new_state, old_state, *args):
elif new_state == 'review':
change_zoom( config[ 'review_zoom' ] )

resetSentByZoom = False
def zoomStateChange(new_state, old_state, *args):
global resetSentByZoom
global zoomActual
global userZoomActual
QTimer.singleShot(150, unpause) #prevents flickering

def unpause():
mw.setUpdatesEnabled(True)
numDeg = 0
def AnkiWebView_eventFilter_wrapper(self, obj, event):
global numDeg
global zoom_step

if old_state == 'startup':
return
config = mw.addonManager.getConfig(__name__) #can be moved out of func for performance but config will not update automatically
scrl_threshold = config[ 'scroll_sensitivity' ]
zoom_step = config[ 'zoom_step' ]

if resetSentByZoom:
resetSentByZoom = False
QTimer.singleShot(150, unpause) #prevents flickering
return
elif abs(userZoomActual-zoomActual)>0.001: #does not refresh if close enough
mw.setUpdatesEnabled(False)
mw.web.setZoomFactor(userZoomActual**0.5)
resetSentByZoom = True
mw.reset() #recursion

prevZoom = mw.web.zoomFactor()
def calcActualZoom(new_state, old_state, *args):
global zoomActual
global prevZoom
global currZoom
if old_state == 'startup':
return
currZoom = mw.web.zoomFactor()
if currZoom != prevZoom:
zoomActual = currZoom**2
prevZoom = currZoom
debugPrint('After Statechange')

def debugPrint(debug=""):
#print(debug + ': ZoomValue: ' + f'{mw.web.zoomFactor():.3f}' + ' Actual: ' + f'{zoomActual:.3f}' + ' UserActual: ' + f'{userZoomActual:.3f}')
return

# Uses QtEventListener to track wheel
class wheelListener(QObject):
def __init__(self):
QObject.__init__(self)
self.ctrl_Pushed = False
self.numDeg = 0
def eventFilter(self, obj, event):
if event.type() == QEvent.Wheel and self.ctrl_Pushed:
self.numDeg = self.numDeg + event.angleDelta().y()
if self.numDeg > 110:
zoom_in()
self.numDeg = 0
elif self.numDeg < -110:
zoom_out()
self.numDeg = 0
elif event.type() == QEvent.KeyPress and event.key() == Qt.Key_Control:
self.ctrl_Pushed = True
elif event.type() == QEvent.KeyRelease and event.key() == Qt.Key_Control:
self.ctrl_Pushed = False
return False
wheelListener = wheelListener()

#enables scrolling regardless of what has focus (listener installed on bottom, toolbar and main screen)
def setup_event_filters(new_state, old_state, *args): #if called before startup will throw error on linux machines
if old_state == 'startup':
reviewer_QWidget = mw.reviewer.web.findChildren(QWidget)[0] #undocumented method to get underlying event-handling widget of QWebView *may break in future Qt releases
reviewer_QWidget.installEventFilter(wheelListener)

bottom_QWidget = mw.reviewer.bottom.web.findChildren(QWidget)[0] #undocumented method to get underlying event-handling widget of QWebView *may break in future Qt releases
bottom_QWidget.installEventFilter(wheelListener)

toolbar_QWidget = mw.toolbar.web.findChildren(QWidget)[0] #undocumented method to get underlying event-handling widget of QWebView *may break in future Qt releases
toolbar_QWidget.installEventFilter(wheelListener)
if (mw.app.keyboardModifiers() == Qt.ControlModifier and
event.type() == QEvent.Wheel):
numDeg = numDeg + event.angleDelta().y()
if numDeg > scrl_threshold:
zoom_in()
numDeg = 0
elif numDeg < -scrl_threshold :
zoom_out()
numDeg = 0

AnkiWebView.eventFilter = wrap(AnkiWebView.eventFilter, AnkiWebView_eventFilter_wrapper, 'before')

def add_action(submenu, label, callback, shortcut=None):
"""Add action to menu"""
action = QAction(_(label), mw)
action.triggered.connect(callback)
if shortcut:
Expand All @@ -167,7 +105,6 @@ def add_action(submenu, label, callback, shortcut=None):
return action

def setup_menu():
"""Set up the zoom menu."""
try:
mw.addon_view_menu
except AttributeError:
Expand All @@ -183,21 +120,8 @@ def setup_menu():
add_action(mw.zoom_submenu, 'Zoom &In', zoom_in, ['Ctrl++','Ctrl+='])
add_action(mw.zoom_submenu, 'Zoom &Out', zoom_out, ['Ctrl+-'])
mw.zoom_submenu.addSeparator()

add_action(mw.zoom_submenu, '&Reset', reset_zoom, ['Ctrl+0'])

def real_zoom_factor(self):
"""Use the default zoomFactor.
Overwrites Anki's effort to support hiDPI screens.
"""
return QWebEngineView.zoomFactor(self)

AnkiWebView.zoomFactor = real_zoom_factor

addHook("afterStateChange", calcActualZoom)
addHook("afterStateChange", zoomStateChange)
addHook("afterStateChange", set_save_zoom)
addHook("afterStateChange", setup_event_filters)

setup_menu()
setup_menu()
4 changes: 3 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
"review_zoom": 1.0,
"review_zoom_default": 1.0,
"deckBrowser_zoom": 1.0,
"deckBrowser_zoom_default": 1.0
"deckBrowser_zoom_default": 1.0,
"scroll_sensitivity": 120,
"zoom_step": 1.1
}
12 changes: 10 additions & 2 deletions config.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
### Anki Zoom v1.0 Config:
### Anki Zoom v1.1 Config:
All config is effective immediately

- `deckBrowser_zoom_default`: Zoom level that the reset button applies for the deck browser

- `overview_zoom_default`: Zoom level that the reset button applies for the overview screen (screen after selecting a deck)

- `review_zoom_default`: Zoom level that the reset button applies for card reviews

- `deckBrowser_zoom_default`: Zoom level that the reset button applies for the deck browser
- `zoom_step`: How much is zoom is applied per step.
Values are a percentage: e.g. `1.1` = 110% scaling on zoom in, (1/1.1)≈91% scaling on zoom out
<u>To invert zoom, enter values <1</u>

- `scroll_sensitivity`: Defines how much scrolling triggers a zoom step (in arbitrary units). Lower values correlate to more sensitive scrolling.
For reference, mousewheels typically emit 120 units per click. High resolution trackpads (Mac + Windows Precision Touchpads) produce a range of values depending on your movement.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"package": "538879081",
"ankiweb_id": "538879081",
"author": "Quip13",
"human_version": "v1.0",
"human_version": "v1.1",
"homepage": "https://ankiweb.net/shared/info/538879081",
"conflicts": [ "1846592880" ]
}
Binary file modified zoom.ankiaddon
Binary file not shown.

0 comments on commit 6e04d39

Please sign in to comment.