简体   繁体   中英

Kivy: How to make label size and size of a canvas equal?

I have made a square shaped grid of labels using Gridlayout. Now i want to add a background color the labels(each having different rectangles). I tried to do this by the following code.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Rectangle, Color
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label


class MyGrid(FloatLayout):
    def __init__(self,**kwargs):
        super(MyGrid,self).__init__(**kwargs)
        self.grid=GridLayout()
        self.grid_size=4
        self.grid.cols=self.grid_size
        for k in range(self.grid_size):
            for i in range(self.grid_size):
                with self.grid.canvas:
                    Rectangle(size=(100,100),pos=(k*160+100,i*160+100),source="52852.JPG")
        for h in range(self.grid_size):
            for j in range(self.grid_size):
                self.grid.add_widget(Label(text="labwl"+str(h)+str(j),size=(100,100),pos=(h*160+100,j*160+100)))
        self.add_widget(self.grid)

class GameApp(App):
    def build(self):
        return MyGrid()

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

In this code if I do not specify "self.grid.cols" it generates a warning in the python console and also when the window is turned to full screen mode the rectangles from canvas retain there original size and position but the labels do not. I want to get the labels in front of the rectangles of canvas and they should also retain the size of the screen as specified. Moreover if I change the "self.grid.size" to any other number it should make the grid of labels of that length and corresponding number of canvas too. I tried float layout for this purpose also but it was of no help. The canvas rectangles and labels should fit in the window whatever the size of window has. It would be better if I can get the solution to above problem written in python file(not in.kv file). If you know any other solution to this problem or any other widget please let me know. Like for button widget we can specify the background color and text also your can add any of that widget which will do above task. You should replace the "source" in the rectangle canvas to any known image file. I hope you understand. If you do not please do let me know. :)

Setting the Widgets to not change size or pos is the easiest solution. Basically just use size_hint=(None, None) and don't use the GridLayout :

from kivy.app import App
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label


class MyGrid(FloatLayout):
    def __init__(self,**kwargs):
        super(MyGrid,self).__init__(**kwargs)
        self.grid_size=4
        for k in range(self.grid_size):
            for i in range(self.grid_size):
                with self.canvas:
                    Rectangle(size=(100,100),pos=(k*160+100,i*160+100),source="52852.JPG")
        for h in range(self.grid_size):
            for j in range(self.grid_size):
                self.add_widget(Label(text="labwl"+str(h)+str(j),size=(100,100),pos=(h*160+100,j*160+100), size_hint=(None, None)))

class GameApp(App):
    def build(self):
        return MyGrid()

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

To make the Rectangles and Labels change pos and size is a bit more complex. In the modified version of your code below, I keep lists of the Labels and the Rectangles , and bind the adjust_common_size() method to run whenever the size of MyGrid changes. That method then adjusts the size and pos of the Labels and Rectangles to match:

from kivy.app import App
from kivy.properties import ListProperty
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label


class MyGrid(FloatLayout):
    common_size = ListProperty((100, 100))
    def __init__(self,**kwargs):
        super(MyGrid,self).__init__(**kwargs)
        self.grid_size=4
        self.rects = []
        self.labels = []
        for k in range(self.grid_size):
            one_row = []
            self.rects.append(one_row)
            for i in range(self.grid_size):
                with self.canvas:
                    one_row.append(Rectangle(size=self.common_size,pos=(k*160+100,i*160+100),source="52852.JPG"))
        for h in range(self.grid_size):
            one_row = []
            self.labels.append(one_row)
            for j in range(self.grid_size):
                label = Label(text="labwl"+str(h)+str(j),size=self.common_size,pos=(h*160+100,j*160+100), size_hint=(None, None))
                one_row.append(label)
                self.add_widget(label)

        self.bind(size=self.adjust_common_size)

    def adjust_common_size(self, instance, new_size):
        self.common_size = (new_size[0] * 0.9 / self.grid_size, new_size[1] * 0.9 / self.grid_size)
        for k in range(self.grid_size):
            for i in range(self.grid_size):
                adjusted_pos = (k * new_size[0] / self.grid_size, i * new_size[1] / self.grid_size)
                rect = self.rects[k][i]
                label = self.labels[k][i]
                label.size = self.common_size
                label.pos = adjusted_pos
                rect.size = self.common_size
                rect.pos = adjusted_pos

class GameApp(App):
    def build(self):
        return MyGrid()

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

Using a ListProperty for the common_size is not necessary, but would be handy if you decide to use kv .

This is an interesting question. Here is a better way to make the Rectangle and the Label match. The code below uses the GridLayout , but defines MyLabel to include its own Rectangle and to keep its Rectangle matched in pos and size :

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label

class MyLabel(Label):
    def __init__(self, **kwargs):
        super(MyLabel, self).__init__(**kwargs)
        with self.canvas.before:
            self.rect = Rectangle(size=self.size,pos=self.pos,source="52852.JPG")
        self.bind(size=self.adjust_size)
        self.bind(pos=self.adjust_pos)

    def adjust_pos(self, instance, new_pos):
        self.rect.pos = new_pos

    def adjust_size(self, instance, new_size):
        self.rect.size = new_size


class MyGrid(FloatLayout):
    def __init__(self,**kwargs):
        super(MyGrid,self).__init__(**kwargs)
        self.grid=GridLayout()
        self.grid_size=4
        self.grid.cols=self.grid_size
        for h in range(self.grid_size):
            for j in range(self.grid_size):
                self.grid.add_widget(MyLabel(text="labwl"+str(h)+str(j),size=(100,100),pos=(h*160+100,j*160+100)))
        self.add_widget(self.grid)

class GameApp(App):
    def build(self):
        return MyGrid()

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

With this approach, you don't have to create the Rectangles in the MyGrid at all, since each Label creates its own.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM