2017-07-12 49 views
0

似乎很多人遇到與Kivy的on_press參數有問題,但我還沒有找到我的問題的答案... 發生了什麼事情: 我開始使用我的第一個應用程序在python/kivy 。我知道python,但可能不足以涉及類...我可以創建一個按鈕,用on_press操作打開一個彈出窗口。 現在我們的目標如下:我有一個函數affiche_grille,它在屏幕上顯示一個帶有線條的網格。在每個方塊內部,我創建一個帶有文本(數字)的按鈕。這工作。我想在這些按鈕上綁定一個on_press事件:但現在,語法不再起作用了......也許是因爲該按鈕是在「with self.canvas」指令中創建的,而self.function不是更適合嗎?Kivy - on_press選項在「with self.canvas」中不起作用?

這裏是(編輯完成後)代碼:

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中的類不是很熟悉,我肯定錯過了一些東西。 預先感謝您,如果問題太簡單,我們很抱歉。

+0

我不認爲這有什麼與你的問題,但我不認爲你應該做的這些在'with canvas'塊中。畫布是用於圖形指令的(基本上包裝了opengl函數),子窗口小部件是一個獨立的概念。 – syntonym

+0

謝謝你的建議。但是我只是刪除了'with canvas'塊,這並沒有改變任何東西。 – Tyty87

+0

謝謝你的建議。但是我只是刪除了'with canvas'塊,這並沒有改變任何東西。 但是,可能有線索來解釋發生了什麼: - 函數resoudre必須用2個參數定義:self,instance。如果我刪除第二個,調用'self.BoutonResoudre.bind(on_press = self.resoudre)'不起作用(TypeError:resoudre()只需要1個參數(給定2)) - 函數choisir_valeur可以定義爲no不管它包含什麼參數,並且不會導致任何問題... – Tyty87

回答

2

那麼,你現在知道你不能添加一個小部件到畫布。另外,你不應該在你的Ecran_Principal類中有一個構建方法。 build()只屬於Sudoku()應用程序類。改爲使用__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" 
+0

哇!這比我的版本更乾淨,它的工作原理^^ 我將從這個例子重新開始我的工作。 非常感謝你的這一課:) – Tyty87