How to implement a drag feature (or zoom) in a python window application?

拥有回忆 提交于 2021-02-10 20:47:52

问题


I'm doing some simple fractal drawings using turtle graphics in python and have encountered the situation where the turtle draws beyond the application frame. I want to be able to drag the view within the window so that I can center it over a different section of the fractal I am drawing or alternatively, Zoom in or out in order to manipulate the view.

Does Python have a module for this? I am relatively new to python as I primarily program with Java and can think of many ways to do with Java and JFrames etc. My research for a simple python implementation has been fruitless :( and I don't want to be reinventing the wheel if there is a simple solution avaliable~ all methods are appreciated though. If I have missed another thread on this site that answers my question, please comment.

here is my fractal code for anyone interested..

import turtle

t = turtle.Turtle()
t.color("white")
t.backward(450)
t.color("red")

def f2(length, depth):
    if depth == 0:
        t.forward(length)
    else:
        f2(length/2, depth -1)
        t.left(135)
        f2(length/8, depth -1)
        t.right(90)
        f2(length/8, depth -1)
        t.right(90)
        f2(length/8, depth -1)
        t.right(90)
        f2(length/8, depth -1)
        t.left(135)
        f2(length/2, depth -1)


f2(400, 4)

回答1:


there are a couple of ways you could do this, i had a need a while ago and unfortunately i don't think there is a very simple way. but you can explicitly set the size of the canvas (which if bigger than shown adds scrollbars) by using:

screen.screensize(width, height)

this normally acts on the turtle screen not the turtle iteself, so to get the screen you will need to call:

t.getscreen()

there is a more complicated but more capable way where you subclass the turtle to keep track of its bounding box (the furthest points travelled to in each axis) and then update the screensize based on that, but it's much more invloved, i will post details if asked.

EDIT: adding details of subclassing method first we subclass the turtle so that it has an attribute called bbox, this is a list which gets updated every time we move the turtle.

class MyTurtle(turtle.RawTurtle): # here we subclass the turtle to allow us to call statusbar updates after each movement
    def __init__(self, canvas):
        turtle.RawTurtle.__init__(self, canvas)
        self.bbox = [0,0,0,0]

    def _update_bbox(self): # keep a record of the furthers points visited
        pos = self.position()
        if pos[0] < self.bbox[0]:
            self.bbox[0] = pos[0]
        elif pos[0] > self.bbox[2]:
            self.bbox[2] = pos[0]
        if pos[1] < self.bbox[1]:
            self.bbox[1] = pos[1]
        elif pos[1] > self.bbox[3]:
            self.bbox[3] = pos[1]

    def forward(self, *args):
        turtle.RawTurtle.forward(self, *args)
        self._update_bbox()

    def backward(self, *args):
        turtle.RawTurtle.backward(self, *args)
        self._update_bbox()

    def right(self, *args):
        turtle.RawTurtle.right(self, *args)
        self._update_bbox()

    def left(self, *args):
        turtle.RawTurtle.left(self, *args)
        self._update_bbox()

    def goto(self, *args):
        turtle.RawTurtle.goto(self, *args)
        self._update_bbox()

    def setx(self, *args):
        turtle.RawTurtle.setx(self, *args)
        self._update_bbox()

    def sety(self, *args):
        turtle.RawTurtle.sety(self, *args)
        self._update_bbox()

    def setheading(self, *args):
        turtle.RawTurtle.setheading(self, *args)
        self._update_bbox()

    def home(self, *args):
        turtle.RawTurtle.home(self, *args)
        self._update_bbox()

then we create a canvas for the turtle to work on:

cv = turtle.ScrolledCanvas(root)

and then we turn that canvas into a turtlescreen:

screen = turtle.TurtleScreen(cv)

we create a turtle on the screen:

turt = MyTurtle(screen)

now the turtle can be used as a normal turtle (move, colour, shape, speed etc) and if the turtle goes beyond the bounds of the screen you can call:

min_x, min_y, max_x, max_y = turt.bbox # get the furthest points the turtle has been
width = max((0-min_x),(max_x)) * 2 + 100 # work out what the maximum distance from 0,0 is for each axis
height = max((0-min_y),(max_y)) * 2 + 100 # the 100 here gives us some padding between the edge and whats drawn
screen.screensize(width, height)

note that the above code re sizes equally about the origin as thats what the screen method allows for, however if you delved deeper and applied the bbox to the canvas itself you could make it re size only around the items on the canvas.




回答2:


This has come up a number of times in my programming classes. My solution is also a bit complicated, but it adds the scrollbars correctly for me (unfortunately, they don't seem to get added automatically for me as the screen size grows), and I don't need to keep track of the bounding box myself.

Here is the setup:

import turtle
import Tkinter

root = Tkinter.Tk()
START_WIDTH = 700 
START_HEIGHT = 700 

frame = Tkinter.Frame(root, width=START_WIDTH, height=START_HEIGHT)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)

xscrollbar = Tkinter.Scrollbar(frame, orient=Tkinter.HORIZONTAL)
xscrollbar.grid(row=1, column=0, sticky=Tkinter.E+Tkinter.W)

yscrollbar = Tkinter.Scrollbar(frame, orient=Tkinter.VERTICAL)
yscrollbar.grid(row=0, column=1, sticky=Tkinter.N+Tkinter.S)

canvas = Tkinter.Canvas(frame, width=START_WIDTH, height=START_HEIGHT,
                        scrollregion=(0, 0, START_WIDTH, START_HEIGHT),
                        xscrollcommand=xscrollbar.set,
                        yscrollcommand=yscrollbar.set)

canvas.grid(row=0, column=0, sticky=Tkinter.N+Tkinter.S+Tkinter.E+Tkinter.W)

xscrollbar.config(command=canvas.xview)
yscrollbar.config(command=canvas.yview)

frame.pack()

turt = turtle.RawTurtle(canvas)

Now you can use turt as you would any other turtle object you'd created. Each time you want to make sure you can scroll to everything that has been drawn, run

canvas.config(scrollregion=canvas.bbox(Tkinter.ALL))

See http://effbot.org/zone/tkinter-scrollbar-patterns.htm and http://effbot.org/tkinterbook/canvas.htm for documentation on customizing the canvas and scrollbars.



来源:https://stackoverflow.com/questions/29276229/how-to-implement-a-drag-feature-or-zoom-in-a-python-window-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!