John
John

Reputation: 13

update label in kivy parser app during infinite loop

I am tying to add gui for my selenium parser app, chose Kivy, but it doesnt update label text. I found StringProperty solution and Threading to workaround an infinite loop, but yet unsuccessfully. I need such loop cause app should constantly rotate the site pages searching for username and saying what page was it found at. Please, check my code and correct me cause I'm def wrong :D I tried to make backround thread for the while loop and foreground for a whole kivy app. Here's what I got

import threading
import numpy as num

from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

pages = num.arange(1, 70, 1)
firefox_options = webdriver.FirefoxOptions()
firefox_options.set_preference("permissions.default.image", 2)

global browsertab

class MyApp(App):
    position = StringProperty('Scan website')
    def __init__(self):
        super().__init__()
        self.label = Label(text = self.position)
        self.input_data = TextInput(hint_text = 'Enter username to serach for', multiline = False)
        self.input_data.bind(on_text_validate = self.on_enter)
        self.btn = Button(text = 'Scan')
        self.btn.bind(on_press = self.btn_pressed)
    def on_enter(self, *args):
        self.btn_pressed(self)
    def btn_pressed(self, *args):
        name = self.input_data.text
        background = threading.Thread(target=self.scanwebsite(name))
        background.start()
    def showpos(self, name, page):
        self.position = 'User ' + name + ' is currently on page #' + str(page)
    def build(self):
        box = BoxLayout(padding=20, orientation = 'vertical')
        box.add_widget(self.label)
        box.add_widget(self.input_data)
        box.add_widget(self.btn)
        return box

    def scanwebsite(self, name):
        with webdriver.Firefox(options=firefox_options) as driver:
            driver.get("https://website.com/")
            WebDriverWait(driver, 3)
            browsertab = driver.current_window_handle
            try:
                if driver.find_element(By.LINK_TEXT, "I ACCEPT"):
                    driver.find_element(By.LINK_TEXT, "I ACCEPT").click()
            except:
                pass
            
            while True:
                for page in pages:
                    url = "https://website.com/?page=" + str(page) 
                    driver.switch_to.window(browsertab)
                    driver.get(url)
                    WebDriverWait(driver, 2)
                    try:
                        if driver.find_element(By.LINK_TEXT, name.lower()):
                            self.showpos(name, page)
                            break
                    except:
                        pass
                    else:
                        continue
if __name__ == "__main__":
    foreground = threading.Thread(target=MyApp().run())
    foreground.start()
exit()

Upvotes: 0

Views: 201

Answers (1)

John Anderson
John Anderson

Reputation: 38992

Some problems with your code:

  • You cannot run an app in another thread, it must run in the main thread
  • Setting the text of a Label to a StringProperty, in python code, just uses the value of the StringProperty at the time that code gets executed. No updates will happen without adding more code to do those updates.
  • The code background = threading.Thread(target=self.scanwebsite(name)) actually runs the scanwebsite() method in the current thread and then assigns its return (which is None) to the target argument.

If you use the kivy language to define your GUI, you can take advantage of automatic updates, like updating a Label that uses StringProperty. Here is a modified version of your code that uses this approach:

import threading
from functools import partial

import numpy as num

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

pages = num.arange(1, 70, 1)
firefox_options = webdriver.FirefoxOptions()
firefox_options.set_preference("permissions.default.image", 2)

global browsertab

kv = '''
BoxLayout:
    padding: 20
    orientation: 'vertical'
    Label:
        text: app.position
    TextInput:
        id: input
        hint_text: 'Enter username to serach for'
        multiline: False
    Button:
        text: 'Scan'
        on_press: app.btn_pressed()
'''


class MyApp(App):
    position = StringProperty('Scan website')

    def on_enter(self, *args):
        self.btn_pressed(self)

    def btn_pressed(self, *args):
        name = self.root.ids.input.text  # get name by using id set in kv
        
        # use partial to set args for the scanwebsite() method
        # set daemon=True to kill this thread when the App quits
        background = threading.Thread(target=partial(self.scanwebsite, name), daemon=True)
        background.start()

    def showpos(self, name, page):
        self.position = 'User ' + name + ' is currently on page #' + str(page)

    def build(self):
        return Builder.load_string(kv)

    def scanwebsite(self, name):
        with webdriver.Firefox(options=firefox_options) as driver:
            driver.get("https://website.com/")
            WebDriverWait(driver, 3)
            browsertab = driver.current_window_handle
            try:
                if driver.find_element(By.LINK_TEXT, "I ACCEPT"):
                    driver.find_element(By.LINK_TEXT, "I ACCEPT").click()
            except:
                pass

            while True:
                for page in pages:
                    url = "https://website.com/?page=" + str(page)
                    driver.switch_to.window(browsertab)
                    driver.get(url)
                    WebDriverWait(driver, 2)
                    try:
                        if driver.find_element(By.LINK_TEXT, name.lower()):
                            self.showpos(name, page)
                            break
                    except:
                        pass
                    else:
                        continue


if __name__ == "__main__":
    MyApp().run()  # do not use a thread here

Upvotes: 0

Related Questions