2

I'm trying to make a kind of basic photo editor in kivy. It's supposed to allow the placement and transformation of a base image as well as adding text labels. I'm implementing this functionality using scatters, but I'm running across a problem where I can't get the root scatter to match the size of its child label or image. This makes it hard to actually drag and scale the image and labels, as there's invisible scatter space around each of them that makes it practically impossible to drag and resize the desired scatter.

Here's my python code:

from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.colorpicker import ColorPicker
from kivy.logger import Logger
from kivy.graphics import Color, Ellipse, Line
from kivy.properties import ListProperty
from kivy.properties import NumericProperty
from kivy.properties import ObjectProperty
from kivy.core.window import Window
import os
#from editor import PhotoEditor
import random

#Each class def is a different screen.
class WelcomeScreen(Screen):
    pass

class PhoneSelection(Screen):
    pass

class StyleSelection(Screen):
    pass

class ColorSelection(Screen):
    pass

class DesignSelection(Screen):
    pass

class UploadPhoto(Screen):
    pass

class PatternSelection(Screen):
    pass

class PhotoEditor(Screen):
    color = ListProperty([1,1,1,1])
    word = 'Enter Text'
    font_size = 14
    font_type = ''
    image_path = 'Mike1.png'
    erase = False
    def open_popup(self):
        popup = ColorPopup()
        popup.open()
    def close_popup(self):
        color_bttn = self.ids['color_bttn']
        color_bttn.background_color = self.color
        color_bttn.color = [1 - self.color[0], 1 - self.color[1], 1 - self.color[2], 1]
    def open_text_entry(self):
        al = AnchorLayout(anchor_x='center',anchor_y='top')
        popup = TextLabelEntry()
        al.add_widget(popup)
        popup.open()
    def close_text_entry(self):
        l = Label(text=self.word,color=self.color,font_size=40)
        t = TextLabel()
        t.add_widget(l)
        t.size = l.size
        self.ids['il'].add_widget(t)

class Confirm(Screen):
    pass

class Pay(Screen):
    pass

class ThankYou(Screen):
    pass

#These classes are used as parts of the UI for other screens.
class ColorPopup(Popup):
    pass
class TextLabel(Scatter):
    pass
class TextLabelEntry(Popup):
    fonts = []
    def font_selector(self):
        for file in os.listdir("/mydir"):
            if file.endswith(".tff"):
                fonts.append(file)

class ImageLabel(ScatterLayout):
    pass
class Variables():
    #used for storing info about the case to be printed
    phone_type = ""
    phone_color = ""
    phone_style = ""
    phone_design_option = ""
    total_price = 0.0

#This is the main class that has all the app info.
class MainApp(App):
    #Window.size = (1920, 1080)
    #Window.fullscreen = True
    #list of names for each screen. Each name is used as an ID when changing the current screen.
    screens = ["welcome","choose_phone","choose_style","choose_color","choose_design","upload_photo","choose_pattern","photo_edit","confirm","pay","thanks"]
    vars = Variables()
    #The manager that holds all the screens and allows transitioning between them.
    SM = ScreenManager()
    def build(self):
        #list of names for each screen. Each name is used as an ID when changing the current screen.
        screens = ["welcome","choose_phone","choose_style","choose_color","choose_design","upload_photo","choose_pattern","photo_edit","confirm","pay","thanks"]

        #Add all screens to the manager and assign the corresponding ID.
        self.SM.add_widget(WelcomeScreen(name=screens[0]))
        self.SM.add_widget(PhoneSelection(name=screens[1]))
        self.SM.add_widget(StyleSelection(name=screens[2]))
        self.SM.add_widget(ColorSelection(name=screens[3]))
        self.SM.add_widget(DesignSelection(name=screens[4]))
        self.SM.add_widget(UploadPhoto(name=screens[5]))
        self.SM.add_widget(PatternSelection(name=screens[6]))
        self.SM.add_widget(PhotoEditor(name=screens[7]))
        self.SM.add_widget(Confirm(name=screens[8]))
        self.SM.add_widget(Pay(name=screens[9]))
        self.SM.add_widget(ThankYou(name=screens[10]))

        #Set the current screen to the welcome screen.
        self.SM.current = screens[0]
        return self.SM

#Runs the app.
if __name__ == "__main__":
    t = MainApp();
    t.run()

Most of the action happens in the PhotoEditor class.

And here's my kivy code:

<WelcomeScreen>:
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'bottom'
        Button:
            text: "Welcome!"
            size_hint: 0.1,0.1
            on_press: root.manager.current = app.screens[7] 

<PhoneSelection>:
    BoxLayout:
        Button:
            text: "iPhone 4/4s"
            on_press: 
                root.manager.current = app.screens[1] 
                app.vars.phone_type = "iPhone YAYAYA"
                self.text = str(app.vars.phone_type)

<PhotoEditor>
    id: PhotoEditor
    FloatLayout:
        id: il
        ImageLabel:
            auto_bring_to_front: False
            scale_min: 100/img.width
            scale_max: 5000/img.width
            id: ilr
            Image:
                id: img
                source: root.image_path
                #width: il.width
                #height: il.height/self.image_ratio
                on_touch_up:
                    if int(ilr.rotation % 15) < 8: ilr.rotation = int(ilr.rotation - int(ilr.rotation % 15))
                    if int(ilr.rotation % 15) >= 8: ilr.rotation = int(ilr.rotation - int(ilr.rotation % 15)) + 15
    Image:
        id: ovrly
        source: 'overlay/iphone6.png'
    AnchorLayout:
        anchor_x: 'left'
        anchor_y: 'center'
        BoxLayout:
            size_hint: 0.1, 0.5
            orientation: 'vertical'
            Button:
                text: 'Color'
                color: [1 - root.color[0], 1 - root.color[1], 1 - root.color[2], 1]
                background_color: root.color
                background_normal: ''
                id: color_bttn
                on_press: root.open_popup()
            Button:
                group: 'text'
                text: 'Text'
                on_press: root.open_text_entry()

<ColorPopup>
    size_hint: .5, .5
    auto_dismiss: False
    title: 'Hello world'
    on_open: 
        r.value = int(r.max * app.SM.get_screen('photo_edit').color[0])
        g.value = int(g.max * app.SM.get_screen('photo_edit').color[1])
        b.value = int(b.max * app.SM.get_screen('photo_edit').color[2])
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'center'
        AnchorLayout:
            anchor_x: 'center'
            anchor_y: 'center'
            BoxLayout:
                orientation: 'vertical'
                Slider:
                    id: r
                    on_value: 
                        app.SM.get_screen('photo_edit').color[0] = self.value_normalized
                        bttn.background_color = app.SM.get_screen('photo_edit').color
                        bttn.color = [1 - app.SM.get_screen('photo_edit').color[0], 1 - app.SM.get_screen('photo_edit').color[1], 1 - app.SM.get_screen('photo_edit').color[2], 1]
                Slider:
                    id: g
                    on_value: 
                        app.SM.get_screen('photo_edit').color[1] = self.value_normalized
                        bttn.background_color = app.SM.get_screen('photo_edit').color
                        bttn.color = [1 - app.SM.get_screen('photo_edit').color[0], 1 - app.SM.get_screen('photo_edit').color[1], 1 - app.SM.get_screen('photo_edit').color[2], 1]
                Slider:
                    id: b
                    on_value: 
                        app.SM.get_screen('photo_edit').color[2] = self.value_normalized
                        bttn.background_color = app.SM.get_screen('photo_edit').color
                        bttn.color = [1 - app.SM.get_screen('photo_edit').color[0], 1 - app.SM.get_screen('photo_edit').color[1], 1 - app.SM.get_screen('photo_edit').color[2], 1]
                Button:
                    text: 'Close'
                    id: bttn
                    background_normal: ''
                    on_press:
                        app.SM.get_screen('photo_edit').close_popup()
                        root.dismiss()


<ImageLabel>

<TextLabel>
    size: lbl.size
    on_touch_down: if app.SM.get_screen('photo_edit').erase == True: app.SM.get_screen('photo_edit').remove_widget(self)
    Label:
        id: lbl
        #on_size: root.size = self.size

<TextLabelEntry>
    size_hint: 0.5,0.5
    title: 'Text Entry'
    auto_dismiss: True
    on_open: 
        app.SM.get_screen('photo_edit').word = 'Enter Text'
    BoxLayout:
        size_hint: 0.5,0.2
        orientation: 'horizontal'
        TextInput:
            id: txt
            text: 'Enter Text'
            on_text: app.SM.get_screen('photo_edit').word = self.text
        Button:
            on_press:
                app.SM.get_screen('photo_edit').close_text_entry()
                root.dismiss()

This has been a problem for me for a while, and no matter what I've tried I can't get it fixed. Any help doing so would be greatly appreciated.

watswat5
  • 671
  • 1
  • 7
  • 19
  • I talk about this issue in the video [here](https://www.youtube.com/watch?v=2Gc8iYJQ_qk), maybe that can help. – inclement Nov 05 '15 at 18:03

1 Answers1

2

Everything is in your title!

The Scatter must have the same size as the Image. Which means that it is the image which defines the size of the scatter.

ScatterLayout:
    size: img.size
    size_hint: (None,None)
    Image:
        id: img
        size: (500, 500/self.image_ratio)    
        # Here I fix the width to 500p, 
        # You could fix the height or make them
        # related to the size of your screen.

Do you know that you have examples of what you can do in your kivy installation folder? One you in particular should interest you in kivy34\examples\demo\pictures

BR.

PS: for my part I had to delete your Image with the "ovrly" id to see something.

JGcht
  • 246
  • 2
  • 4