python - 使用 kivy 窗口同时运行循环

标签 python kivy python-multiprocessing python-multithreading kivymd

我正在尝试构建一个简单的消息应用程序,它使用rabbitmq作为其消息代理,并使用kivy作为其UI。因此,为了接收传入消息,我有一个循环的接收函数,但是当我尝试多处理该应用程序并运行它时,kivy 似乎正在打开多个窗口。请告诉我如何解决这个问题。

.py 文件

import pika
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.card import MDCard
from database import Database as D
from kivy.core.window import Window
from multiprocessing import Process
from kivymd.uix.label import MDLabel
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDRectangleFlatButton
from kivy.uix.screenmanager import ScreenManager,Screen

Window.size = (400,700)

global logged_in_user
logged_in_user = ""

def receive():
    CREDENTIALS = pika.PlainCredentials('redbarker', 'Redbarker@20-21')
    PARAMETERS = pika.ConnectionParameters(credentials=CREDENTIALS)
    connection = pika.BlockingConnection(PARAMETERS)
    channel = connection.channel()
    channel.exchange_declare(exchange='system_exchange', exchange_type='topic', durable=True)
    result = channel.queue_declare(queue='', exclusive=True)
    queue_name = result.method.queue
    channel.queue_bind(exchange='system_exchange', queue=queue_name, routing_key="command.#")

    def callback(ch, method, properties, body):
        print(body)
        
    channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True)
    channel.start_consuming()

class LoginScreen(Screen):

    def validate_user(self):
        uname = self.ids.uname_input.text
        pword = self.ids.pword_input.text

        is_found = D().login(uname,pword)
        self.ids.uname_input.text = ""
        self.ids.pword_input.text = ""
        if is_found:
            logged_in_user = uname
            self.parent.current = "users_screen"

class SignupScreen(Screen):

    def signin(self):
        uname = self.ids.uname_input.text
        pword1 = self.ids.pword_input1.text
        pword2 = self.ids.pword_input2.text
        D().signup(uname,pword1,pword2)
        self.parent.current = "login_screen"

class UsersScreen(Screen):

    def to_inbox(self,text):
        InboxScreen().change_header(text)
        self.parent.current = 'inbox_screen'

    def add_user(self,name):
        container = self.ids.user_field
        button = MDRectangleFlatButton(
            text = name,
            font_style = "H6",
            text_color = (.95,.44,.49,1),
            line_color = (.95,.44,.49,1),
            pos_hint = {"center_x": .5, "center_y": .5},
            on_press = self.to_inbox(self.text),
        )
        container.add_widget(button)
        
class CreateGroupScreen(Screen):

    def add_group(self):
        container = self.ids.user_field
        name = self.ids.username.text
        if len(name) > 0:
            card = MDCard(
                size_hint_y = None,
                height = 50,
                line_color = (.95,.44,.49,1),
                radius = 10,
                padding = 10
            )
            label = MDLabel(
                halign = "left",
                text = name,
                theme_text_color = "Custom",
                text_color = (.95,.44,.49,1)
            )
            card.add_widget(label)
            container.add_widget(card)
            self.ids.username.text = ""

    def remove_all_users(self):
        for child in [child for child in self.ids.user_field.children]:
            self.ids.user_field.remove_widget(child)

class AddUserScreen(Screen):

    users = []

    def add_user(self):
        for name in self.users:
            pass

    def append_user(self):
        name = self.ids.username.text
        if len(name) > 0:

            container = self.ids.user_field
            card = MDCard(
                size_hint_y = None,
                height = 50,
                line_color = (.95,.44,.49,1),
                radius = 10,
                padding = 10
            )
            label = MDLabel(
                halign = "left",
                text = name,
                theme_text_color = "Custom",
                text_color = (.95,.44,.49,1)
            )
            card.add_widget(label)
            container.add_widget(card)
            self.users.append(name)
            self.ids.username.text = ""

    def remove_all_users(self):
        for child in [child for child in self.ids.user_field.children]:
            self.ids.user_field.remove_widget(child)

class InboxScreen(Screen):

    def change_header(self, text):
        self.ids.header.text = text

    def build_widget(self,message):
        container = self.ids.text_field
        layout = MDBoxLayout(
            size_hint_y = None,
            height = 50,
            padding = (10,0),
        )

        card = MDCard(
            radius = (0,10,0,10),
            padding = 10
        )

        place_holder = MDBoxLayout()

        label = MDLabel(
            halign = "right",
            text = message,
            font_style = "H6",
            theme_text_color = "Custom",
            text_color = (.95,.44,.49,1)
        )
        card.add_widget(label)
        layout.add_widget(place_holder)
        layout.add_widget(card)
        container.add_widget(layout)
    
    def send(self):
        message = self.ids.message_input.text
        if len(message) > 0:
            CREDENTIALS = pika.PlainCredentials('redbarker', 'Redbarker@20-21')
            PARAMETERS = pika.ConnectionParameters(credentials=CREDENTIALS)

            consumer = self.ids.header.text
            
            connection = pika.BlockingConnection(PARAMETERS)
            channel = connection.channel()
            channel.exchange_declare(exchange = 'system_exchange', exchange_type='topic', durable= True)
            channel.basic_publish(exchange='system_exchange', routing_key=f"Temesgen.{consumer}", body=message)

            self.build_widget(message)

class WindowManager(ScreenManager):
    pass
    
class ChatApp(MDApp):

    def build(self):
        return Builder.load_file('test.kv')
    
#if __name__ == "__main__":
#    ChatApp().run()

if __name__ == '__main__':
    ChatApp().run()
    p1 = Process(target=receive)
    p1.start()

.kv 文件

WindowManager:

    LoginScreen:

    SignupScreen:

    AddUserScreen:

    UsersScreen:

    CreateGroupScreen:

    InboxScreen:

<LoginScreen>:
    name: "login_screen"

    MDBoxLayout:
        orientation: "vertical"
        padding: 0,1
    
        MDCard:
            size_hint_y: .1
            line_color: (.95,.44,.49,1)
            spacing: 20
            padding: 20,10

            Widget:
                size_hint_y: .33

            MDLabel:
                halign: "center"
                text: "Login"
                font_style: "H4"
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)

            Widget:
                size_hint_y: .33

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .4
            spacing: 20
            
            MDTextField:
                id: uname_input
                pos_hint: {"center_x": .5}
                size_hint_x: None
                width: 250
                hint_text: 'Username'
                mode: 'rectangle'
                color: (0,1,1,1)
                line_color_normal: (0,1,0,1)
                text_color: (.95,.44,.49,1)

            MDTextField:
                id: pword_input
                pos_hint: {"center_x": .5}
                size_hint_x: None
                width: 250
                hint_text: 'Password'
                mode: 'rectangle'
                color: (0,1,1,1)
                line_color_normal: (0,1,0,1)
                text_color: (.95,.44,.49,1)

            MDTextButton:
                text: 'Sign Up'
                underline: True
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)
                pos_hint: {'center_x': 0.5, 'center_y': 0.5}
                on_press: app.root.current = "signup_screen"

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .5

            Widget:
                size_hint_y: .33

            MDRectangleFlatButton:
                text: "Login"
                font_size: 20
                text_color: (.95,.44,.49,1)
                line_color: (.95,.44,.49,1)
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: root.validate_user()

            Widget:
                size_hint_y: .6

<SignupScreen>:
    name: "signup_screen"

    MDBoxLayout:
        orientation: "vertical"
        padding: 0,1
    
        MDCard:
            size_hint_y: .1
            line_color: (.95,.44,.49,1)
            spacing: 20
            padding: 20,10

            MDBoxLayout:
                size_hint_x: .25

                MDIconButton:
                    size_hint_x: .25
                    icon: "keyboard-backspace"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: app.root.current = "login_screen"

            MDLabel:
                size_hint_x: .5
                halign: "center"
                text: "Signin"
                font_style: "H4"
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)

            MDBoxLayout:
                size_hint_x: .25

                Widget:
                    size_hint_y: .25
                    

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .4
            spacing: 20
            
            MDTextField:
                id: uname_input
                pos_hint: {"center_x": .5}
                size_hint_x: None
                width: 250
                hint_text: 'Username'
                mode: 'rectangle'
                color: (0,1,1,1)
                line_color_normal: (0,1,0,1)
                text_color: (.95,.44,.49,1)

            MDTextField:
                id: pword_input1
                pos_hint: {"center_x": .5}
                size_hint_x: None
                width: 250
                hint_text: 'Password'
                mode: 'rectangle'
                color: (0,1,1,1)
                line_color_normal: (0,1,0,1)
                text_color: (.95,.44,.49,1)

            MDTextField:
                id: pword_input2
                pos_hint: {"center_x": .5}
                size_hint_x: None
                width: 250
                hint_text: 'Confirm Password'
                mode: 'rectangle'
                color: (0,1,1,1)
                line_color_normal: (0,1,0,1)
                text_color: (.95,.44,.49,1)

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .5

            Widget:
                size_hint_y: .33

            MDRectangleFlatButton:
                text: "Signin"
                font_size: 20
                text_color: (.95,.44,.49,1)
                line_color: (.95,.44,.49,1)
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: root.signin()

            Widget:
                size_hint_y: .6

<UsersScreen>:
    name: "users_screen"

    MDBoxLayout:
        orientation: "vertical"
        padding: 0,1

        MDCard:
            size_hint_y: .1
            line_color: (.95,.44,.49,1)
            spacing: 20
            padding: 20,10

            MDIconButton:
                size_hint_x: .3
                icon: "logout"
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: app.root.current = "login_screen"

            MDLabel:
                halign: "center"
                text: "Users"
                font_style: "H4"
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)

            MDBoxLayout:
                size_hint_x: .4

                MDIconButton:
                    icon: "account-plus"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: app.root.current = "add_user_screen"

                MDIconButton:
                    icon: "account-multiple-plus"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: app.root.current = "create_group_screen"

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .9

            ScrollView:
                size: self.size
                pos: self.pos
                
                MDList:
                    id: user_field
                    spacing: 10
                    padding: 10

                    MDRectangleFlatButton:
                        text: 'User-1                                                           '
                        font_style: "H6"
                        text_color: (.95,.44,.49,1)
                        line_color: (.95,.44,.49,1)
                        pos_hint: {"center_x": .5, "center_y": .5}
                        on_press: root.to_inbox(self.text)

                    MDRectangleFlatButton:
                        text: 'User-2                                                           '
                        font_style: "H6"
                        text_color: (.95,.44,.49,1)
                        line_color: (.95,.44,.49,1)
                        pos_hint: {"center_x": .5, "center_y": .5}
                        on_press: root.to_inbox(self.text)

                    MDRectangleFlatButton:
                        text: 'User-3                                                           '
                        font_style: "H6"
                        text_color: (.95,.44,.49,1)
                        line_color: (.95,.44,.49,1)
                        pos_hint: {"center_x": .5, "center_y": .5}
                        on_press: root.to_inbox(self.text)

<CreateGroupScreen>:
    name: "create_group_screen"

    MDBoxLayout:
        orientation: "vertical"
        padding: 0,1

        MDCard:
            size_hint_y: .1
            line_color: (.95,.44,.49,1)
            spacing: 20
            padding: 20,10

            MDIconButton:
                id: remove_button
                size_hint_x: .2
                icon: "keyboard-backspace"
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: app.root.current = "users_screen"

            MDLabel:
                halign: "center"
                text: "Create Group"
                font_style: "H4"
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .9
            padding: 20
            spacing: 40

            MDCard:
                size_hint_y: .1
                line_color: (.95,.44,.49,1)
                radius: 10
                padding: 25,3

                MDTextField:
                    id: username
                    pos_hint: {"center_x": .5, "center_y": .4}
                    size_hint_x: None
                    width: 250
                    hint_text: 'Username'

                MDIconButton:
                    id: add_button
                    size_hint_x: .2
                    icon: "account-multiple-plus"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: root.add_user()

                MDIconButton:
                    id: remove_button
                    size_hint_x: .2
                    icon: "account-multiple-remove"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: root.remove_all_users()

            MDCard:
                orientation: "vertical"
                size_hint_y: .6
                line_color: (.95,.44,.49,1)
                radius: 10

                MDLabel:
                    size_hint_y: .1
                    halign: "center"
                    text: "Add Users"
                    theme_text_color: "Custom"
                    text_color: (.95,.44,.49,1)

                MDBoxLayout:
                    size_hint_y: .9

                    ScrollView:
                        size: self.size
                        pos: self.pos
                    
                        MDList:
                            id: user_field
                            spacing: 10
                            padding: 10

            MDBoxLayout:
                orientation: "vertical"
                size_hint_y: .3

                Widget:
                    size_hint_y: .33

                MDRectangleFlatButton:
                    text: "Create Group"
                    text_color: (.95,.44,.49,1)
                    line_color: (.95,.44,.49,1)
                    pos_hint: {"center_x": .5, "center_y": .5}

                Widget:
                    size_hint_y: .33

<AddUserScreen>:
    name: "add_user_screen"

    MDBoxLayout:
        orientation: "vertical"
        padding: 0,1

        MDCard:
            size_hint_y: .1
            line_color: (.95,.44,.49,1)
            spacing: 20
            padding: 20,10

            MDIconButton:
                size_hint_x: .33
                icon: "keyboard-backspace"
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: app.root.current = "users_screen"

            MDLabel:
                halign: "center"
                text: "Add Users"
                font_style: "H4"
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)

            Widget:
                size_hint_x: .33

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .9
            padding: 20
            spacing: 40

            MDCard:
                size_hint_y: .1
                line_color: (.95,.44,.49,1)
                radius: 10
                padding: 25,3

                MDTextField:
                    id: username
                    pos_hint: {"center_x": .5, "center_y": .4}
                    size_hint_x: None
                    width: 250
                    hint_text: 'Username'

                MDIconButton:
                    id: add_button
                    size_hint_x: .2
                    icon: "account-multiple-plus"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: root.append_user()

                MDIconButton:
                    id: remove_button
                    size_hint_x: .2
                    icon: "account-multiple-remove"
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: root.remove_all_users()

            MDCard:
                orientation: "vertical"
                size_hint_y: .6
                line_color: (.95,.44,.49,1)
                radius: 10

                MDLabel:
                    size_hint_y: .1
                    halign: "center"
                    text: "Add Users"
                    theme_text_color: "Custom"
                    text_color: (.95,.44,.49,1)

                MDBoxLayout:
                    size_hint_y: .9

                    ScrollView:
                        size: self.size
                        pos: self.pos
                    
                        MDList:
                            id: user_field
                            spacing: 10
                            padding: 10

            MDBoxLayout:
                orientation: "vertical"
                size_hint_y: .3

                Widget:
                    size_hint_y: .33

                MDRectangleFlatButton:
                    text: "Add Users"
                    text_color: (.95,.44,.49,1)
                    line_color: (.95,.44,.49,1)
                    pos_hint: {"center_x": .5, "center_y": .5}
                    on_press: root.add_user()

                Widget:
                    size_hint_y: .33

<InboxScreen>:
    name: "inbox_screen"

    MDBoxLayout:
        orientation: "vertical"
        padding: 0,1

        MDCard:
            size_hint_y: .1
            line_color: (.95,.44,.49,1)
            spacing: 20
            padding: 20,10

            MDIconButton:
                size_hint_x: .2
                icon: "keyboard-backspace"
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: app.root.current = "users_screen"

            MDLabel:
                id: header
                halign: "center"
                text: ""
                font_style: "H4"
                theme_text_color: "Custom"
                text_color: (.95,.44,.49,1)

        MDBoxLayout:
            orientation: "vertical"
            size_hint_y: .9

            ScrollView:
                size: self.size
                pos: self.pos
                MDList:
                    id: text_field
                    spacing: 10

        MDCard:
            size_hint_y: .075
            line_color: (.95,.44,.49,1)
            padding: 10,0

            MDTextField:
                id: message_input
                size_hint: None,None
                height: 100
                width: 325
                hint_text: 'Write'
                color: (0,1,1,1)
                line_color_normal: (.95,.44,.49,1)
                text_color: (.95,.44,.49,1)
                pos_hint: {"center_x": .5, "center_y": .3}

            MDIconButton:
                id: send_button
                icon: "send"
                pos_hint: {"center_x": .5, "center_y": .5}
                on_press: root.send()

最佳答案

您假装做的是同时运行 kivy。 kivy 中异步事件的最佳选择是使用 asyncio (如官方文档建议的那样)。首先,您必须确保您的应用程序从 asyncio.run() 而不是 App.run() 运行。为此,您必须导入 asyncio,并且还必须向您的 App 类添加一个方法。请参阅下面的示例:

import asyncio 

######## MAIN APP ######## 
class ExampleApp(App):
    def build(self):
        #Your  app stuff here

    async def kivyCoro(self):  #This is the method that's gonna launch your kivy app
        await self.async_run(async_lib='asyncio')
        print('Kivy async app finished...')

    # This func will start all the "tasks", in this case the only task is the kivy app
    async def base(self):
        (done, pending) = await asyncio.wait({self.kivyCoro()}, 
    return_when='FIRST_COMPLETED')

if __name__ == '__main__':
    instanceApp = ExampleApp() #You have to instanciate your App class
    asyncio.run(instanciaApp.base()) # Run in async mode

使用上面的代码,您将能够将tour kivy应用程序作为任务(同时)运行。

到目前为止,我们只是以异步模式(并发)运行 kivy 应用程序。在 Kivy 运行循环中添加其他任务:

import asyncio 

######## MAIN APP ######## 
class ExampleApp(App):
    def build(self):
        #Your  app stuff here

    async def kivyCoro(self):  #This is the method that's gonna launch your kivy app
        await self.async_run(async_lib='asyncio')
        print('Kivy async app finished...')

    async def task2InsideKivyLoop(self): #Here you declare the other task
        print('Another task running inside the kivy loop')
        await asyncio.sleep(1)


    # This func will start all the "tasks", in this case the only task is the kivy app
    async def base(self):
        (done, pending) = await asyncio.wait({self.kivyCoro(), task2InsideKivyLoop()}, 
    return_when='FIRST_COMPLETED')

if __name__ == '__main__':
    instanceApp = ExampleApp() #You have to instanciate your App class
    asyncio.run(instanciaApp.base()) # Run in async mode

如您所见,要在 KivyLoop 中添加另一个任务,我们只需将其声明为 App 的异步方法。类,然后将其添加到 asyncio.wait()

如果你想在 kivyLoop 之外运行其他任务,你必须执行以下操作:

import asyncio 

######## MAIN APP ######## 
class ExampleApp(App):
    def build(self):
        #Your  app stuff here

    async def kivyCoro(self):  #This is the method that's gonna launch your kivy app
        await self.async_run(async_lib='asyncio')
        print('Kivy async app finished...')

    # This func will start all the "tasks", in this case the only task is the kivy app
    async def base(self):
        (done, pending) = await asyncio.wait({self.kivyCoro()}, 
    return_when='FIRST_COMPLETED')


######### GLOBAL COROUTINE #######
# Here you can import functions from other python files or just declare a global func
async def GlobalTask():
    for i in range(10):
        print('Other concurrently global task... ',i)
        await asyncio.sleep(1)


if __name__ == '__main__':
        async def mainThread(): 
            instanceApp = ExampleApp() #Instanciate your App class
            a = asyncio.create_task(instanceApp.base()) #Run kivyApp as a task
            b = asyncio.create_task(GlobalTask()) #Run Global func as a task
            (done, pending) = await asyncio.wait({a}, return_when='FIRST_COMPLETED')
    asyncio.run(mainThread())

上面的示例可以让您在 kv 循环期间执行任何异步任务,无论是否在 kvLoop 内部

关于python - 使用 kivy 窗口同时运行循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70721388/

相关文章:

python - 虚拟环境中的kivy,窗口提供程序错误(linux)

python - 通过 pool.map_async 进行多处理对于大数据帧来说非常慢

用于字符串列表的 Python 2.7 多处理池?

python - 用户定义类: hash() and id() and doc

Python 天增量 : Remove time from days output in dataframe and convert column to float

python - .py 和 .kv 交互中的 Kivy 类

android - Eclipse、PyDev 和 Kivy

Python 多处理,从远程 RTSP 摄像头读取,无需缓冲区

python - 调用从方法返回的对象方法作为方法变量

python - 如何在右侧绘制 yticks 并保留经典的左侧