【问题标题】:Is there any way to return the turtle object that is clicked?有没有办法返回被点击的海龟对象?
【发布时间】:2021-03-11 10:04:11
【问题描述】:

我正在制作一个匹配游戏,其中有几张纸牌面朝下,用户必须匹配正确的配对。倒置的牌都是海龟对象。

例如。如果有 8 张面朝下的牌,则有 8 个海龟对象。

我在弄清楚如何选择卡片时遇到了一些麻烦,因为我不知道哪个海龟与用户选择的特定卡片相关联。我确实有一个包含所有海龟的嵌套列表,并且具有相似图像的那些被组合在一起。有什么方法可以返回用户选择的海龟对象吗?

【问题讨论】:

    标签: python python-3.x return turtle-graphics python-turtle


    【解决方案1】:

    如果我有你的问题,一种方法是你应该为每只海龟提供一些 id 属性来识别它。然后您可以轻松检查用户选择了哪个海龟。

    【讨论】:

      【解决方案2】:

      您可以做的是定义一个列表,turned 来存储翻过来的卡片。

      这是Card 类的示例:

      class Card(turtle.Turtle):
          def __init__(self, number):
              super(Card, self).__init__()
              self.number = number
          def click(self, x, y):
              if self in turned:
                  self.clear()
                  turned.remove(self)
              else:
                  self.sety(self.ycor() - self.shapesize()[1] * 7)
                  self.write(self.number, align='center', font=('Arial', 20, 'bold'))
                  self.sety(self.ycor() + self.shapesize()[1] * 7)
                  turned.append(self)
      

      super(Card, self).__init__() 将赋予Cardturtle.Turtle 类具有的所有属性。使用self.number = number 将类变量添加到Card 类。

      click 函数中:

              if self in turned:
                  self.clear()
                  turned.remove(self)
      

      这将允许用户通过将其从turned 列表中删除并清除文本来取消选择已选择的卡,然后

              else:
                  self.sety(self.ycor() - self.shapesize()[1] * 7)
                  self.write(self.number, align='center', font=('Arial', 20, 'bold'))
                  self.sety(self.ycor() + self.shapesize()[1] * 7)
                  turned.append(self)
      

      将写入文本并将卡片附加到turned 列表中。

      我还定义了一个Deck 类,它将使用Card 类来创建一个完整的卡片网格:

      class Deck:
          def __init__(self, rows, cols, width, height, x, y):
              self.cards = []
              for i in range(cols):
                  for j in range(rows):
                      card = Card(randint(2, 10))
                      card.shape("square")
                      card.color('black', 'white')
                      card.shapesize(height / 20, width / 20)
                      card.goto(i * width + x, j * height + y)
                      card.onclick(card.click)
                      self.cards.append(card)
      

      例子:

      import turtle
      from random import randint
      
      wn = turtle.Screen()
      wn.tracer(0)
      
      turned = []
      
      class Card(turtle.Turtle):
          def __init__(self, number):
              super(Card, self).__init__()
              self.number = number
          def click(self, x, y):
              if self in turned:
                  self.clear()
                  turned.remove(self)
              else:
                  self.sety(self.ycor() - self.shapesize()[1] * 7)
                  self.write(self.number, align='center', font=('Arial', 20, 'bold'))
                  self.sety(self.ycor() + self.shapesize()[1] * 7)
                  turned.append(self)
              print([card.number for card in turned]) # print to display the clicked cards
      
      class Deck:
          def __init__(self, rows, cols, width, height, x, y):
              self.cards = []
              for i in range(cols):
                  for j in range(rows):
                      card = Card(randint(2, 10))
                      card.shape("square")
                      card.color('black', 'white')
                      card.shapesize(height / 20, width / 20)
                      card.goto(i * width + x, j * height + y)
                      card.onclick(card.click)
                      self.cards.append(card)
      
      deck = Deck(8, 8, 45, 62.5, -165, -210)
      
      wn.update()
      wn.mainloop()
      

      输出:


      上面的代码可能看起来更简单,但它使用了一个全局变量turned,并且在tracer 保持打开状态下,数字只会显示半秒钟。并且直接点击显示的数字不会改变卡片的状态。

      以下代码纠正了这些缺陷:

      您可以做的是为每张卡片定义一个类,并将一个类定义为一副卡片。在deck类中,定义一个类变量列表turned来存放翻过来的卡片。

      这是Card 类的示例:

      class Card(turtle.Turtle):
          def __init__(self, number):
              super(Card, self).__init__()
              self.number = number
              self.penup()
      
          def write_number(self, pen):
              pen.goto(self.xcor(), self.ycor() - self.shapesize()[1] * 7)
              pen.write(self.number, align='center', font=('Arial', 20, 'bold'))
      
          def clicked(self, x, y):
              h, w = self.shapesize()[:-1]
              half_width = w * 10
              half_height = h * 10
              return self.xcor() + half_width > x > self.xcor() - half_width and \
                     self.ycor() + half_height > y > self.ycor() - half_height
      

      super(Card, self).__init__() 将赋予Cardturtle.Turtle 类具有的所有属性。使用self.number = number 将类变量添加到Card 类。

      write_number 函数将使用单独的海龟对象在当前海龟对象上显示一个数字,以便数字显示在卡片的中心。

      clicked函数基本上会取两个坐标,检测坐标是否在当前的turtle对象上。我定义一个clicked 函数而不是使用内置的turtle.onclick 方法的原因是因为我希望它返回一个布尔值而不是执行一个函数。

      Deck 类将使用 Card 类创建整个卡片网格。:

      class Deck:
          def __init__(self, rows, cols, width, height, x, y):
              self.pen = turtle.Turtle(visible=False)
              self.pen.penup()
              self.pen.speed(0)
              self.cards = []
              self.numbers = []
              self.turned = []
              for i in range(cols):
                  for j in range(rows):
                      card = Card(randint(2, 10))
                      card.shape("square")
                      card.color('black', 'white')
                      card.shapesize(height / 20, width / 20)
                      card.goto(i * width + x, -j * height - y)
                      self.cards.append(card)
                      
          def click(self, x, y):
              for card in self.cards:
                  if card.clicked(x, y):
                      if card in self.turned:
                          card.clear()
                          self.turned.remove(card)
                      else:
                          self.turned.append(card)
              self.draw()
              print([card.number for card in self.turned])
              
          def draw(self):
              for card in self.turned:
                  card.write_number(self.pen)
      

      self.cards 列表存储所有Card 对象,self.turned 存储翻转的卡片。

      click 函数将使用来自Card 类的clicked 函数来检测对Deck 类变量cards 内的所有Card 对象的点击。

      最后,draw 函数将使用Deck 中定义的self.pen 显示turned 列表中所有卡片上的数字。

      完整的工作代码:

      import turtle
      from random import randint
      
      wn = turtle.Screen()
      
      class Card(turtle.Turtle):
          def __init__(self, number):
              super(Card, self).__init__()
              self.number = number
              self.penup()
      
          def write_number(self, pen):
              pen.goto(self.xcor(), self.ycor() - self.shapesize()[1] * 7)
              pen.write(self.number, align='center', font=('Arial', 20, 'bold'))
      
          def clicked(self, x, y):
              h, w = self.shapesize()[:-1]
              half_width = w * 10
              half_height = h * 10
              return self.xcor() + half_width > x > self.xcor() - half_width and \
                     self.ycor() + half_height > y > self.ycor() - half_height
          
      class Deck:
          def __init__(self, rows, cols, width, height, x, y):
              self.pen = turtle.Turtle(visible=False)
              self.pen.penup()
              self.pen.speed(0)
              self.cards = []
              self.numbers = []
              self.turned = []
              for i in range(cols):
                  for j in range(rows):
                      card = Card(randint(2, 10))
                      card.shape("square")
                      card.color('black', 'white')
                      card.shapesize(height / 20, width / 20)
                      card.goto(i * width + x, -j * height - y)
                      self.cards.append(card)
                      
          def click(self, x, y):
              for card in self.cards:
                  if card.clicked(x, y):
                      if card in self.turned:
                          card.clear()
                          self.turned.remove(card)
                      else:
                          self.turned.append(card)
              self.draw()
              print([card.number for card in self.turned])
              
          def draw(self):
              for card in self.turned:
                  card.write_number(self.pen)
      
      deck = Deck(8, 8, 45, 62.5, -165, -210)
      
      wn.onscreenclick(deck.click)
      wn.mainloop()
      

      【讨论】:

      • 您已经很好地将卡片和套牌封装为对象,但全局turned 列表除外。相反,它可以是每张卡的布尔属性。至于列出所有已转牌的print 语句,这似乎是调试代码,不应指示对象的实现,最好在了解所有牌的Deck 类中处理,并且可以询问他们的turned 状态。
      • @cdlane 谢谢。 Card 类中的 click 函数是否应该返回一些内容以便 Deck 类可以访问它?
      • 我不能说,因为click()是一个事件处理程序,它真的不能返回任何东西。我相信这取决于该应用程序将如何传递信息的大局。也许您在创建实例时传递Card 一个函数,该函数将在卡片更改状态时调用。那么卡片的onclick() 可能会调用它。
      • 您的代码有问题。您将tracer() 关闭,因此我们希望update() 位于click() 的末尾,因为它会更改屏幕。但它不存在,因为你得到了你想要的效果,打电话给update() 会打乱它。我假设您正在利用海龟中的一个小故障,但是,如果实施得当,您的代码将无法正常工作。您可以找到关于the difficulty of implementing numbered blocks in turtle 的讨论(需要修复!)启用跟踪后,您会发现问题——tracer(0) 使视觉效果快速而干净,但如果没有它,它们应该工作
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      相关资源
      最近更新 更多