nlve/tests/zoomanchor.py
4aiman 6c81fd0907 Проверки с окном не трогают чужой редактор и не врут о поломке
genrestart.sh закрывал все окна редактора разом, включая работу
пользователя на рабочем экране. Закрывает теперь только свои — те, что
живут на экране проверки.

Он же падал по всем случаям, потому что съёмки, на которую смотрит его
монтаж, на диске больше нет: ffmpeg открывать нечего, и прерывать нечего.
Теперь сразу называет ненайденные файлы и не притворяется ни пройденной,
ни сломанной.

zoomanchor.py не передавал XAUTHORITY своим командам: экран, поднятый
через xvfb-run, пускает только по своему файлу доступа.
2026-10-15 23:33:46 +03:00

112 lines
4.4 KiB
Python
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Проверка того, что масштаб таймлайна берётся от точки под мышкой.
Ctrl с колесом меняет масштаб. Точка монтажа, на которую смотрит мышь,
обязана остаться на месте: иначе прицелиться масштабом нельзя — картинка
уезжает из-под курсора.
Как меряется. Курсор воспроизведения ставится в известное место и служит
меткой: он привязан ко времени, а не к пикселям, и при смене масштаба
едет вместе с монтажом. Зная, что один щелчок колеса меняет масштаб ровно
в 1.25 раза, неподвижную точку считаем по двум положениям метки:
p1 - a = k * (p0 - a) => a = (k * p0 - p1) / (k - 1)
Ошибку это не прячет: если опора уедет, a разойдётся с положением мыши.
Запуск (редактор уже должен идти на невидимом экране :99):
tests/zoomanchor.py <окно>
где окно — из `DISPLAY=:99 xdotool search --name nlve`.
"""
import os
import subprocess
import sys
import time
from PIL import Image
DISP = os.environ.get('ЭКРАН', ':99')
ENV = {'DISPLAY': DISP, 'PATH': '/usr/bin:/bin'}
# Невидимый экран, поднятый через xvfb-run, пускает только по своему файлу
# доступа. Без него ни xdotool, ни import не видят ни одного окна, и
# проверка падает на первом же движении мыши.
if os.environ.get('XAUTHORITY'):
ENV['XAUTHORITY'] = os.environ['XAUTHORITY']
# Один щелчок колеса. Должен совпадать с ZoomStep в uTimelineCtrl.
STEP = 1.25
# Полоса окна, по которой всё и меряется: линейка и первая дорожка.
BAND = '1500x120+0+450'
SHOT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'кадры-зума.png')
def run(*args):
subprocess.run(list(args), env=ENV, check=True)
def playhead(win):
"""Где сейчас курсор воспроизведения. Он единственный красный."""
run('import', '-window', win, '-crop', BAND, '+repage', SHOT)
im = Image.open(SHOT).convert('RGB')
px = im.load()
w, h = im.size
best = (0, None)
for x in range(10, w - 10):
red = sum(1 for y in range(h)
if px[x, y][0] > 120
and px[x, y][0] - px[x, y][1] > 40
and px[x, y][0] - px[x, y][2] > 40)
if red > best[0]:
best = (red, x)
return best[1] if best[0] > 60 else None
def measure(win, mouse_x):
before = playhead(win)
run('xdotool', 'mousemove', str(mouse_x), '520')
time.sleep(0.4)
run('xdotool', 'keydown', 'ctrl', 'click', '4', 'keyup', 'ctrl')
time.sleep(0.9)
after = playhead(win)
run('xdotool', 'keydown', 'ctrl', 'click', '5', 'keyup', 'ctrl')
time.sleep(0.9)
if before is None or after is None:
return None
return (STEP * before - after) / (STEP - 1)
def main():
if len(sys.argv) < 2:
print(__doc__)
return 2
win = sys.argv[1]
# Метку ставим щелчком по пустому месту под дорожками.
run('xdotool', 'mousemove', '900', '600', 'click', '1')
time.sleep(0.8)
bad = 0
for mouse_x in (700, 1000, 1300):
anchor = measure(win, mouse_x)
if anchor is None:
print('мышь %4d: метку не видно, замер не вышел' % mouse_x)
bad += 1
continue
off = anchor - mouse_x
ok = abs(off) <= 2
print('мышь %4d: опора %6.0f, промах %+.0f точек — %s'
% (mouse_x, anchor, off, 'ok' if ok else 'ПЛОХО'))
if not ok:
bad += 1
print()
if bad:
print('ВЕРДИКТ: масштаб берётся не от мыши, случаев: %d' % bad)
return 1
print('ВЕРДИКТ: точка под мышкой остаётся на месте')
return 0
if __name__ == '__main__':
sys.exit(main())