Ответ 1
Насколько мне известно, встроенный масштаб класса Tkinter Canvas не будет автоматически масштабировать изображения. Если вы не можете использовать собственный виджет, вы можете масштабировать необработанное изображение и заменять его на холсте при вызове функции масштабирования.
Ниже приведен фрагмент кода, который можно объединить в исходный класс. Он выполняет следующие действия:
- Задает результат
Image.open()
. - Добавляет функцию
redraw()
для вычисления масштабированного изображения и добавляет его к холсту, а также удаляет ранее нарисованное изображение, если оно есть. - Использует координаты мыши как часть размещения изображения. Я просто передаю
x and y
функцииcreate_image
, чтобы показать, как перемещение изображения смещается по мере перемещения мыши. Вы можете заменить это на свой собственный расчет центра/смещения. - Это использует кнопки 4 и 5 для колесиков Linux (вам нужно обобщить их для работы в 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()