Michael Green
Michael Green

Reputation: 810

self.add_widget not responding to pos=() arguments

I have some code which is attempting to place a graph at the top of the window with text/button inputs underneath. I'm using FigureCanvas to generate the window and trying to add widgets simultaneously via the python approach and the kivy approach (Python approach seems necessary since it's a specialized backdoor, and kivy for everything else just seems easier). Anyways, I have it such that my Meta class is evoking a graph class to add the graph window widget to the main window above the GridLayout which is going to hold all the user inputs. My class graph(FloatLayout) responds to size_hint/size inputs but isn't responding to pos_hint/pos.

I'm hoping for help in either one of two ways:

1) how can I get the class graph(FloatLayout) to take position at the top half of the screen?

or alternatively,

2) how can I get kivy to add the graph first into the GridLayout and avoid the issue of the extra class graph(FloatLayout) alltogether?

I have the size set arbitrarily small to show the buttons behind the graph. Eventually I'm going to be calling other methods to add/remove plots to the graph (already have that part working fine) so try not to screw with the def graph(self) portion unless necessary.


import matplotlib.pyplot as plt
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.config import Config
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvas
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.window import Window



Config.set('graphics', 'borderless', '1')
Config.set('graphics', 'width', '1200')
Config.set('graphics', 'height', '400')
Config.set('graphics', 'resizable', '0')

KV = '''
<Meta>:
    GridLayout:
        size: root.width, root.height/2
        rows: 5
        BoxLayout:
            Button:
                text: 'test1'
            Button:
                text: 'test2'
        BoxLayout:
            Button:
                text: 'test3'
            Button:
                text: 'test4'
'''

Builder.load_string(KV)


class Graph(FloatLayout):
    def __init__(self, **kwargs):
        super(Graph, self).__init__(**kwargs)
        self.add_widget(self.graph())

    def graph(self):
        global fig1, ax
        fig1 = plt.figure()
        ax = fig1.add_subplot(111)
        ax.plot([], [])
        wid = FigureCanvas(fig1)
        return wid


class Meta(Widget):
    def __init__(self, **kwargs):
        super(Meta, self).__init__(**kwargs)
        self.add_widget(Graph(size_hint=(None,None), size=(Window.width/5, Window.height/5), pos_hint=(None,None), pos=(1000,1000)))


class Builder(App):
    def build(self):
        return Meta()


if __name__ == "__main__":
    Builder().run()

Upvotes: 0

Views: 447

Answers (2)

ikolim
ikolim

Reputation: 16031

Question 1

How can I get the class Graph(FloatLayout) to take position at the top half of the screen?

Answer

To place widget within a layout using pos_hint or pos, the best layout to use is a FloatLayout. Please refer to the solution, "Method 1 - using FloatLayout as root" for details.

Question 2

How can I get kivy to add the graph first into the GridLayout and avoid the issue of the extra class graph(FloatLayout) altogether?

Solution

There are two solutions to the problem. Method 1 using FloatLayout as the root, and Method 2 using BoxLayout as root. Both methods does not require an extra class.

Method 1 - using FloatLayout as root

This method also use pos_hint to place the graph in the the top half and the buttons in the bottom half.

kv file

  1. Add size_hint_y: 0.5 and pos_hint: {'bottom': 0.5} to the child, GridLayout:

Snippets - kv file

<Meta>:
    GridLayout:
        size_hint_y: 0.5
        pos_hint: {'bottom': 0.5}
        ...

py file

  1. Replace class Meta(Widget): with class Meta(FloatLayout):
  2. Replace class Graph(FloatLayout): with class Graph(BoxLayout):
  3. Replace self.add_widget(Graph(...)) with self.add_widget(Graph(size_hint_y=0.5, pos_hint={'top': 1}))

Snippets - py file

class Graph(BoxLayout):
...

class Meta(BoxLayout):
    def __init__(self, **kwargs):
        super(Meta, self).__init__(**kwargs)
        self.ids.graph.add_widget(Graph(size_hint_y=0.5, pos_hint={'top': 1}))


class Builder(App):
    title = 'Method 1 - FloatLayout, pos_hint & size_hint_y'

    def build(self):
        return Meta()

Method 2 - using BoxLayout as root

kv file

  1. Add orientation: 'vertical to class rule, <Meta>:
  2. Add child, BoxLayout: with an id: graph, and size_hint_y: 0.5
  3. Add size_hint_y: 0.5 to the child, GridLayout:

Snippets - kv file

<Meta>:
    orientation: 'vertical'

    BoxLayout:
        id: graph
        size_hint_y: 0.5

    GridLayout:
        size_hint_y: 0.5
        ...

py file

  1. Replace class Meta(Widget): with class Meta(BoxLayout):
  2. Replace class Graph(FloatLayout): with class Graph(BoxLayout):
  3. Replace self.add_widget(Graph(...)) with self.ids.graph.add_widget(Graph())

Snippets - py file

class Graph(BoxLayout):
...

class Meta(BoxLayout):
    def __init__(self, **kwargs):
        super(Meta, self).__init__(**kwargs)
        self.ids.graph.add_widget(Graph())


class Builder(App):
    title = 'Method 2 - BoxLayout & size_hint_y'

    def build(self):
        return Meta()

Upvotes: 0

Michael Green
Michael Green

Reputation: 810

Adding an extra class which inherits from the KVlang will do the trick.

import matplotlib.pyplot as plt
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.config import Config
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvas
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.window import Window



Config.set('graphics', 'borderless', '1')
Config.set('graphics', 'width', '1200')
Config.set('graphics', 'height', '400')
Config.set('graphics', 'resizable', '0')

KV = '''
<Meta>:
    orientation: 'vertical'


<Body>:
    GridLayout:
        size: root.width, root.height
        rows: 5
        BoxLayout:
            Button:
                text: 'test1'
            Button:
                text: 'test2'
        BoxLayout:
            Button:
                text: 'test3'
            Button:
                text: 'test4'
'''

Builder.load_string(KV)


class Graph(BoxLayout):
    def __init__(self, **kwargs):
        super(Graph, self).__init__(**kwargs)
        self.add_widget(self.graph())

    def graph(self):
        global fig1, ax
        fig1 = plt.figure()
        ax = fig1.add_subplot(111)
        ax.plot([], [])
        wid = FigureCanvas(fig1)
        return wid


class Body(Widget):
    pass


class Meta(BoxLayout):
    def __init__(self, **kwargs):
        super(Meta, self).__init__(**kwargs)
        self.add_widget(Graph())
        self.add_widget(Body())

class Builder(App):
    def build(self):
        return Meta()


if __name__ == "__main__":
    Builder().run()

Upvotes: 0

Related Questions