Layout behaving like view in a 2D-CAD program

303 views
Skip to first unread message

audiowerk

unread,
May 16, 2014, 3:13:03 PM5/16/14
to kivy-...@googlegroups.com
Hi,

I'm in need of a layout without boundaries. It should behave like the view in a 2D-CAD program. That means that i want to
  • move the layouts center by touch, with all the children following
  • and zoom in and out by touch or mouse scroll, with all the children getting smaller or bigger
  • it should be possible to move the children around by drag
  • Furthermore it should be infinite in the x and y direction. 
I already experimented with the ScatterLayout, but it seems to have different behavior.

Do you have any ideas how i could relize this?

Thanks for any help in advance!

Amirouche Boubekki

unread,
May 21, 2014, 7:03:04 PM5/21/14
to kivy-...@googlegroups.com
Héllo,

I tried something, checkout the screenshot.

In principle the scatter widget allows to scale, rotate and translate. Changing size of markers should work but I did not test it. Translation works as expected.

Zooming is not implemented at all «zoom in and out by touch or mouse scroll, with all the children getting smaller or bigger»

But moving on the blueprint is possible as expected.

I tried with a relative layout but failed, it might possible, and would allow to keep the code simpler with other tradeoffs maybe.

- The grid is implemented with kivy.graphics.Line on_touch_move, I redraw the texture with the a new Grid.origin position.
- Marker's position as marker.pos doesn't change when you move the grid,
- look at the end of Grid.redraw, it adds the markers to the canvas which was cleared at the beginning.

Also:

- It is all based on window coordinates, except grid texture which relies on Grid.origin to draw the Line axis correctly.
- The positions you see displayed, are never actual position of Kivy widgets, except if you start the with a Grid(origin=(0, 0)).

I tried to get the grid lines thiner, but I couldn't, a problem with dpi settings? Inkscape has thiner grid lines.

There is quiet a few things to improve in terms of performance, but so far so good.

It is at bitbucket

https://bitbucket.org/amirouche/antiquote/src/98ab73a2671d024f384a01939a604d10d9aab14f/blueprint/?at=default

Clone with this command:

hg clone https://bitbucket.org/amirouche/antiquote

Tell me how it goes on your side. It requires Python 3 and Kivy 1.8.0. You can probably get it working on Python 2, I think that only super() calls are not backward compatible.


Regards,


Amirouche
 
blueprint.png

audiowerk

unread,
May 23, 2014, 9:29:23 PM5/23/14
to kivy-...@googlegroups.com
Hi,

thanks for your help and giving it a try! Because of the performance problems i thought of drawing a large (but not infinite) grid only once and then scale and translate it. This looks like this:


from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics.vertex_instructions import Rectangle, Line
from kivy.graphics.context_instructions import Color
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.widget import Widget

kv_test = '''
#:import Label kivy.uix.label

BoxLayout:
    orientation: 'vertical'
    Button:
        size_hint_y: 0.15
        text: 'Im not touchable'
    View:
        id: _view
    Button:
        size_hint_y: 0.15
        text: 'Add Item'
        on_press: _view.add_block()
'''

kv = '''
<View>:
    grid: _grid
    StencilView:
        Scatter:
            auto_bring_to_front: False
            do_rotation: False
            size: (10000, 10000)
            pos: (-5000, -5000)
            Grid:
                id: _grid

    AnchorLayout:
        anchor_x:'left'
        anchor_y:'top'
        Button:
            size_hint: (None, None)
            size: (200,40)
            text: 'Menue Dummy'
'''

class Grid(Widget):
    def __init__(self, **kwargs):
        super(Grid, self).__init__(**kwargs)
        Clock.schedule_once(self.draw_grid)

    def draw_grid(self,asdf):
        with self.canvas.before:
            
            #draw background
            Color(0.616, 0.639, 0.667, mode='rgb')
            Rectangle(size=self.parent.size)

            #draw grid
            grid_size = 10

            Color(0.576, 0.600, 0.627, mode='rgb')

            x_length, y_length = [int(abs(i)) for i in self.parent.size]

            for i in range(0,x_length, grid_size):
                Line(points=[i, 0, i, y_length])

            for i in range(0,y_length, grid_size):
                Line(points=[0, i, x_length, i])

            Color(0.537, 0.557, 0.580, mode='rgb')

            for i in range(0,x_length, grid_size*10):
                Line(points=[i, 0, i, y_length], width=1.05)

            for i in range(0,y_length, grid_size*10):
                Line(points=[0, i, x_length, i], width=1.05)

class View(RelativeLayout):
    def __init__(self, **kwargs):
        super(View, self).__init__(**kwargs)
        Builder.load_string(kv)

    def add_block(self):
        widget = Label(text='Test Label')

        s = Scatter(
            #auto_bring_to_front = False,
            do_rotation = False,
            do_scale = False,
            size = (150, 150),
            size_hint = (None, None),
            pos = (5000, 5000))
        s.add_widget(widget)
        self.grid.add_widget(s)


class TestApp(App):
    def build(self):
        return Builder.load_string(kv_test)

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

this works, but i have the problem, that the first button doesnt work. it makes sense that it doesn't, but i don't know how to bring it to front or fix it.

is there any way to cut the collide_point in general similar to StencilView for canvas?

do you have any other suggestions to fix this?

thanks for any help!

audiowerk

unread,
May 23, 2014, 10:06:50 PM5/23/14
to kivy-...@googlegroups.com
Hi,

i could fix the problem with a custom scatter and a new collide_pont function:

class CustomScatter(Scatter):

    def collide_point(self, x, y):
        x0, y0 = self.view.to_local(self.view.x, self.view.y)
        x1, y1 = self.view.to_local(self.view.right, self.view.top)

        #print('***************** x:'+str(x)+'+ y:'+str(y) + '+ self.view.x:'+str(self.view.x)+ '+ self.view.right:'+str(self.view.right)+ '+ self.view.y:'+str(self.view.y) + '+ self.view.top:'+str(self.view.top))
        #print('################# pos:'+str(self.view.to_local(self.view.x, self.view.y)))
        if x > x0 and x < x1 and y > y0 and y < y1:
            return True

        else:
            return False



Reply all
Reply to author
Forward
0 new messages