Home > database >  Trying to update an image texture in screenmanager throws index out of range error
Trying to update an image texture in screenmanager throws index out of range error

Time:07-08

Im trying to update the texture of an image on a second screen on my application. When I try to do this it throws an Index out of range error.

I think this might be because im not updating this on the main thread as im trying to do it via the second window, but I dont know how I could structure the program any differently, without splitting up the two screens.

Code and Logs and screenshots

WindowManager:
    LoginWindow:
    MainWindow: 


<LoginWindow>:
    name: "Login"

    MDTextField:
        
        id: ipAddress

        hint_text: "drone ip"

        size_hint_x: None
        size_hint_y: None

        height: 30
        width: 200
        pos_hint: {"center_x": .5, "center_y": .55}

        on_text_validate: app.ip_validate(ipAddress.text)


    MDSpinner:

        id: spinnerIP

        size_hint: None, None
        size: dp(20), dp(20)
        pos_hint: {'center_x': .5, 'center_y': .45}
        
        active: False

    MDLabel:

        id: timeLbl

        text: "I am here"
        theme_text_color: "Hint"
        font_size: ipAddress.font_size

        pos_hint: {'center_x': 0.52, 'center_y': 0.95}


<MainWindow>

    name: "Main"

    MDLabel:

        id: timeLbl

        text: "I am here"
        theme_text_color: "Hint"

        pos_hint: {'center_x': 0.52, 'center_y': 0.95}

    Image:

        id: imageFrame
        
        source: "images/noSignal.jpg"
#!/usr/bin/env python3.9
# adding sub-modules
import sys
sys.path.append('../')

# auxillary libraries
import cv2
import socket
import threading 
import subprocess
import numpy as np 
from enum import Enum
from datetime import datetime

# kivy libraries
from kivymd.app import MDApp
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
from kivy.uix.screenmanager import ScreenManager, Screen 

# module libraries 
#from Networking.GCSPublisher import GCSPublisher

# Window Enum class
class windows(Enum):
    loginWindow = 0
    mainWindow = 1

# login window class
class LoginWindow(Screen):
    pass

# main window class
class MainWindow(Screen):
    pass

# window manager
class WindowManager(ScreenManager): 
    pass


# class - UAVApp
class UAVApp(MDApp):

    # current frame
    currentFrame = windows.loginWindow.name

    # builds the application
    def build(self):
        
        # layout options
        self.theme_cls.theme_style = "Dark"
        self.theme_cls.primary_palette = "BlueGray"

        # creating capture for imagery
        self.capture = cv2.VideoCapture(0)

        # scheduling image clock
        Clock.schedule_interval(self.frame_capture, 1/30.0)

        # scheduling time clock
        Clock.schedule_interval(self.time_function, 1)  

        return Builder.load_file('UAVApp.kv') 


    # a function to capture frames from the receiver
    def frame_capture(self, dt):
        
        if(self.currentFrame == windows.mainWindow.name):

            # get frame
            ret, frame = self.capture.read()

            if(ret):

                bufferFrame = cv2.flip(frame, 0)    
                bufferFrameStr = bufferFrame.tostring()

                imageTexture = Texture.create(size = (frame.shape[1], frame.shape[0]), colorfmt = 'bgr')
                imageTexture.blit_buffer(bufferFrameStr, colorfmt = 'bgr', bufferfmt = 'ubyte')

                self.root.screens[windows.mainWindow.value].ids['imageFrame'].texture = imageTexture # update texture

    # a function to update the time label
    def time_function(self, dt):

        if(self.currentFrame == windows.loginWindow.name):
            now = datetime.now()

            current_time = now.strftime("%H:%M:%S")

            #self.root.ids.timeLbl.text = current_time
            self.root.screens[windows.loginWindow.value].ids.timeLbl.text = current_time # todo: make this an enum 

    # ADC threading
    def controlThread(self, name):
        # opening connection to drone
        gcsPublisher = GCSPublisher(self.root.screens[windows.loginWindow.value].ids.ipAddress.text)

    # on ip text field validation
    def ip_validate(self, text):

        self.root.screens[windows.loginWindow.value].ids.spinnerIP.active = True

        validIP = self.checkIP(self.root.screens[windows.loginWindow.value].ids.ipAddress.text)

        if(not validIP):
            self.root.screens[windows.loginWindow.value].ids.spinnerIP.active = False
            self.root.screens[windows.loginWindow.value].ids.ipAddress.text = ""
            return
        self.root.screens[windows.loginWindow.value].ids.spinnerIP.active = False

        # Create thread for networking and control 
        #try:
        #   t1 = threading.Thread(target=self.controlThread, args=("",))
        #   t1.start()
        #except:
        #   print ("Error: unable to start thread")
        
        self.root.switch_to(self.root.screens[windows.mainWindow.value])
        self.currentFrame = windows.mainWindow.name

    # checking the ip address
    def checkIP(self, text):

        st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        try:
            st.connect((text, 1))
            IP = st.getsockname()[0]
            return True
        
        except:
            return False

# entry point 
if __name__ == '__main__':

    UAVApp().run()

CodePudding user response:

The problem is your use of the switch_to() method of ScreenManager. Not well documented, but this method removes the old Screen when it switches to the new Screen. So the number of Screens in the screens list gets reduced causing the index error.

A fix is to use the current property of ScreenManager rather than switch_to(). Try replacing:

self.root.switch_to(self.root.screens[windows.mainWindow.value])

with:

self.root.current = "Main"

And your texture update is happening on the main thread because you are calling the frame_capture() method by using Clock.schedule_interval(), which puts it on the main thread.

  • Related