Насколько мне известно, встроенный масштаб класса Tkinter Canvas не будет автоматически масштабировать изображения. Если вы не можете использовать собственный виджет, вы можете масштабировать необработанное изображение и заменять его на холсте при вызове функции масштабирования.
Фрагмент кода ниже можно объединить с вашим исходным классом. Это делает следующее:
- Кэширует результат
Image.open()
.
- Добавляет функцию
redraw()
для вычисления масштабированного изображения и добавляет ее на холст, а также удаляет ранее нарисованное изображение, если оно есть.
- Использует координаты мыши как часть размещения изображения. Я просто передаю
x and y
функции create_image
, чтобы показать, как расположение изображения смещается при перемещении мыши. Вы можете заменить это собственным расчетом центра / смещения.
- При этом используются кнопки мыши и колеса мыши Linux 4 и 5 (вам нужно обобщить их для работы в Windows и т. Д.).
( Обновлено ) Код:
class GUI:
def __init__(self, root):
# ... omitted rest of initialization code
self.canvas.config(scrollregion=self.canvas.bbox(ALL))
self.scale = 1.0
self.orig_img = Image.open(File)
self.img = None
self.img_id = None
# draw the initial image at 1x scale
self.redraw()
# ... rest of init, bind buttons, pack frame
def zoom(self,event):
if event.num == 4:
self.scale *= 2
elif event.num == 5:
self.scale *= 0.5
self.redraw(event.x, event.y)
def redraw(self, x=0, y=0):
if self.img_id:
self.canvas.delete(self.img_id)
iw, ih = self.orig_img.size
size = int(iw * self.scale), int(ih * self.scale)
self.img = ImageTk.PhotoImage(self.orig_img.resize(size))
self.img_id = self.canvas.create_image(x, y, image=self.img)
# tell the canvas to scale up/down the vector objects as well
self.canvas.scale(ALL, x, y, self.scale, self.scale)
Обновление Я провел небольшое тестирование для различных масштабов и обнаружил, что resize / create_image использует довольно много памяти. Я запустил тест, используя 540x375 JPEG на Mac Pro с 32 ГБ ОЗУ. Вот память, используемая для различных масштабных коэффициентов:
1x (500, 375) 14 M
2x (1000, 750) 19 M
4x (2000, 1500) 42 M
8x (4000, 3000) 181 M
16x (8000, 6000) 640 M
32x (16000, 12000) 1606 M
64x (32000, 24000) ...
reached around ~7400 M and ran out of memory, EXC_BAD_ACCESS in _memcpy
Учитывая вышеизложенное, более эффективным решением может быть определение размера области просмотра, в которой будет отображаться изображение, вычисление прямоугольника обрезки вокруг центра координат мыши, обрезка изображения с помощью прямоугольника, а затем масштабирование только обрезанная часть. Это должно использовать постоянную память для хранения временного изображения. В противном случае вам может понадобиться использовать сторонний элемент управления Tkinter, который выполняет это кадрирование / оконное масштабирование.
Обновление 2 Рабочая, но упрощенная логика обрезки, только для начала:
def redraw(self, x=0, y=0):
if self.img_id: self.canvas.delete(self.img_id)
iw, ih = self.orig_img.size
# calculate crop rect
cw, ch = iw / self.scale, ih / self.scale
if cw > iw or ch > ih:
cw = iw
ch = ih
# crop it
_x = int(iw/2 - cw/2)
_y = int(ih/2 - ch/2)
tmp = self.orig_img.crop((_x, _y, _x + int(cw), _y + int(ch)))
size = int(cw * self.scale), int(ch * self.scale)
# draw
self.img = ImageTk.PhotoImage(tmp.resize(size))
self.img_id = self.canvas.create_image(x, y, image=self.img)
gc.collect()