简体   繁体   English

如何在python窗口应用程序中实现拖动功能(或缩放)?

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

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. 我正在使用python中的海龟图形来做一些简单的分形图,并且遇到了海龟超出应用程序框架的情况。 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? Python是否为此提供了一个模块? 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. 我对python相对较新,因为我主要使用Java进行编程,并且可以想到许多与Java和JFrames等相关的方法。我对简单python实现的研究是徒劳的:(并且我不想重新发明轮子,如果有一个简单的解决方案可用,尽管可以使用所有方法, 如果我错过了这个站点上回答我问题的另一个线程,请发表评论。

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)

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. 编辑:添加子类化方法的详细信息首先,我们对乌龟进行子类化,以便它具有一个名为bbox的属性,这是一个列表,每当我们移动乌龟时都会对其进行更新。

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. 请注意,上述代码在原点大小上的调整与屏幕方法所允许的大小相同,但是,如果您深入研究并将bbox应用于画布本身,则可以使其仅围绕画布上的项目调整大小。

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. 现在,您可以像创建其他任何乌龟对象一样使用turt 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. 有关自定义画布和滚动条的文档,请参见http://effbot.org/zone/tkinter-scrollbar-patterns.htmhttp://effbot.org/tkinterbook/canvas.htm

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM