python-3.xbuttonkivyorthogonal

How to work with a isometric / orthogonal view in Kivy?


The image below is a GridLayout 10 x 10 with buttons.

https://i.sstatic.net/yZsJI.png

I'd like to create the same Grid but in an isometric / orthogonal 2d view.

It means that every button, instead of being a square, it might be like a Rhombus, as the image below:

enter image description here

How can I do this?


Solution

  • I don't think you can actually do a 3D rotation on kivy UIX widgets, but you can do 2D rotations, and scaling. Here is an example of an App that does it in the build() method:

    from kivy.app import App
    from kivy.graphics.context_instructions import PushMatrix, Rotate, Scale, PopMatrix
    from kivy.properties import BooleanProperty
    from kivy.uix.button import Button
    from kivy.uix.gridlayout import GridLayout
    import numpy as np
    
    
    def matrixToNumpy(mat):
        a = []
        for i in range(4):
            b = []
            for j in range(4):
                b.append(mat[i*4+j])
            a.append(b)
        npmat = np.mat(a)
        return npmat
    
    
    class MyButton(Button):
    
        def on_touch_down(self, touch):
            if not self.parent.touched:
                self.parent.touched = True
                if self.parent.mat is None:
                    scale = matrixToNumpy(self.parent.sca.matrix)
                    rotate = matrixToNumpy(self.parent.rot.matrix)
                    self.parent.mat = np.matmul(rotate, scale)
                    self.parent.inv_mat = self.parent.mat.I
                npTouch = np.mat([touch.x, touch.y, 0, 1.0])
                convTouch = np.matmul(npTouch, self.parent.inv_mat)
                touch.x = convTouch[0,0]
                touch.y = convTouch[0,1]
            return super(MyButton, self).on_touch_down(touch)
    
        def on_touch_up(self, touch):
            self.parent.touched = False
            return super(MyButton, self).on_touch_up(touch)
    
    
    class MyGridLayout(GridLayout):
        touched = BooleanProperty(False)
    
        def __init__(self, **kwargs):
            super(MyGridLayout, self).__init__(**kwargs)
            self.mat = None
            self.inv_mat = None
    
    
    class MyApp(App):
        def build(self):
            layout = MyGridLayout(cols=10)
            with layout.canvas.before:
                PushMatrix()
                layout.sca = Scale(1.0, 0.5, 1.0)
                layout.rot = Rotate(angle=45, axis=(0,0,1), origin=(400,300,0))
            with layout.canvas.after:
                PopMatrix()
            for i in range (1, 101):
                layout.add_widget(MyButton(text=str(i)))
            return layout
    
    MyApp().run()
    

    I suspect that with clever application of those two kivy.graphics.context_instructions, you can simulate what you want. The PushMatrix() and PopMatrix() confine the effects of the Scale and Rotate to just the MyGridLayout. You should be able to adjust these values using the layout.sca and layout.rot references.

    I noticed after my original answer that the Buttons looked good, but no longer worked. I added a bunch of code to address that issue. All the numpy matrix stuff is just to get the mouse press position into the same coordinates as the MyGridLayout. Unfortunately, applying Canvas scaling and rotations isn't automatically taken into account by the Kivy events, so the additional code is necessary.

    Here is what it looks like: This is the resulting button array