0

似乎很多人在使用 Kivy 的 on_press 参数时遇到了麻烦,但我还没有找到我的问题的答案......这是正在发生的事情:我开始使用 python/kivy 中的第一个应用程序。我知道python,但可能对类还不够……我可以创建一个按钮,带有一个打开弹出窗口的on_press操作。现在的目标如下:我有一个函数 affiche_grille,它在屏幕上显示一个带有线条的网格。在每个方块内,我创建了一个带有文本(数字)的按钮。这行得通。我想在这些按钮上绑定一个 on_press 事件:但是现在,语法不再起作用......也许是因为按钮是在“with self.canvas”指令中创建的,而 self.function 不是再合适不过了吗?

这是(编辑后完成)代码:

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.graphics import Color, Line
from kivy.core.window import Window
from kivy.core.text.text_layout import layout_text
from kivy.uix.floatlayout import FloatLayout
import numpy as np
from functools import partial # for on_press syntax

class Ecran_Principal(BoxLayout):
    def build(self):
        self.orientation='vertical'
        self.liste_txt = np.zeros([9,9], dtype=object) # will contain Label
        self.grille_affichee = np.zeros([9,9])
        self.lGrille() # layout for the grid
        self.lMenu() # layout for the menu


    def lGrille(self):
        LayGrille = GridLayout(cols=1,size_hint_y=0.8)
        # window dimensions, in pixels
        (L, H) = Window.size
        H = int(0.8*H) # because of the 20% menu
        if L > H: # landscape format - computer case
            self.ymin = int(0.25*H) + int(0.05*H)
            self.delta = int(0.1*H)
            self.xmin = int((L-9*self.delta)/2.)
        else: # portrait format - phone case
            self.xmin = int(0.05*L)
            self.delta = int(0.1*L)
            self.ymin = int(0.25*H) + int((H-9*self.delta)/2.)
        # end dimensions
        self.deltaxrel = self.delta/H
        self.deltayrel = self.delta/L
        # grid definition (without numbers)
        with self.canvas:
            Color(1, 1, 1) # white
            # automatic line traces
            for i in range(4):
                # big vertical lines
                ymax = self.ymin+9*self.delta
                xligne = self.xmin+i*3*self.delta
                Line(points=[xligne, self.ymin, xligne, ymax], width=2)
                # big horizontal lines
                xmax = self.xmin+9*self.delta
                yligne = self.ymin+i*3*self.delta
                Line(points=[self.xmin, yligne, xmax, yligne], width=2)
                # little intermediary lines
                for ipetit in range(3):
                    if i ==3:
                        break
                    xpetit = xligne + ipetit*self.delta
                    Line(points=[xpetit, self.ymin, xpetit, ymax], width=1)
                    ypetit = yligne + ipetit*self.delta
                    Line(points=[self.xmin, ypetit, xmax, ypetit], width=1)
                # end little lines
            # end for
            # grid display :
            self.affiche_grille()
        self.add_widget(LayGrille)
        # end with

    def affiche_grille(self):
        # I tried to remove this 'with' instruction and does not change anything
        with self.canvas:
            for i in range(9): # line
                for j in range(9): # colomn
                    valeur = self.grille_affichee[i,j]
                    val = "{0:.0f}".format(valeur)
                    layout = FloatLayout(size=(self.xmin + (j+0.5)*self.delta,
                                               self.ymin + (8.5-i)*self.delta),
                                         pos_hint=(None, None))
                    montexte = Button(text=val,
                                      size_hint=(self.deltaxrel,
                                                 self.deltayrel),
                                      pos=(self.xmin + (j+0.5)*self.delta,
                                           self.ymin + (8.5-i)*self.delta),
                                      background_color = (0,0.2,0,1),
                                      background_normal = '',
                                      on_press=partial(self.choisir_valeur, i, j)
                                      )
                    self.liste_txt[i, j] = montexte
                    # THE BUTTONS AND THE TEXT ARE DISPLAYED,
                    # BUT NOTHING HAPPENS WHEN YOU PRESS THE BUTTONS
                    layout.add_widget(self.liste_txt[i, j])
                # end j
            # end i
        # end with

    def choisir_valeur(self, i, j):
        print("Hello !") # NEVER DISPLAYED :(
        #champ = TextInput(text=str(self.grille_affichee[i, j]),
        #                  font_size=30,
        #                  focus=True,
        #                  multiline=False)
        champ = Button(text=str(self.grille_affichee[i, j]))
        popup = Popup(title='Value in line {} - colomn {}'.format(i, j),
                      content=champ,
                      size_hint=(0.5,0.5))
        champ.bind(on_press=popup.dismiss)
        popup.open()

    def lMenu(self):
        LayMenu = GridLayout(cols=2, rows=2, size_hint_y=0.2)
        # Bouton Résoudre
        self.BoutonResoudre=Button(text='Resoudre',size_hint=(0.5,0.1),pos_hint={'left': 0.},background_color=[0.9,0.9,0.9,1])
        self.BoutonResoudre.bind(on_press=self.resoudre)
        LayMenu.add_widget(self.BoutonResoudre)
        # Bouton Remplir
        self.BoutonScanner=Button(text='Scanner',size_hint=(0.5,0.1),pos_hint={'left': 0.5},background_color=[0.9,0.9,0.9,1])
        self.BoutonScanner.bind(on_press=self.scanner)
        LayMenu.add_widget(self.BoutonScanner)
        # Bouton Indice
        self.BoutonIndice=Button(text='Indice',size_hint=(0.5,0.1),pos_hint={'bottom': 0.},background_color=[0.9,0.9,0.9,1])
        self.BoutonIndice.bind(on_press=self.indice)
        LayMenu.add_widget(self.BoutonIndice)
        # Bouton Quitter
        self.BoutonQuitter=Button(text='Quitter',size_hint=(0.5,0.1),background_color=[0.9,0.9,0.9,1])
        self.BoutonQuitter.bind(on_press=self.quitter)
        LayMenu.add_widget(self.BoutonQuitter)

        self.add_widget(LayMenu)

    def resoudre(self, instance):
        content = Button(text='Resolution du sudoku', font_size=20)
        popup = Popup(title='RESOLUTION',content=content, size_hint=(0.5,0.5))
        content.bind(on_press=popup.dismiss)
        popup.open()

    def scanner(self, instance):
        content = Button(text='Remplissage auto par photo', font_size=20)
        popup = Popup(title='SCAN PHOTO',content=content, size_hint=(0.5,0.5))
        content.bind(on_press=popup.dismiss)
        popup.open()

    def indice(self, instance):
        content = Button(text='Affichage d\'un indice', font_size=20)
        popup = Popup(title='INDICE',content=content, size_hint=(0.5,0.5))
        content.bind(on_press=popup.dismiss)
        popup.open()

    def quitter(self, instance):
        content = Button(text='Au revoir !', font_size=20)
        popup = Popup(title='QUITTER',content=content, size_hint=(0.5,0.5))
        content.bind(on_press=exit())
        popup.open()


class Sudoku(App):
    def build(self):
        ecran=Ecran_Principal()
        ecran.build()
        return ecran


if __name__ == '__main__':
    Sudoku().run()

一切都被解释了,但网格内的按钮不起作用......我已经看到了 functools.partial() 提示,但这似乎还不够......

有谁知道发生了什么?我对 python 中的类不是很熟悉,我当然错过了一些东西。提前谢谢你,如果问题太基本,对不起。

4

1 回答 1

2

好吧,您现在知道不能将小部件添加到画布。此外,您的课程中不应有 build 方法Ecran_Principalbuild()只属于Sudoku()App 类。改为使用__init__

我认为,如果您尝试将视觉内容分离为 kv 语言,事情会更容易。下面是一个使用 GridLayouts 的间距和填充来“绘制”游戏板的示例。这些按钮与一个简单的回调挂钩。

btngrid.py

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button


class SmallGrid(GridLayout):
    pass


class BigGrid(GridLayout):
    pass


class GameBoard(AnchorLayout):
    # A nice layout to use to keep things centered. Only one widget can be added to this.

    def __init__(self, *args, **kwargs):
        super(GameBoard, self).__init__(*args, **kwargs)

        big = BigGrid()

        for i in range(9):
            small = SmallGrid()
            for j in range(9):
                small.add_widget(Button(text="{}, {}".format(i, j), on_release=self.callback))

            big.add_widget(small)

        self.add_widget(big)

    def callback(self, button):
        print button.text


class RootWidget(BoxLayout):

    def __init__(self, *args, **kwargs):
        super(RootWidget, self).__init__(*args, **kwargs)
        self.orientation = 'vertical'
        self.add_widget(GameBoard())
        bottom_btns_container = GridLayout(cols=2, size_hint=(1, .2))

        for i in range(4):
            # Just for show, they don't do anything
            bottom_btns_container.add_widget(Button(text="Button {}".format(i)))

        self.add_widget(bottom_btns_container)


class BtnGridApp(App):

    def build(self):
        return RootWidget()

btngird.kv

<BigGrid>:
    cols: 3
    size_hint: (None, .8)  # scales
    width: self.height  # scales
    spacing: 5  # Width of lines
    padding: 5  # perimeter border
    # This draws a background for the whole grid.
    # When used with spacing and padding, part of the background will show.
    # Same with SmallGrid below
    canvas.before:
        Color:
            rgba: [.9, .9, .9, 1]
        Rectangle:
            pos: self.pos
            size: self.size    

<SmallGrid>:
    cols: 3
    size_hint: (None, .8)  # scales
    width: self.height  # scales
    spacing: .25
    canvas.before:
        Color:
            rgba: [.6, .6, .6, 1]  # White lines
        Rectangle:
            pos: self.pos
            size: self.size    

<GameBoard>:
    anchor_x: "center"
    anchor_y: "center"
于 2017-07-14T02:08:39.417 回答