首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使按钮在Tkinter中随意移动

如何使按钮在Tkinter中随意移动
EN

Stack Overflow用户
提问于 2017-11-30 10:03:38
回答 2查看 1K关注 0票数 1

这是我正在制作的游戏的一部分。在游戏中,玩家必须点击标记为“鹿”的按钮,而不是尽可能快地点击“doe”:

代码语言:javascript
运行
复制
import tkinter
import time
import random

from __main__ import *

level = 2
name = "Oliver"
stop = False
score = 0
global level, name, score
if level >= 10:
    level = 10 + level/10
difficulty = level * 2
bonus = 1

def shot(animal):
    root.destroy()
    time.sleep(1)
    if animal == "Doe":
        print("You shot a doe!"),time.sleep(1)
        print("The rest of the herd have trampled you to death"),time.sleep(1.2)
        print("Oh deer!")
    elif animal == "Deer":
        print("You shot the deer!")


time.sleep(1), print("***Deer Shooting***\n\nShoot the deer, not the does"), time.sleep(2.5)

print("Ready"),time.sleep(1),print("Steady"),time.sleep(1),print("Go!\n\n\n")

root = tkinter.Tk()

NumOfDoe = difficulty

for i in range(0, NumOfDoe):
    tkinter.Button(root, text = "Doe", command = lambda: shot("Doe")).pack()

tkinter.Button(root, text = "Deer", command = lambda: shot("Deer")).pack()

root.mainloop()

当它运行时,Tkinter窗口看起来有点无聊,因为所有的按钮都排列好了。

我知道'.place()‘函数,但是如何使用它随机分散按钮并使它们随意移动呢?

谢谢

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-11-30 12:52:45

首先,正如@furas所提到的,睡眠是错误的选择,所以请记住使用after来处理tkinter中的时间延迟!

此外,请记住构造代码(link#1link#2),不要试图在一行上放置不必要的多个语句。

琐事

对于可移动的按钮,最好使用Canvas小部件而不是任何布局管理器(除非您真正的目标是远程可移植按钮)。随机连续两次place button很容易,但是如果您想要模拟移动,则不需要一组新的随机坐标,而是旧的坐标、随机距离(偏移)和随机方向。

place实现这个想法是可能的,但是这里有一个很好的move方法,它为您实现了所有这些。

您所需要的只是将每个button放在canvas上,并使用create_window (同时,它还为您提供了在画布上控制您的小部件的object ID )并模拟大规模移动!

示例

代码语言:javascript
运行
复制
import tkinter as tk
import random


class App(tk.Tk):
    def __init__(self, difficulty, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.difficulty = difficulty
        self.play_area = tk.Canvas(self, background='bisque')
        self.play_area.pack(expand=True, fill='both')
        self.animals = self.generate_animals()
        self.play()

    def play(self):
        #   move all animals continuously (each 100 ms)
        self.move_animals()
        self.after(100, self.play)

    def generate_animals(self):
        #   generate all button-like animals
        animals = [Animal('deer', self, text='DEER')]

        for _ in range(self.difficulty * 2):
            animals.append(Animal('doe', self, text='DOE'))

        return animals

    def move_animals(self):
        #   move all animals
        for animal in self.animals:
            animal.move()


class Animal(tk.Button):
    def __init__(self, animal_type, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.animal_type = animal_type
        self.base_speed = self.master.difficulty
        self.play_area = self.master.play_area
        self.move_sets = ['n', 's', 'w', 'e']

        #   place button on canvas (it's possible to randomize starting locations)
        self.id = self.play_area.create_window(0, 0, window=self)

        self.configure(command=self.kill)

    def move(self):
        #   move animal
        #   get random speed and direction
        distance = random.randint(0, 15) * self.base_speed
        direction = random.choice(self.move_sets)

        if direction in self.move_sets[:2]:
            if direction == 'n':
                distance *= -1

            #   to prevent case when an animal leaves play area
            if 0 <= self.play_area.coords(self.id)[1] + distance <= self.play_area.winfo_height():
                self.play_area.move(self.id, 0, distance)

        else:
            if direction == 'w':
                distance *= -1

            #   to prevent case when an animal leaves play area
            if 0 <= self.play_area.coords(self.id)[0] + distance <= self.play_area.winfo_width():
                self.play_area.move(self.id, distance, 0)

    def kill(self):
        if self.animal_type == 'deer':
            print('You shot the deer!')
        else:
            print('You shot a doe!')
            print('The rest of the herd have trampled you to death')
            print('Oh deer!')

        self.master.destroy()

app = App(difficulty=2)
app.mainloop()

就像你看到的-它不知怎么起作用了。

然而,有一个很大的空间来改进和调整事情。例如,一个“更平稳”的运动。虽然这取决于我们移动对象的距离,但它也取决于帧速率,它是人类物种(高帧速率)的24 fps。再次感谢after,我们可以通过时间延迟来控制这个参数,这个延迟可以用公式time_delay = 1000 // desired_fps来计算。

代码语言:javascript
运行
复制
...
    def play(self):
        #   move all animals continuously
        self.move_animals()
        self.after(41, self.play)        #    24 fps
        #   self.after(33, self.play)    #    30 fps
...

还可以通过附加方向改进移动,并将条件逻辑简化为一条语句:

代码语言:javascript
运行
复制
...
class Animal(tk.Button):
    def __init__(self, animal_type, *args, **kwargs):
        super().__init__(*args, **kwargs)
        ...
        #   dictionary for representation purpose
        self.move_sets = {'n': (0, -1), 's': (0, 1), 'w': (-1, 0), 'e': (1, 0),
                          'nw': (-1, -1), 'sw': (-1, 1), 'ne': (1, -1), 'se': (1, 1)}
        ...

    def move(self):
        #   move animal
        #   get random distance and direction
        distance = random.randint(0, 5) * self.base_speed
        direction = random.choice(list(self.move_sets.values()))
        movement = [_ * distance for _ in direction]
        current_coords = self.play_area.coords(self.id)

        #   to prevent case when an animal leaves play area
        if 0 <= current_coords[0] + movement[0] <= self.play_area.winfo_width() and \
           0 <= current_coords[1] + movement[1] <= self.play_area.winfo_height():
            self.play_area.move(self.id, *movement)
        ...

相似问题

票数 2
EN

Stack Overflow用户

发布于 2017-11-30 10:58:16

一种方法是将按钮放在一行中(和以前一样),但按随机顺序排列。

您必须创建包含所有单词的列表(包含许多"Doe"),

洗牌列表,然后使用此列表创建Buttons

代码语言:javascript
运行
复制
num_of_doe = level * 2 # we use CamelCaseNames for class names

# create list with words
all_words = ["Doe"]*num_of_doe + ["Deer"]

# shuffle list ("in-place")
random.shuffle(all_words)

#  create buttons using words
for word in all_words:
    b = tk.Button(root, text=word, command=lambda txt=word:shot(txt))
    b.pack()

BTW:如果在lambda中使用来自forword,那么就必须使用lambda txt=word:shot(txt)。如果您使用lambda:shot(word),那么它会分配相同的值--列表中的最后一个值--因为它将引用放在变量word上,而不是从这个变量中的值。

如果需要更多的随机位置,则可以使用pack()place()grid()在任意位置或单元格中放置按钮(在网格中)。

代码语言:javascript
运行
复制
b.place(x=random.randint(0, 200), y=random.randint(0, 100))

代码语言:javascript
运行
复制
b.grid(row=random.randint(0, 10), column=random.randint(0, 10))

但这会产生问题,因为你可以把两个按钮放在同一个地方。

您需要首先生成所有的值。它需要将值保留在列表中,然后获得新的随机值,与列表中的值进行比较,如果已经在列表上,则生成新的值。

这是你的工作;)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47570682

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档