From 01c1f97d55097fdadbe44deb352c900e3891c7fd Mon Sep 17 00:00:00 2001 From: Javier Sancho Date: Wed, 18 Jul 2018 12:13:46 +0200 Subject: [PATCH 1/1] Initial commit --- LICENSE | 20 +++++ README.md | 0 VERSION | 1 + examples/designer.py | 10 +++ kivyforms/__init__.py | 1 + kivyforms/formcanvas.py | 180 ++++++++++++++++++++++++++++++++++++++++ setup.py | 26 ++++++ 7 files changed, 238 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 VERSION create mode 100644 examples/designer.py create mode 100644 kivyforms/__init__.py create mode 100644 kivyforms/formcanvas.py create mode 100644 setup.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba3ae94 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2018 Javier Sancho + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..49d5957 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1 diff --git a/examples/designer.py b/examples/designer.py new file mode 100644 index 0000000..5ccdb50 --- /dev/null +++ b/examples/designer.py @@ -0,0 +1,10 @@ +from kivy.app import App +from kivyforms import FormCanvas + +class DesignerApp(App): + def build(self): + return FormCanvas() + +if __name__ == '__main__': + app = DesignerApp() + app.run() diff --git a/kivyforms/__init__.py b/kivyforms/__init__.py new file mode 100644 index 0000000..26a0a18 --- /dev/null +++ b/kivyforms/__init__.py @@ -0,0 +1 @@ +from .formcanvas import FormCanvas diff --git a/kivyforms/formcanvas.py b/kivyforms/formcanvas.py new file mode 100644 index 0000000..0f8f2fe --- /dev/null +++ b/kivyforms/formcanvas.py @@ -0,0 +1,180 @@ +from kivy.graphics import Color, Line +from kivy.uix.behaviors import ButtonBehavior +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.button import Button +from kivy.uix.stacklayout import StackLayout + +class Grabbable(BoxLayout): + def __init__(self, **kwargs): + super(Grabbable, self).__init__(**kwargs) + self.detached = False + self.form_canvas = None + + self.point_area = None + with self.canvas.after: + Color(1, 1, 0) + self.line = Line(points=[]) + + def point(self, touch=None): + if touch: + side = self.x + self.width / 6 + if touch.x > side * 5: + self.point_area = 'right' + self.line.points = [self.x + self.width + 5, self.y + 5, self.x + self.width + 5, self.y + self.height - 5] + elif touch.x < side: + self.point_area = 'left' + self.line.points = [self.x - 5, self.y + 5, self.x - 5, self.y + self.height - 5] + elif touch.y > self.y + self.height / 2: + self.point_area = 'top' + self.line.points = [self.x + 5, self.y + self.height + 5, self.x + self.width - 5, self.y + self.height + 5] + else: + self.point_area = 'bottom' + self.line.points = [self.x + 5, self.y - 5, self.x + self.width - 5, self.y - 5] + + else: + self.point_area = None + self.line.points = [] + + def get_form_canvas(self): + parent = self.parent + while parent and not isinstance(parent, FormCanvas): + parent = parent.parent + return parent + + def get_index(self): + for idx in range(len(self.parent.children)): + if self.parent.children[idx] is self: + return idx + return None + + def detach(self): + self.detached = True + self.form_canvas = self.get_form_canvas() + root_window = self.get_root_window() + self.parent.remove_widget(self) + self.size_hint = (None, None) + self.width = 150 + self.height = 50 + root_window.add_widget(self) + + def attach(self, x, y): + self.detached = False + self.parent.remove_widget(self) + self.height = self.form_canvas.widgets_height + self.size_hint = self.form_canvas.widgets_size_hint + + for widget in self.form_canvas.walk(restrict=True): + if type(widget) is Grabbable and widget.collide_point(x, y): + idx = widget.get_index() + if widget.point_area == 'top': + widget.parent.add_widget(self, index=idx + 1) + elif widget.point_area == 'bottom': + widget.parent.add_widget(self, index=idx) + elif widget.point_area == 'left': + parent = widget.parent + box = parent.create_box() + parent.add_widget(box, index=idx) + box.add_widget(self) + parent.remove_widget(widget) + box.add_widget(widget) + else: + parent = widget.parent + box = parent.create_box() + parent.add_widget(box, index=idx) + parent.remove_widget(widget) + box.add_widget(widget) + box.add_widget(self) + widget.point() + break + else: + self.form_canvas.add_widget(self) + + def on_touch_down(self, touch): + if self.collide_point(*touch.pos): + if touch.grab_current is None: + touch.grab(self) + return True + return super(Grabbable, self).on_touch_down(touch) + + def on_touch_move(self, touch): + if touch.grab_current is self: + self.pos = [touch.x - self.width / 2, touch.y - self.height / 2] + self.point() + if not self.detached: + self.detach() + else: + if self.parent and self.parent != self.get_root_window(): + if self.collide_point(touch.x, touch.y): + self.point(touch) + else: + self.point() + + def on_touch_up(self, touch): + if touch.grab_current is self: + touch.ungrab(self) + if self.detached: + self.attach(touch.x, touch.y) + + +class FormCanvas(ButtonBehavior, StackLayout): + def __init__(self, *args, **kwargs): + super(FormCanvas, self).__init__(*args, **kwargs) + self.n = 0 + self.orientation = 'lr-tb' + self.padding = [10, 10, 10, 10] + self.spacing = [10, 10] + + self.widgets_height = 40 + self.widgets_size_hint = (1, None) + + def on_press(self, *args): + self.n += 1 + g = Grabbable( + height=self.widgets_height, + size_hint=self.widgets_size_hint + ) + g.add_widget(Button(text=str(self.n))) + self.add_widget(g) + #print(self.export_to_kv(self)) + + def create_box(self): + return BoxLayout( + orientation='horizontal', + height=self.widgets_height, + size_hint=self.widgets_size_hint, + spacing=self.spacing[0] + ) + + def export_to_kv(self, widget, indent=''): + kv = """StackLayout: + orientation: '{orientation}' + padding: {padding} + spacing: {spacing} +""".format(orientation=self.orientation, padding=self.padding, spacing=self.spacing) + + indent = ' ' + stack = [self] + + widgets = self.walk(restrict=True) + next(widgets) # the first widget is the FormCanvas + for widget in widgets: + if not type(widget) is Grabbable: + # Look for the widget position inside the tree + parent = widget.parent + if type(parent) is Grabbable: + parent = parent.parent + while not parent is stack[-1]: + stack.pop() + + # Widget header + kv += '{indent}{widget}:\n'.format(indent=indent*len(stack), widget=type(widget).__name__) + + stack.append(widget) + + # Widget attributes + kv += """{indent}height: 40 +{indent}size_hint: (1., None) +{indent}text: '{text}' +""".format(indent=indent*len(stack), text=widget.text) + + return kv diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0387653 --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +from setuptools import setup +import os + +def get_version(): + with open('VERSION') as fd: + return fd.read().strip() + +def get_long_description(): + with open(os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'README.md' + ), encoding='utf8') as fp: + return fp.read() + + +setup( + name='kivyforms', + version=get_version(), + description='Tools for making forms easy', + long_description=get_long_description(), + long_description_content_type='text/markdown', + author='Javier Sancho', + url='https://git.jsancho.org/kivyforms', + license='MIT', + packages=['kivyforms'], + install_requires=['kivy'] +) -- 2.39.5