Киви не отвечает и не может изменить текст в текстовом поле

Так что это часть моего кода, и теоретически он должен работать хорошо, потому что я не мог воспроизвести проблему где-либо еще.

Проблема в том, что, хотя print(string) работает очень хорошо и печатает по порядку, когда я пытаюсь создать интерфейс kivy и вместо этого распечатывать результаты на textbox, интерфейс работает какое-то время, затем все тормозит, а затем Window сообщает мне, что программа не отвечает. Но если я посмотрю на print результаты, они работают нормально.

Также, когда я жду завершения программы, когда она завершается, она выводит весь текст в одну большую кучу, а затем завершает свою работу.

Итак, есть ли способ заставить textbox распечатать результаты в том же порядке, в каком будет работать обычный print?

ex)

обычная печать: print('hello')

печать текстового поля: self.work.text += '\nhello'

P.S. Если вам нужно знать функции, которые используются в этом коде, спросите

Код Python

import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.uix.togglebutton import ToggleButton
Window.clearcolor = (1, 1, 1, 1)
from kivy.uix.popup import Popup
from datetime import date
import requests
from bs4 import BeautifulSoup
import sys
import urllib.request
import urllib.parse
import re
import os
import time
from pytube import YouTube
from pydub import AudioSegment
AudioSegment.converter = "/FFmpeg/bin/ffmpeg.exe"
import eyed3

############################### functions #########################################

just some functions that work

################################################################################################################
class MyGrid(Widget):
    # url = ObjectProperty(None)
    # mod = ObjectProperty(None)
    # submit = ObjectProperty(None)

    def submit(self):

        self.work.text='starting...'

        if self.mod.state=='down':
            info=get_name_list(self.url.text,'single')
            print(info)
            self.high_audio(info)
        
        if self.mod.state=='normal':
            info=get_name_list(self.url.text,'playlist')
            print(info)
            self.high_audio(info)
        
        

    def high_audio(self,song_album_artist_list):

        for item in song_album_artist_list:
            song = str(item[0])
            artist_name = str(item[1])
            album = str(item[2])
            
            print(song+" : "+artist_name+" : "+album)
            self.work.text += song+" : "+artist_name+" : "+album
            print('............................')

            youtube_url=get_top_url(song,artist_name)

            # print(youtube_url)

            download_high_audio(youtube_url,song)

            convert(youtube_url,song)


            self.work.text += 'converting finished, applying metadata...'

            song_file = eyed3.load(output_path+"/"+song+".mp3")
            song_file.tag.artist = artist_name
            song_file.tag.album = album

            self.work.text += 'searching music...'

            art=get_music_inf(song,artist_name,album)

            if art=='':
                art=get_album_art_high(song,artist_name,album)

            if art=='':
                art=get_album_art_low(song,artist_name,album)
            
            if art=='':
                self.work.text += '****************************No artwork availiable****************************'
            
            self.work.text+=art

            try:
                response = requests.get(art)
                imagedata = response.content
                song_file.tag.images.set(3,imagedata,"image/png",u"None")
                song_file.tag.save()
            except requests.exceptions.MissingSchema:
                pass


            
            song_file.tag.save()

            self.work.text+='done'




class MyApp(App):
    def build(self):
        return MyGrid()



if __name__ == "__main__":
    MyApp().run()

Код Киви


<RoundButton@Button>:
    background_color: 0,0,0,0
    canvas.before:
        Color:
            rgba: (.7,.7,.7,0.7) if self.state=='normal' else (0.82,0.96,0.92,1)
        RoundedRectangle:
            pos: self.pos
            size: self.size
            radius: [10,]

<RoundToggleButton@ToggleButton>:
    background_color: 0,0,0,0
    canvas.before:
        Color:
            rgba: (.7,.7,.7,0.7) if self.state=='normal' else (0.82,0.96,0.92,1)
        RoundedRectangle:
            pos: self.pos
            size: self.size
            radius: [10,]

<MyTextInput@TextInput>:
    background_normal: "textinput.png"
    background_color: (0.82,0.96,0.92,1) if self.focus else (1,1,1,0.5)

<MyGrid>:

    url:url
    mod:mod
    work:work

    FloatLayout:

        size: root.width, root.height

        
        MyTextInput:
            id: url
            pos_hint: {"x": 0.25, "top":0.85}
            size_hint: 0.5,0.1
            text:"Paste url"

        RoundButton:
            text: "submit"
            on_press: root.submit()
            pos_hint: {"x":0.4, "top":0.7}
            size_hint: 0.2,0.1

        RoundToggleButton:
            id: mod
            text: 'Playlist' if self.state=='normal' else 'Single'
            pos_hint: {"x":0.4, "top":0.45}
            size_hint: 0.2,0.1
        
        MyTextInput:
            id:work
            text:''
            pos_hint: {"x": 0.25,"top":0.3}
            size_hint: 0.5,0.25

Подскажите, пожалуйста, в чем проблема? Я потратил почти день, пытаясь понять это :(


person Andy_ye    schedule 30.04.2020    source источник
comment
Основной поток в kivy - это цикл событий, который реагирует на события и обновляет графический интерфейс каждый кадр. Button press - одно из таких событий, поэтому ваши методы submit() и high_audio() запускаются в основном потоке. Если эти методы не возвращаются быстро, то графический интерфейс не может быть обновлен (потому что цикл зависает при выполнении вашего кода). Лучшим подходом может быть запуск метода submit() в другом потоке и использование Clock.schedule_once() для обновления MyTextInput.   -  person John Anderson    schedule 30.04.2020
comment
что, если я запускаю метод submit в основном потоке и создаю новый поток каждый раз, когда хочу напечатать что-то, что добавляет текст в текстовое поле? это сработает?   -  person Andy_ye    schedule 01.05.2020
comment
Нет, изменения в TextInput должны выполняться в основном потоке, и все, что связывает основной поток для другой обработки, может привести к тому, что ваш графический интерфейс перестанет отвечать.   -  person John Anderson    schedule 01.05.2020
comment
догадаться! спасибо большое!   -  person Andy_ye    schedule 01.05.2020


Ответы (1)


Когда вы запускаете print(), на самом деле происходит то, что текст выводится программой в виде потока байтов, а затем какая-то другая программа (например, ваш терминал) может проверить этот поток и отобразить его как символы. Неважно, что еще делает программа Python, потому что этот поток байтов является внешним по отношению к остальной части ее потока управления.

Когда вы рисуете текст с помощью графического интерфейса пользователя, модель отличается - установка текста TextInput сообщает ему, что делать, но фактическое отображение текста также является частью потока программы.

Когда вы запускаете много кода одновременно, вы блокируете этот поток, поэтому код Kivy для обновления графического интерфейса не запускается между запуском вашей функции и завершением всего кода, поэтому функция возвращается.

Решение - не блокировать графический интерфейс. Либо разделите вашу функцию на фрагменты, каждый из которых вызывает следующий с помощью Clock.schedule_once, либо запустите длительный вызов функции в потоке.

person inclement    schedule 30.04.2020
comment
Как мне разделить свои функции? он должен быть похож на def function(): "do something then print into text box" Clock.schedule_once(self.nextfunction()), а затем def nextfunction(): "do something then print into text box" Clock.schedule_once(self.nextnextfunction()) и так далее? - person Andy_ye; 01.05.2020
comment
Или мне попробовать напечатать текст в текстовом поле после Clock.schedule()? - person Andy_ye; 01.05.2020