Reputation: 38
I work on a kivy interface. I try to implement a popup but when I press the button that should fire the popup I get the error kivy.uix.popup.PopupException: Popup can have only one widget as content
(full console logs below).
The minimal reproducible example I prepared consists of 3 files; main.py, my.kv, and mygrid.py.
How can I achieve that pressing the button triggers the popup without altering the architecture of the modules?
Contains the structure of <GridLayout>
(green section) and <ScrollView>
(blue section)
<ScrollView>:
canvas:
Color:
rgb: 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
<GridLayout>:
canvas:
Color:
rgb: 0, 1, 0
Rectangle:
pos: self.pos
size: self.size
cols:1
size: root.width, root.height
Button:
width: root.width
text: "Submit"
on_press:root.SendButton()
size_hint: 1, None
Contains the logic regarding <GridLayout>
. In the example case, it only decides when to fire a popup
from kivy.uix.gridlayout import GridLayout
class MyGrid(GridLayout):
def SendButton(self):
from main import myApp
myApp().show_popup()
Run the application
Nest <GridLayout>
in <ScrollView>
Define the popup
import kivy
from kivy.app import App
from mygrid import MyGrid
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivy.uix.popup import Popup
from kivy.lang import Builder
Builder.load_string('''
<CustomPopup>:
size_hint: .5, .5
auto_dismiss: False
title: 'Hello world'
Button:
text: 'Click me to dismiss'
on_press: root.dismiss()
''')
class CustomPopup(Popup):
pass
class myApp(App):
def build(self):
layout = MyGrid(cols=1, padding=10, spacing=10, size_hint_y=None)
layout.bind(minimum_height=layout.setter('height'))
root = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
root.add_widget(layout)
return root
def show_popup(self):
p = CustomPopup()
p.open()
if __name__ == "__main__":
myApp().run()
PS C:\Users\EXO\Desktop\mre> & 'C:\Users\EXO\AppData\Local\Programs\Python\Python310\python.exe' 'c:\Users\EXO\.vscode\extensions\ms-python.python-2022.10.1\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '51462' '--' 'c:\Users\EXO\Desktop\mre\main.py'
[INFO ] [Logger ] Record log in C:\Users\EXO\.kivy\logs\kivy_22-07-29_14.txt
[INFO ] [deps ] Successfully imported "kivy_deps.angle" 0.3.2
[INFO ] [deps ] Successfully imported "kivy_deps.glew" 0.3.1
[INFO ] [deps ] Successfully imported "kivy_deps.sdl2" 0.4.5
[INFO ] [Kivy ] v2.1.0
[INFO ] [Kivy ] Installed at "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\_
_init__.py"
[INFO ] [Python ] v3.10.5 (tags/v3.10.5:f377153, Jun 6 2022, 16:14:13) [MSC v.1929 64 bit (AMD64)]
[INFO ] [Python ] Interpreter at "C:\Users\EXO\AppData\Local\Programs\Python\Python310\python.exe"
[INFO ] [Logger ] Purge log fired. Processing...
[INFO ] [Logger ] Purge finished!
[INFO ] [Factory ] 189 symbols loaded
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2 (img_pil, img_ffpyplayer ignored)
[INFO ] [Window ] Provider: sdl2
[INFO ] [Window ] Activate GLES2/ANGLE context
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] Backend used <angle_sdl2>
[INFO ] [GL ] OpenGL version <b'OpenGL ES 3.0.0 (ANGLE 2.1.17461 git hash: b790affce32e)'>
[INFO ] [GL ] OpenGL vendor <b'Google Inc. (Intel)'>
[INFO ] [GL ] OpenGL renderer <b'ANGLE (Intel, Intel(R) HD Graphics 3000 Direct3D11 vs_4_1 ps_4_1, D3D11-
9.17.10.4459)'>
[INFO ] [GL ] OpenGL parsed version: 3, 0
[INFO ] [GL ] Shading version <b'OpenGL ES GLSL ES 3.00 (ANGLE 2.1.17461 git hash: b790affce32e)'>
[INFO ] [GL ] Texture max size <8192>
[INFO ] [GL ] Texture max units <16>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [Text ] Provider: sdl2
[INFO ] [Base ] Start application main loop
[INFO ] [GL ] NPOT texture support is available
[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "c:\Users\EXO\Desktop\mre\main.py", line 36, in <module>
myApp().run()
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\app.py", line 955, in run
runTouchApp()
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\base.py", line 574, in runTouchApp
EventLoop.mainloop()
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\base.py", line 339, in mainloop
self.idle()
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\base.py", line 383, in idle
self.dispatch_input()
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\base.py", line 334, in dispatch_input
post_dispatch_input(*pop(0))
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\base.py", line 263, in post_dispatch_input
listener.dispatch('on_motion', etype, me)
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\core\window\__init__.py", line 1664, in on_motion
self.dispatch('on_touch_up', me)
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\core\window\__init__.py", line 1701, in on_touch_up
if w.dispatch('on_touch_up', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\scrollview.py", line 968, in on_touch_up
if self.dispatch('on_scroll_stop', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\scrollview.py", line 1005, in on_scroll_stop
self.simulate_touch_down(touch)
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\scrollview.py", line 668, in simulate_touch_down
ret = super(ScrollView, self).on_touch_down(touch)
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\widget.py", line 589, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\widget.py", line 589, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
self.dispatch('on_press')
File "kivy\_event.pyx", line 727, in kivy._event.EventDispatcher.dispatch
File "kivy\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1191, in kivy._event.EventObservers._dispatch
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\lang\builder.py", line 55, in custom_callback
exec(__kvlang__.co_value, idmap)
File "c:\Users\EXO\Desktop\mre\my.kv", line 23, in <module>
on_press:root.SendButton()
File "c:\Users\EXO\Desktop\mre\mygrid.py", line 6, in SendButton
myApp().show_popup()
File "c:\Users\EXO\Desktop\mre\main.py", line 32, in show_popup
p = CustomPopup()
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\modalview.py", line 195, in __init__
super(ModalView, self).__init__(**kwargs)
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\anchorlayout.py", line 68, in __init__
super(AnchorLayout, self).__init__(**kwargs)
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\layout.py", line 76, in __init__
super(Layout, self).__init__(**kwargs)
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\widget.py", line 366, in __init__
self.apply_class_lang_rules(
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\widget.py", line 470, in apply_class_lang_rules
Builder.apply(
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\lang\builder.py", line 540, in apply
self._apply_rule(
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\lang\builder.py", line 659, in _apply_rule
widget.add_widget(child)
File "C:\Users\EXO\AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\uix\popup.py", line 216, in add_widget
raise PopupException(
kivy.uix.popup.PopupException: Popup can have only one widget as content
PS C:\Users\EXO\Desktop\mre>
Upvotes: 0
Views: 215
Reputation: 38962
The problem is your code:
def SendButton(self):
from main import myApp
myApp().show_popup()
The import
here causes the Builder.load_string()
in main.py
to be executed a second time (the first was when you ran the App
). And that second execution causes the <CustomPopup>
rule to be loaded twice which results in the CustomPopup
rule trying to put two Buttons
in the Popup
, and trigger the error message.
Also, this is creating another instance of myApp
(myApp()
always creates a new instance). And then calls the show_popup()
method of that new instance. A better approach is to not do an import in the SendButton()
method, and just call the show_popup()
method of the running App
. Like this:
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
class MyGrid(GridLayout):
def SendButton(self):
App.get_running_app().show_popup()
This code gets the currently running App
and calls its show_popup()
method without importing anything from main.py
.
Upvotes: 1