协调tkinter画布上的像素坐标和PIL图像(Python)

时间:2017-05-03 12:40:44

标签: python tkinter python-imaging-library pixels tkinter-canvas

我有一个带有空白文本框的表单(格式为.png)(通常是手工填写)。我想用文字填充方框。

为此,我使用tkinter在屏幕上显示表单,然后使用鼠标(使用箭头键进行更精细的定位)来获取框的像素坐标,然后使用PIL将文本写入该框。下面是一个工作示例。

我的主要问题是我正在努力调整tkinter画布中的像素坐标和PIL图像中的像素坐标。

一些额外的背景。图像分辨率高,大约4961 x 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%之间,所以不应该是负数,我不能在我的PIL生成的.png文件中看到这个。

如果我使用0.125的缩放系数,我可以很好地将文本写入tkinter画布框,但是PIL .png文件中的文本显示得相当低(即在框外),该文件保存到驱动器中。因此,这两个系统之间显然无法正常工作。

如何协调PIL和tkinter像素坐标?

顺便说一句,我有四个独立的功能来处理更精细的键调整。理想情况下,这些将是一个函数,但我无法让箭头按钮(等)在if ifif块中工作(例如,我尝试了这个以及更多左,右等的衍生物)

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')

2 个答案:

答案 0 :(得分:0)

我无法运行您的代码,所以这只是一个有根据的猜测。

由于画布通常没有焦点,并且绑定在根窗口上,因此event.xevent.y的值可能相对于窗口整体而不是帆布。

这应该很容易确定。您可以打印出绑定中的坐标,然后单击画布的左上角,尽可能接近0,0。如果坐标相对于画布,打印的坐标也应该非常接近0,0。如果它们关闭,它们可能会偏离画布左上角到窗口左上角的距离。

答案 1 :(得分:0)

这是由于 Pillow 将文本坐标设置在左上角(即所选坐标成为枕头添加的任何文本框的左上角)。但是在新版本的 Pillow(我认为是 8.1)中,文本有一个锚选项。只需在创建文本的参数中添加anchor="mm"。 Pillow 文档对此有更多信息(ps。在某些情况下,由于 Pillow 字体大小略小,您还必须稍微增加字体。我发现添加 4 非常接近)

编辑:确保您使用的线也是帆布线