Согласование координат пикселей на холсте tkinter и изображении PIL (Python)

У меня есть форма (в формате .png) с пустыми текстовыми полями (обычно заполняются вручную). Я хотел бы заполнить поля текстом.

Для этого я использую tkinter для отображения формы на экране, затем использую мышь (с более точным позиционированием с помощью клавиш со стрелками), чтобы получить пиксельные координаты поля, а затем использую PIL для записи текста в это поле. Рабочий пример ниже.

Моя основная проблема заключается в том, что я изо всех сил пытаюсь выровнять координаты пикселей на холсте tkinter и координаты пикселей в изображении PIL.

Какой-то дополнительный фон. Изображение имеет высокое разрешение и составляет около 4961 на 7016 пикселей. Разрешение моего экрана 1920 x 1080. У меня были проблемы с написанием текста там, где мне нужно было писать так, и я добился большего успеха, если масштабировал изображение, чтобы оно полностью помещалось на моем экране. Я могу только предположить, что это связано с тем, что я / путаю пиксели экрана с пикселями изображения (и решил это, когда я подогнал изображение к экрану, чтобы выровнять эти два, но понимание различий здесь и как выполнить эту задачу без масштабирования было бы наиболее тоже полезно).

Но у меня также возникают проблемы с согласованием координат пикселей на холсте tkinter с изображением PIL. Например, приведенный ниже код предназначен для записи (x, y) координат пикселей, а затем его относительности страницы {x% по странице, y% вверх по странице} в поле (причина этого в том, что это входные данные в другой процесс). Пример: (346, 481) >> {49,856, 51,018}

Но если (используя коэффициент масштабирования 0,14) я щелкну очень низко в нижней части изображения, я получу (209, 986) >> {30,115, -0,407}. Относительность должна быть ограничена от 0 до 100%, поэтому не должна быть отрицательной, и я не вижу этого в моем файле .png, созданном PIL.

Если я использую коэффициент масштабирования 0,125, я могу нормально писать текст в поле холста tkinter, но текст выглядит немного ниже (то есть вне поля) в файле PIL .png, который сохраняется на дисках. Так что между этими двумя системами явно что-то не работает.

Как я могу согласовать координаты пикселей PIL и tkinter?

Кроме того, у меня есть четыре отдельные функции для более точной настройки клавиш. В идеале это была бы одна функция, но я не мог заставить кнопки со стрелками (и т. д.) работать внутри блока if elif (например, я пробовал это и некоторые другие производные от левого, правого и т. д.)

def mouseMovement(event):
    moveSpeed = 1 
    try: 
        int(event.char)
        moveSpeed = max(1, int(event.char)*5)
        return True
    except ValueError:
        return False

    x, y = pyautogui.position()

    if event.char == '<Left>':
        pyautogui.moveTo(x-moveSpeed, y)     
    elif event.char == '<Right>':
        pyautogui.moveTo(x+moveSpeed, y)          

root.bind('<Key>'  , mouseMovement)

Любая помощь очень ценится!

Почти рабочий пример ниже:

from tkinter import *
from PIL import Image, ImageDraw, ImageFont, ImageTk
import pyautogui


# Form

formName = '2013+ MCS4'

# PIL image'

formImage = Image.open(formName+'.png')
wForm, hForm = formImage.size
scale = 0.14
formImage = formImage.resize((int(scale*wForm), int(scale*hForm)), Image.ANTIALIAS)
draw = ImageDraw.Draw(formImage)

font = ImageFont.truetype('arial.ttf', 10)
textColor = (255, 40, 40)

# tkinter canvas

def colorConversion(RGB):
    def hexadecimalScale(RGB):
        hexadecimalSystem = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F')
        return str(hexadecimalSystem[RGB//16]) + str(hexadecimalSystem[RGB%16])
    return '#' + hexadecimalScale(RGB[0]) + hexadecimalScale(RGB[1]) + hexadecimalScale(RGB[2])

fontCanvas = 'arial 7'
textColorCanvas = colorConversion(textColor)


# generate canvas

if __name__ == '__main__':

    root = Tk()

    # set up tkinter canvas with scrollbars

    frame = Frame(root, bd=2, relief=SUNKEN)
    frame.grid_rowconfigure(0, weight=1)
    frame.grid_columnconfigure(0, weight=1)
    xscroll = Scrollbar(frame, orient=HORIZONTAL)
    xscroll.grid(row=1, column=0, sticky=E+W)
    yscroll = Scrollbar(frame)
    yscroll.grid(row=0, column=1, sticky=N+S)
    canvas = Canvas(frame, width=int(scale*wForm), height=int(scale*hForm), bd=0, xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
    canvas.grid(row=0, column=0, sticky=N+S+E+W)
    xscroll.config(command=canvas.xview)
    yscroll.config(command=canvas.yview)
    frame.pack(fill=BOTH,expand=1)

    # add image

    #img = PhotoImage(file=formName+'.png')
    img = ImageTk.PhotoImage(formImage)
    canvas.create_image(0,0,image=img,anchor="nw")
    canvas.config(scrollregion=canvas.bbox(ALL))

    wForm = img.width()
    hForm = img.height()

    # finer mouse movements

    moveSpeed = 1 

    def setMoveSpeed(event):
        global moveSpeed 
        try: 
            int(event.char)
            moveSpeed = max(1, int(event.char)*5)
            return moveSpeed
        except ValueError:
            return False

    def moveMouseLeft(event):
        x, y = pyautogui.position()
        pyautogui.moveTo(x-moveSpeed, y)

    def moveMouseRight(event):
        x, y = pyautogui.position()
        pyautogui.moveTo(x+moveSpeed, y)

    def moveMouseUp(event):
        x, y = pyautogui.position()
        pyautogui.moveTo(x, y-moveSpeed)

    def moveMouseDown(event):
        x, y = pyautogui.position()
        pyautogui.moveTo(x, y+moveSpeed)

    root.bind('<Key>'  , setMoveSpeed)
    root.bind('<Left>' , moveMouseLeft)
    root.bind('<Right>', moveMouseRight)
    root.bind('<Up>'   , moveMouseUp)
    root.bind('<Down>' , moveMouseDown)


    # print coordinates

    def printCoordinates(event):
        x = event.x    # minor adjustments to correct for differences in tkinter vs PIL methods (investigate further)
        y = event.y    # minor adjustments to correct for differences in tkinter vs PIL methods (investigate further)
        canvas.create_text(x, y-5, fill= textColorCanvas, font= fontCanvas, anchor= 'sw', 
                           text= '{'+str(round(x/wForm*100,3))+', '+str(round((1-y/hForm)*100,3))+'}' )
        draw.text( (x, y-5), '{'+str(round(x/wForm*100,3))+', '+str(round((1-y/hForm)*100,3))+'}' , fill=textColor, font=font)
        print('('+str(x)+', '+str(y)+') >> {'+str(round(x/wForm*100,3))+', '+str(round((1-y/hForm)*100,3))+'}')

    root.bind('<Return>', printCoordinates)

    root.mainloop()

formImage.save('coordinates - '+formName+'.png')

person brb    schedule 03.05.2017    source источник


Ответы (2)


Я не могу запустить ваш код, так что это просто обоснованное предположение.

Поскольку холст обычно не имеет фокуса, а привязка находится в корневом окне, значения для event.x и event.y, возможно, относятся к окну в целом, а не к холсту.

Это должно быть легко определить. Вы можете распечатать координаты в привязке, а затем нажать в верхнем левом углу холста, как можно ближе к 0,0. Печатные координаты также должны быть очень близки к 0,0, если координаты указаны относительно холста. Если они отключены, они могут быть отклонены на расстояние от верхнего левого угла холста до верхнего левого угла окна.

person Bryan Oakley    schedule 03.05.2017

Это связано с тем, что подушка устанавливает текстовые координаты в левом углу (т. е. выбранные координаты становятся верхним левым углом любой подушки текстового поля). Но в более новых версиях Pillow (кажется, 8.1) в тексте есть опция привязки. Просто в параметрах создания текста добавьте anchor=mm. В документах Pillow есть больше информации об этом (ps. В некоторых случаях вам также придется немного увеличить шрифт из-за того, что размеры шрифта Pillow немного меньше. Я обнаружил, что добавление 4 довольно близко)

РЕДАКТИРОВАТЬ: убедитесь, что шнуры, которые вы используете, также являются шнурами Canvas.

person StandingPad Animations    schedule 30.05.2021