【问题标题】:How can I bind a Canvas Object in a class on tkinter?如何在 tkinter 的类中绑定 Canvas 对象?
【发布时间】:2023-04-01 18:56:01
【问题描述】:
from tkinter import *
from random import randint as rnd

class SpaceField:
    def __init__(self, window = None):
        self.window = window
        self.window = Tk()
        self.window.title("Asteriods")
        # Define canvas size and active flag
        self.wide, self.high, self.active = 700, 600, True
        self.canvas_display()
        self.asteriod_creation_seperation()
        self.spaceship_creation()
        self.spaceship_movement()
        #self.bullet_creation()

# Start Up

    def spaceship_creation(self):
        self.shipcoords = [] #Dont forget to append
        self.spaceship = self.canvas.create_polygon(340,300,360,
            300, 350,280, outline ="white")
        self.shipcoords.append(self.spaceship)

# Creation of the spaceship

    def spaceship_movement(self):
        self.spaceshipx, self.spaceshipy = 10, 10
        self.canvas.move(self.spaceship, self.spaceshipx, self.spaceshipy)

# Movement of the Spaceship

    def down(self, event):
        print(event.keysym)
        self.spacehipx = 0
        self.spaceshipy = -10

# Event that triggers movement, from the down arrow. Right now it only prints the "down" which means it is sensing I am pressing it, but other than that doesn't move the space ship. Which is what I want.

    def asteriod_creation_seperation(self):
        self.asteroids, self.speed = [], []
        size, radius = 50, 25
        for i in range(25):
            spacex = rnd(size, self.wide - size)
            spacey = rnd(size, self.high - size)
            self.asteroids.append( # Store oval item id
                self.canvas.create_oval(
                    spacex, spacey, spacex+size, spacey+size,
                    width=2, tags = "asteriod", outline = "white"))
            self.speed.append((rnd(1,4),rnd(1,4))) # Store random speed x, y

# Creation of the Asteroids

    def asteriod_update(self): # MAIN DRIVER: Work on ALL asteroids
        for i, a in enumerate(self.asteroids):
            xx, yy = self.speed[i] # get speed data
            x, y, w, h = self.canvas.coords(a)
            # check for boundary hit then change direction and speed
            if x < 0 or w > self.wide:
                xx = -xx * rnd(1, 4)
            if y < 0 or h > self.high:
                yy = -yy * rnd(1, 4)

            # Limit max and min speed then store it
            self.speed[i] = (max( -4, min( xx, 4)), max( -4, min( yy, 4 )))
            self.canvas.move(a, xx, yy) # update asteroid position

# Movement of the Asteroids

    def move_active(self):
        if self.active:
            self.asteriod_update()
            self.window.after(40, self.move_active)

# Updating the Asteroids Position

    def canvas_display(self):
        self.canvas = Canvas(
            self.window, width = self.wide,
            height = self.high, background = "black")
        self.canvas.pack(expand = True, fill = "both")

# Displaying the Canvas

    def run(self): # Begin asteroids here so that mainloop is executed
        self.window.bind("<KeyPress-Down>", lambda e: SpaceF.down(e))
        self.window.after(200, self.move_active)
        self.window.mainloop()


# Binding Keys and running mainloop

if __name__ == "__main__":
    SpaceF = SpaceField()

    SpaceF.run()

总而言之,我想使用向下箭头键将飞船向下移动,但我不知道如何使其移动。我想我可能不得不使用 if 语句,但任何帮助或视频推荐也会有所帮助。

【问题讨论】:

  • 我编辑了你的问题,将描述性部分作为代码 cmets 的一部分,因为否则很难复制代码并运行它,因为它是分开的,它也使它更难阅读,你也是需要提供minimal reproducible example,不要征求建议
  • 问题是,当您还需要在画布上移动对象时,您才更改属性的值,例如使用self.canvas.move(已记录,因此请阅读文档),还有它应该这样写:self.window.bind("&lt;KeyPress-Down&gt;", self.down) 而不是你在那里的。并且您的代码可能应该进行重组,例如宇宙飞船应该是它自己的类,小行星也应该是,给定的类也可以从Tk 继承,并且实例属性应该在__init__ 方法中定义,以便清楚或...跨度>
  • 我想我明白了,我不习惯使用类,但我会尝试。您还可以提供文档的链接吗?
  • 你可以直接谷歌 tkinter canvas.move docs 而第二个结果(对我来说)是文档(该方法介于两者之间,因为所有方法都列出了) :anzeljg.github.io/rin2/book2/2405/docs/tkinter/…

标签: python tkinter events event-handling tkinter-canvas


【解决方案1】:

这是一个代码 sn-p,它演示了如何以不同的速度移动你的航天器。

我给出了两种实现方式。

  1. 使用条件语句
  2. 使用备选方案的列表索引

您还需要确保画布具有焦点。

class SpaceField:
    def __init__(self, window = None):
        ...

        self.canvas.bind("<Down>", self.down)
        self.canvas.bind("<Up>", self.up)

    def down(self, event):
        if event.state == 262144: # Open key
            y = 1
        elif event.state == 262145: # Shift key
            y = 5
        else: # Control key
            y = 10
        self.canvas.move(self.spaceship, 0, y)

    def up(self, event):
        y = [-1, -5, -10][[262144, 262145, 262148].index(event.state)]
        self.canvas.move(self.spaceship, 0, y)

    def run(self): # Begin asteroids here so that mainloop is executed
        self.window.after(200, self.move_active)

        # make sure canvas has the focus
        self.canvas.focus_force()
        self.window.mainloop()

【讨论】:

  • Derek,如何让 Canvas 成为焦点?也出了这两种方式,什么是最多的。我不知道要说这个“最佳”/“有效”@Derek
  • @user14232195 来集中注意力,您可以使用.focus_set()(或.focus_force()),在这两种方式中,更快的似乎是使用if/elif,因为您不需要创建列表和然后还找到一个索引,然后将该索引用作另一个列表的索引(也可以以类似的方式使用字典:states = {262144: -1, ...} 等等,因此它将用作self.canvas.move(self.spaceship, 0, states[event.state]),但也可以在__init__ 方法(在使用列表时应该这样做),以便在每次调用函数时都不会创建它)
  • 我提供了两种方法来提供选择@user14232195。
猜你喜欢
  • 2022-01-22
  • 1970-01-01
  • 2013-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多