Создание диалогового окна ввода с помощью Kivy

Я хотел бы создать функцию, которая возвращает свое значение только после того, как пользователь вводит текст в TextInput и нажимает кнопку «ОК». Например

n = PopupInput("What number should I add?")
print 5+n

Я не могу понять, как написать kivy-диалог, который будет приостанавливать выполнение и ждать, пока пользователь его не закроет. В других инструментах GUI я бы использовал что-то вроде

while True:
   if dialog.visable == False:
      return int(textbox.text)
   else:
       wx.Yield()

Чтобы позволить моему коду просто сидеть на одном месте, позволяя графическому интерфейсу делать свое дело. Однако я не могу найти эквивалентного метода для Kivy.

РЕДАКТИРОВАТЬ:

Вот моя неудачная попытка (это грязно)

def PopupOk(text, title='', btn_text='Continue'):
    btnclose = Button(text=btn_text, size_hint_y=None, height='50sp')
    content = BoxLayout(orientation='vertical')
    p = Popup(title=title, content=content, size=('300dp', '300dp'),
                size_hint=(None, None))
    content.add_widget(Label(text=text))
    ti = TextInput(height='50sp', font_size='50sp', input_type='number')
    content.add_widget(ti)
    def _on_d(*args):
        p.is_visable = False
    p.bind(on_dismiss=_on_d)
    p.is_visable = True

    content.add_widget(btnclose)

    btnclose.bind(on_release=p.dismiss)
    p.open()
    while not p.is_visable:
        EventLoop.idle()
   return ti.text

person jsexauer    schedule 19.02.2014    source источник


Ответы (3)


Я бы подумал об этом с другой стороны - что вы действительно хотите сделать, это напечатать число, когда всплывающее окно закрыто.

Предполагая, что у вас есть всплывающее окно с текстовым вводом для ввода пользователем, вы можете сделать popup.bind(on_dismiss=some_function), чтобы запустить это some_function, когда всплывающее окно закрыто. Это означает, что все, что вам нужно сделать, это написать функцию, которая принимает всплывающее окно, извлекает текст текстового поля и печатает любой ответ, который вы хотите.

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

Редактировать: Видя ваше редактирование, это почти то, что вы делаете, но я думаю, что это плохая идея - пытаться таким образом обыграть цикл событий, а не плыть по течению. Я бы создал новую функцию (как я сказал выше), которая принимает текстовый ввод и делает все, что вы действительно хотите. Привязывая on_dismiss к этой функции, вы позволяете kivy позаботиться о том, чтобы запустить ваши вычисления позже, когда пользователь закроет всплывающее окно.

person inclement    schedule 19.02.2014
comment
Спасибо @inclement. Я читал несколько примеров, которые следуют этой парадигме, но это не очень хорошо для меня, потому что происходит следующее: пользователь нажимает кнопку, которая вызывает функцию, и выполняются некоторые вычисления, которые затем вызывают подсказку, которая затем вернуться туда, где я остановился в этой функции. Я бы предпочел не разбивать эту функцию на две части для удобства чтения. - person jsexauer; 20.02.2014
comment
Я думал об этом, но у меня действительно нет лучшей идеи, чем то, что я предложил, это кажется мне очень естественным. Честно говоря, я не уверен, что эта парадигма неверна для вас, но это ваш выбор, и я, конечно, не могу претендовать на авторитет в этом вопросе. - person inclement; 20.02.2014
comment
Просто мне кажется таким странным, что это невозможно. В других фреймворках с графическим интерфейсом довольно стандартно иметь простые диалоги и вызывать функцию, чтобы получить ответ пользователя на приглашение. Просто удивлен, что в Киви нет чистого способа сделать это. Спасибо за помощь, как всегда. (Кстати, спасибо за то, что ответили на так много вопросов Киви о SO; я нашел их бесценными) - person jsexauer; 20.02.2014

Kivy действительно построен на принципе событий и асинхронных обратных вызовов. Поскольку он использует OpenGL и полагается на кадры, визуализируемые на графическом процессоре, а не на центральном процессоре, вы никогда не захотите использовать блокирующий код. Таким образом, kivy использует привязку событий, чтобы обойти эту проблему.

Вот один подход.

from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput


class MainApp(App):
    def build(self):
        self.button = Button(text="Click",
                             on_release=self.get_caption)
        return self.button

    def get_caption(self, btn):
        Popup(title="Enter text here",
              content=TextInput(focus=True),
              size_hint=(0.6, 0.6),
              on_dismiss=self.set_caption).open()

    def set_caption(self, popup):
        self.button.text = popup.content.text

MainApp().run()

Вы размещаете контент во всплывающем окне, когда даете ему функцию «set_caption» для вызова при его закрытии. Там вы отвечаете на изменение. Нет блокировки. Нет ожидания. Поработав с потоками, чтобы остановить блокировку графического интерфейса в wxWidgets, я действительно думаю, что это лучший способ... ;-)

Ваше здоровье

person ZenCODE    schedule 20.02.2014

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

import kivy
kivy.require('1.5.0') # replace with your current kivy version !
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.app import App
from kivy.clock import Clock

class YourApp(App):

    def build(self):
        return Button(text='Press for popup!', on_press=self.callpopup)

    def callpopup(self, event):
        dlg = MessageBox(titleheader="Titel Header", message="Any Message", options={"YES": "printyes()", "NO": "printno()"})
        print "Messagebox shows as kivy popup and we wait for the \nuser action and callback to go to either routine"

    def printyes(self):
        # routine for going yes
        print "You chose the Yes routine"

    def printno(self):
        # routine for going no
        print "You chose the No routine"

class MessageBox(YourApp):
    def __init__(self, titleheader="Title", message="Message", options={"OK": "self.ok()", "NO": "self.no()"}):

        def popup_callback(instance):
            "callback for button press"
            # print('Popup returns:', instance.text)
            self.retvalue = instance.text
            self.popup.dismiss()

        self.retvalue = None
        self.options = options
        box = BoxLayout(orientation='vertical')
        box.add_widget(Label(text=message, font_size=20))
        b_list =  []
        buttonbox = BoxLayout(orientation='horizontal')
        for b in options:
            b_list.append(Button(text=b, size_hint=(1,.35), font_size=20))
            b_list[-1].bind(on_press=popup_callback)
            buttonbox.add_widget(b_list[-1])
        box.add_widget(buttonbox)
        self.popup = Popup(title=titleheader, content=box, size_hint=(None, None), size=(400, 400))
        self.popup.open()
        self.popup.bind(on_dismiss=self.OnClose)

    def OnClose(self, event):
        self.popup.unbind(on_dismiss=self.OnClose)
        self.popup.dismiss()
        if self.retvalue != None:
            command = "super(MessageBox, self)."+self.options[self.retvalue]
            # print "command", command
            exec command

if __name__ == '__main__':
    YourApp().run()
person bunkus    schedule 02.10.2014