Search code examples
python-3.xkivykivy-language

ObjectProperty has no attribute (Kivy, Python 3.x)


This questions has been asked and answered quite often, but I still can't get it to work. I want to access a widget that is a children of another widget. For this I used an ObjectProperty. When I try to access the ObjectProperty (by opening any file via the FileChooser popup) to change the label's text I get this error:

self.pdfpages.text = "change to this" AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'

Do I need to initialize the ObjectProperty? Or is there a problem with my .kv structure?

main.py

import kivy
kivy.require('1.10.0') # replace with your current kivy version !
# Kivy Imports
from kivy.app import App
#UI Eleemnts
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.scrollview import ScrollView
from kivy.uix.scatter import Scatter
from kivy.uix.image import Image

#Core Elements
from kivy.core.image import Image as CoreImage
from kivy.properties import ObjectProperty

# Python Imports
import os

class Window(BoxLayout):
    #add child widgets (in kv file)
    pass


class PDFView(ScrollView):
    pdfpages = ObjectProperty()

    def newPage(self, filepath):
         #here the error occurs   
         self.pdfpages.text = "change to this"

class SideBar(BoxLayout):
    def openfile(self):
        print("Button pressed")
        OpenDialog().open()

class OpenDialog(Popup):
    def cancelfile(self):
        #close popup
        self.dismiss()
        print("No file opened")

    def openfile(self, path, selection):
        #selection can contain multiple files, [0] is first or only
        self.dismiss()
        print("Opened File: " + os.path.join(path, selection[0]))
        #open PDFView class
        PDFView.newPage(PDFView, os.path.join(path, selection[0]))        


class PDFEditor(App):
    title = "PDFEditor"
    #gets called on startup
    #load program
    def build(self):
        #return root node
        return Window()

if __name__ == '__main__':
    PDFEditor().run()

PDFEditor.kv

<Window>:
    #this is the main (root) "window"
    orientation: "horizontal"
    SideBar:
        size_hint: (.1, 1)
    PDFView:
        size_hint: (.9, 1)

<PDFView>:
    #handler for ObjectProperty
    pdfpages: pdfpages
    Scatter:
    #only zoom
        BoxLayout:
            #add Images in here somehow
            orientation: "vertical"
            Label:
                id: pdfpages
                text: "hi"


<SideBar>:
    orientation: "vertical"
    Button:
        id: btn_openfile
        on_release: root.openfile()
        text: "Open File"
    Label:
        text: "Hello!"

<OpenDialog>:
    title: "Choose File"
    BoxLayout:
        #Fullscreen
        size: root.size
        pos: root.pos
        orientation: "vertical"
        FileChooserListView:
            id: chsr_open
        BoxLayout:
            size_hint_y: None
            height: 30
            Button:
                text: "Cancel"
                on_release: root.cancelfile()
            Button:
                text: "Open"
                on_release: root.openfile(chsr_open.path, chsr_open.selection)

Solution

  • The problem here is that you set the text of a new instance, wich is not even on the window, rather than the one you allready have.
    To fix this, you need to access the one you have.

    First, make your Window as an App attribute, so you can reference it later.

    class PDFEditor(App):
        title = "PDFEditor"
        def build(self):
            self.root = Window()
            return self.root
    

    Then give the PDFView an id in kv

    <Window>:
        orientation: "horizontal"
        SideBar:
            size_hint: (.1, 1)
        PDFView:
            id: pdfview
            size_hint: (.9, 1)
    

    Then in your openfile method, you can get the running apps root attribute you created earlier, like this.

    def openfile(self, path, selection):
        self.dismiss()
        print("Opened File: " + os.path.join(path, selection[0]))
        app = App.get_running_app()
        pdf = app.root.ids.pdfview
        pdf.newPage(os.path.join(path, selection[0]))
    

    That way you can access the ids of your Window class