There is KisConfig entry called zoomHorizontal=bool, but looks like it’s ignored in actual zooming code.
Any way here is plugin that overrides zooming action. Sadly it will not center zoom to cursors initial position.
Just copy & paste code text to correct file paths, and enable plugin from Kritas settings + restart Krita. There should be new menu entry Tools → Show Cursor Zoomer Settings, that allows to change zoomer settings.
file tree structure
pykrita/
cursor_zoomer_plugin/
__init__.py
cursor_zoomer_extension.py
cursor_zoomer_plugin.desktop
cursor_zoomer_plugin/__init__.py
from krita import Krita
PLUGIN_NAME = __name__
def register():
from .cursor_zoomer_extension import (
CursorZoomerExtension,)
app = Krita.instance()
extension = CursorZoomerExtension(app)
extension.setObjectName(f'{PLUGIN_NAME}:cursor_zoomer_extension')
app.addExtension(extension)
register()
cursor_zoomer_plugin/cursor_zoomer_extension.py
from krita import (
Krita,
Extension)
from PyQt5.QtCore import (
Qt,
pyqtSlot as QSlot,
pyqtSignal as QSignal,
pyqtProperty as QProperty,
QSize,
QEvent)
from PyQt5.QtGui import (
QKeySequence,)
from PyQt5.QtWidgets import (
QWidget,
QComboBox,
QPushButton,
QDoubleSpinBox,
QFormLayout,
QHBoxLayout,
QApplication,
QMessageBox)
from cursor_zoomer_plugin import (
PLUGIN_NAME,)
class CursorZoomerExtension(Extension):
def setup(self):
self._button = Qt.MiddleButton
self._modifiers = Qt.KeyboardModifiers(Qt.ControlModifier)
self._axis = 'x'
self._multiplier = 1.0
self._editor = None
self._zoomies_start_pos = None
self._zoomies_start_level = None
def createActions(self, window):
show_editor_action = window.createAction(
f'{PLUGIN_NAME}:show_editor_action',
'Show Cursor Zoomer Settings',
'tools')
show_editor_action.triggered.connect(self._on_show_editor_triggered)
self.read_settings()
def _on_show_editor_triggered(self, checked=None):
if self._editor is None:
self._editor = CursorZoomerSettingsEditor()
self._editor.show()
self._editor.raise_()
def read_settings(self):
self._button, self._modifiers, self._axis, self._multiplier = read_settings()
QApplication.instance().installEventFilter(self)
def eventFilter(self, obj, event):
e_type = event.type()
if e_type in {QEvent.MouseButtonPress, QEvent.TabletPress, QEvent.MouseButtonRelease, QEvent.TabletRelease}:
if ((event.buttons() == self._button) and (event.modifiers() == self._modifiers)):
self._zoomies_start(event.globalPos()) # .globalPosition() in Qt6
return True # eat the event!
else:
# buttons is not ONLY self.button, disable zoomies
self._zoomies_end()
# don't eat the event
elif (e_type in {QEvent.MouseMove, QEvent.TabletMove}
and (event.buttons() == self._button)
and (event.modifiers() == self._modifiers)):
self._zoomies(event.globalPos()) # .globalPosition() in Qt6
return True # eat the event!
return super().eventFilter(obj, event)
def _zoomies_start(self, pos):
app = Krita.instance()
window = app.activeWindow()
view = window.activeView()
canvas = view.canvas()
self._zoomies_start_pos = pos
self._zoomies_start_level = canvas.zoomLevel()
def _zoomies_end(self):
self._zoomies_start_pos = None
self._zoomies_start_level = None
def _zoomies(self, pos):
if (self._zoomies_start_pos is None) or (self._zoomies_start_level is None):
return # something strange.
level_offset = 0.0
if self._axis == 'x':
level_offset = 0.001 * self._multiplier * (self._zoomies_start_pos.x() - pos.x())
elif self._axis == 'y':
level_offset = 0.001 * self._multiplier * (self._zoomies_start_pos.y() - pos.y())
app = Krita.instance()
window = app.activeWindow()
view = window.activeView()
canvas = view.canvas()
canvas.setZoomLevel(self._zoomies_start_level + level_offset)
class ModifiersEdit(QWidget):
modifiers_changed = QSignal(Qt.KeyboardModifiers)
def __init__(self, **kwargs):
super().__init__(**kwargs)
layout = QHBoxLayout()
self.setLayout(layout)
self._shift_button = QPushButton(text='Shift', checkable=True)
layout.addWidget(self._shift_button)
self._ctrl_button = QPushButton(text='Ctrl', checkable=True)
layout.addWidget(self._ctrl_button)
self._alt_button = QPushButton(text='Alt', checkable=True)
layout.addWidget(self._alt_button)
self._meta_button = QPushButton(text='Meta', checkable=True)
layout.addWidget(self._meta_button)
def get_modifiers(self):
modifiers = Qt.KeyboardModifiers(
self._shift_button.isChecked() * Qt.ShiftModifier
| self._ctrl_button.isChecked() * Qt.ControlModifier
| self._alt_button.isChecked() * Qt.AltModifier
| self._meta_button.isChecked() * Qt.MetaModifier)
return modifiers
@QSlot(Qt.KeyboardModifiers)
def set_modifiers(self, new_modifiers):
new_modifiers = Qt.KeyboardModifiers(new_modifiers)
old_modifiers = self.get_modifiers()
if new_modifiers == old_modifiers:
return # nothing to do
if new_modifiers & Qt.ShiftModifier:
self._shift_button.setChecked(True)
if new_modifiers & Qt.ControlModifier:
self._ctrl_button.setChecked(True)
if new_modifiers & Qt.AltModifier:
self._alt_button.setChecked(True)
if new_modifiers & Qt.MetaModifier:
self._meta_button.setChecked(True)
self.modifiers_changed.emit(new_modifiers)
modifiers = QProperty(
Qt.KeyboardModifiers,
fget=get_modifiers,
fset=set_modifiers,
notify=modifiers_changed,
user=True)
class CursorZoomerSettingsEditor(QWidget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.setWindowTitle('Cursor Zoomer Settings')
layout = QFormLayout()
self.setLayout(layout)
self._button_selector = QComboBox(editable=False)
for name, button_enum in mouse_buttons().items():
self._button_selector.addItem(name, button_enum)
layout.addRow('button', self._button_selector)
self._modifiers_edit = ModifiersEdit()
layout.addRow('modifiers', self._modifiers_edit)
self._axis_selector = QComboBox()
self._axis_selector.addItem('x', 'x')
self._axis_selector.addItem('y', 'y')
layout.addRow('axis', self._axis_selector)
self._multiplier_spin = QDoubleSpinBox(minimum=-10000.0, maximum=10000.0, value=1.0)
layout.addRow('multiplier', self._multiplier_spin)
self._button_selector.currentIndexChanged[int].connect(lambda _: self.write_settings())
self._modifiers_edit.modifiers_changed.connect(lambda _: self.write_settings())
self._axis_selector.currentIndexChanged[int].connect(lambda _: self.write_settings())
self._multiplier_spin.valueChanged.connect(lambda _: self.write_settings())
def write_settings(self):
write_settings(
self._button_selector.currentData(),
self._modifiers_edit.modifiers,
self._axis_selector.currentData(),
self._multiplier_spin.value())
self.refresh_extension()
def refresh_extension(self):
app = Krita.instance()
extension = next((e for e in app.extensions() if isinstance(e, CursorZoomerExtension)), None)
extension.read_settings()
def read_settings(self):
button, modifiers, axis, multiplier = read_settings()
button_index = self._button_selector.findData(button)
self._button_selector.setCurrentIndex(button_index)
self._modifiers_edit.modifiers = modifiers
axis_index = self._axis_selector.findData(axis)
self._axis_selector.setCurrentIndex(axis_index)
self._multiplier_spin.setValue(multiplier)
def showEvent(self, event):
super().showEvent(event)
self.read_settings()
def write_settings(button, modifiers, axis, multiplier):
app = Krita.instance()
app.writeSetting(PLUGIN_NAME, 'button', get_mouse_button_name(button))
app.writeSetting(PLUGIN_NAME, 'modifiers', modifiers_to_string(modifiers))
app.writeSetting(PLUGIN_NAME, 'axis', str(axis))
app.writeSetting(PLUGIN_NAME, 'multiplier', str(multiplier))
def read_settings():
app = Krita.instance()
button = get_mouse_button_enum(app.readSetting(PLUGIN_NAME, 'button', 'MiddleButton'))
modifiers = string_to_modifiers(app.readSetting(PLUGIN_NAME, 'modifiers', 'Ctrl'))
axis = app.readSetting(PLUGIN_NAME, 'axis', 'x').strip().lower()
multiplier = float(app.readSetting(PLUGIN_NAME, 'multiplier', '1.0'))
return button, modifiers, axis, multiplier
def mouse_buttons():
mouse_buttons = dict()
for name in dir(Qt):
value = getattr(Qt, name)
if isinstance(value, Qt.MouseButton):
mouse_buttons[name] = value
return mouse_buttons
def get_mouse_button_name(button_enum):
for name in dir(Qt):
value = getattr(Qt, name)
if isinstance(value, Qt.MouseButton) and (value == button_enum):
return name
def get_mouse_button_enum(button_name):
for name in dir(Qt):
value = getattr(Qt, name)
if isinstance(value, Qt.MouseButton) and (name == button_name):
return value
def modifiers_to_string(modifiers):
modifiers_string = '+'.join(token for token, a_modifier in (
('Ctrl', Qt.ControlModifier),
('Alt', Qt.AltModifier),
('Shift', Qt.ShiftModifier),
('Meta', Qt.MetaModifier))
if a_modifier & modifiers)
return modifiers_string
def string_to_modifiers(modifiers_string):
modifiers = Qt.KeyboardModifiers()
tokens = {token.strip().casefold() for token in modifiers_string.split('+')}
for a_modifier, token in (
(Qt.ControlModifier, 'Ctrl'.casefold()),
(Qt.AltModifier, 'Alt'.casefold()),
(Qt.ShiftModifier, 'Shift'.casefold()),
(Qt.MetaModifier, 'Meta'.casefold())):
if token in tokens:
modifiers |= a_modifier
return modifiers
cursor_zoomer_plugin.desktop
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=cursor_zoomer_plugin
X-Python-2-Compatible=false
X-Krita-Manual=readme.md
Name=Cursor Zoomer Plugin
Comment=Adds Cursor Zoomer to Krita (settings for zooming to documents using mouse axis with button + modifiers
Ps: settings will allow you to brick your Krita (buttons = All buttons, modifiers = None) so be gentle! (if you brick your Krita, open kritarc in text editor and edit / remove [cursor_zoomer_plugin] section)
/AkiR
