I'm trying to code a custom popup in kivy for picking a color from the ColorPicker widget. Right now i'm trying to hook up an 'OK' button to dismiss the popup, but it's not working. The popup up displays correctly, but when i click OK, nothing happens. The popup continues on screen.
Here's my python code.
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.colorpicker import ColorPicker
class PaintWindow(BoxLayout):
pass
class CPopup(Popup):
def on_press_dismiss(self, *args):
self.dismiss()
return False
class PixPaint(App):
def build(self):
pw = PaintWindow()
return pw
if __name__ == '__main__':
PixPaint().run()
Here's the KV code.
<PaintWindow>:
orientation: 'vertical'
CPopup:
<CPopup>:
title: 'Pick a Color'
size_hint: 1.0, 0.6
id: cpopup
BoxLayout:
orientation: 'vertical'
ColorPicker:
size_hint: 1.0, 1.0
Button:
text: 'OK'
size_hint: 1.0, 0.2
on_press: cpopup.on_press_dismiss()
Any help is much appreciated. Sorry for all the code! :)
The reason the popup is not closing is because you are adding it directly to your PaintWindow here:
<PaintWindow>:
orientation: 'vertical'
CPopup:
Instead of actually calling the method open() of Popup. So,
1 - Remove the CPopup: from the kv file
<PaintWindow>:
orientation: 'vertical'
#CPopup:
...
2 - Call the open() method somewhere else. As a good example in the build(self) of PixPaint
class PixPaint(App):
def build(self):
pw = PaintWindow()
popup = CPopup();
popup.open()
return pw
Just in case, a few extras:
You can also get rid off the id: cpopup on the the definition of <CPopup>: and use root instead (on_press: root.on_press_dismiss()).
Moreover, you can get rid off the on_press_dismiss() and do on_press: root.dismiss() instead.
Related
I am expecting the following kivy app to switch screens when I press and then release the button, but nothing happens and there's no error on the terminal. When I run the app, the GirisEkrani screen show up, and then when I press and release the button in GirisEkrani, the next screen (GirisEkrani2) should show up. Do you how to make that work?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<ekranApp>
GirisEkrani:
id: ge
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
Button:
id: ge2
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
class GirisEkrani(Screen):
pass
class GirisEkrani2(Screen):
pass
class KontrolEkrani(Screen):
pass
class ekranApp(App, ScreenManager):
def build(self):
#root = ScreenManager()
#root.add_widget(GirisEkrani(name = "giris_ekrani"))
#root.add_widget(GirisEkrani2(name = "giris_ekrani2"))
#root.add_widget(KontrolEkrani(name = "kontrol_ekrani"))
return self
if __name__ == "__main__":
ekranApp().run()
While people seem to advocate the use of .kv files as opposed to using pure Python, I find it very frustrating to have no errors showing up when something doesn't work.
The build() method of an App is expected to return a Widget which will be the root of the Widget tree for your App, but yours returns the App itself. I suspect that will cause problems. However, your code is not working because you are not setting the names of the Screens. Here is a corrected version of your kv:
Builder.load_string("""
<ekranApp>:
GirisEkrani:
id: ge
name: "giris_ekrani"
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
id: ge2
name: "giris_ekrani2"
Button:
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
name: "kontrol_ekrani"
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
Also, your id for GirisEkrani2 is actually set on the Button.
I am trying to use the popup widget in Kivy, but everytime I run the code, the popup just has 2 smaller versions of the widget on the main screen.
This is my Python code (the .py file):
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.widget import Widget
class Layout(Widget):
pass
class KivyTestApp(App):
def build(self):
return Layout()
app = KivyTestApp()
app.run()
and this is my Kivy code (the .kv file):
#: import Factory kivy.factory.Factory
<MyPopup#Popup>:
title: 'Test'
size_hint: None, None
size: 400, 400
<Layout>:
Button:
id: but
size: root.width, root.height
background_normal: ''
background_color: .5, .7, .9, 1
text: 'Press me to open the popup'
pos: 0, 0
on_press: Factory.MyPopup().open()
This creates a window that looks like this:
And the popup looks like this:
As you can see, I have added no content to the popup, yet Buttons still appear! If anyone could help me, that would be great, and thanks in advance!
Kivy already has an internal widget called Layout, which is subclassed all over the place. Try naming your own widget something else.
i have a simple test program:
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.clock import mainthread
class TestScreen(Screen):
#mainthread
def on_pre_enter(self): #Is loaded before kv buttons etc? how make it work
pass
#mainthread
def on_enter(self): #Load after kv buttons etc?
button = Button(text="Work?")
#how now add it to display?
#how control where display it? on end or begin is just about on_pre and on?
class TestApp(App):
pass
if __name__ == '__main__':
TestApp().run()
And test.kv file
#:import NoTransition kivy.uix.screenmanager.NoTransition
<TestScreen>:
name:'try'
GridLayout:
id:'test'
cols:2
Button:
text:'Test'
on_press:app.root.current='Main'
ScreenManager:
transition: NoTransition()
Screen:
name: 'Main'
GridLayout:
cols:1
Button:
text:'1'
Button:
text:'2'
Button:
text:'Test'
on_press:root.current='try'
TestScreen:
Is simple to control kv and python widgets(but i dont know how but is more easy to writes widgets etc in kv file, but still need create some in python for automatic content) or better just create all in python withou kv file? I wanna make somehting like this: App with left menu always displayed and on the right side another screen with dynamic content based on screen(clicked from menu) is maybe another simple solution for this. Anyone can explain me step by step? :)
AttributeError
The solution to AttributeError, please replace "id: 'test'" with "id: test" in test.kv file.
Dynamic Content
It is possible to display screen with dynamic content based on clicked from menu. But remember to remove the widgets that were added when exiting the screen (TestScreen/SettingsScreen). If you do not remove the widgets, you will get duplicates/multiples of each widget added each time you enter the screen (TestScreen/SettingsScreen). I recommend using on_pre_enter and on_leave methods. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class MyScreenManager(ScreenManager):
pass
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
def on_pre_enter(self, *args):
self.ids.test.add_widget(Button(text="Work?"))
def on_leave(self, *args):
self.ids.test.remove_widget(self.ids.test.children[0])
class TestApp(App):
title = "Add & Remove Widgets Dynamically"
def build(self):
return MyScreenManager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
<MyScreenManager>:
transition: NoTransition()
MenuScreen:
SettingsScreen:
<MenuScreen>:
name: 'menu'
GridLayout:
cols: 1
Button:
text: '1'
Button:
text: '2'
Button:
text: 'Test'
on_press: root.manager.current = 'settings'
<SettingsScreen>:
name:'settings'
GridLayout:
id: test
cols: 2
Button:
text: 'Test'
on_press: root.manager.current = 'menu'
Output
as stated in the title - I'm stuck. I've been playing with the code around and everything works as long as I keep ScreenManager and Popup separate. Once combined - they refuse to cooperate. Anyway, here is the simple app that shows the problem I'm having.
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import Screen, ScreenManager
class First(GridLayout,Screen):
def show_popup(self):
Popp().open()
pass
class Second(Screen):
pass
class Popp(Popup):
pass
class ScreenManagement(ScreenManager):
pass
app = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
return app
if __name__ == "__main__":
MainApp().run()
And main.kv file
ScreenManagement:
First:
Second:
<First>:
name:"First"
rows: 2
Button:
text: "FirstButton"
on_release: app.root.current = "Second"
Button:
text: "Show popup"
on_release: root.show_popup()
<Second>:
name:"Second"
Button:
text: "BUTTON"
on_release: app.root.current = "First"
<Popp>:
title: "testing"
text: "Hello world"
size_hint: None,None
size: 400,400
auto_dismiss: False
Button:
text: "Okay"
on_press: root.dismiss()
App starts, first and second screen are working but when trying to get popup up I end up with:
kivy.uix.popup.PopupException: Popup can have only one widget as content
Somehow Screen is seen as a widget inside of Popp? Or am I terribly misinterpreting kivy docs?
It's a bug with loading kv file, it should throw an exception in this case.
What you are doing in the code is loading the kv file twice, what causes some weird behavior. Just delete the Builder.load_file(..) and it will work. The file is going to be loaded automatically.
Also, never do double subclassing of widgets like class First(GridLayout, Screen) as it might lead to some problems. Instead, create a grid layout inside the screen.
Put the elements in the Popup inside a layout, for example: Boxlayout.
Here's what I mean:
<Popp>:
title: "testing"
BoxLayout:
orientation: 'vertical'
size_hint: None,None
size: 400,400
Label:
text: "Hello world"
Button:
text: "Okay"
on_press: root.dismiss()
I have same problem with using kivy Builder.load_file and Popup, they dont work together.
the solution is simple, build popup in python code side. this is a loading popup example:
python:
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
import time, threading
buildKV = Builder.load_file("example.kv")
class ExampleApp(App):
def show_popup(self):
content = BoxLayout(orientation= "vertical")
image=Image(source= 'files/loading.gif', anim_delay= 0)
label=Label(text= 'Model is Running.\nBe Patient Please.')
content.add_widget(image)
content.add_widget(label)
self.popup = Popup(title='Model is Running.',
size_hint=(.250, .785),
content=content, auto_dismiss=False)
self.popup.open()
def process_button_click(self):
# Open the pop up
self.show_popup()
# Call some method that may take a while to run.
# I'm using a thread to simulate this
mythread = threading.Thread(target=self.something_that_takes_5_seconds_to_run)
mythread.start()
def something_that_takes_5_seconds_to_run(self):
thistime = time.time()
while thistime + 10 > time.time(): # 5 seconds
time.sleep(1)
# Once the long running task is done, close the pop up.
self.pop_up.dismiss()
if __name__ == "__main__":
ExampleApp().run()
kivy:
BoxLayout:
Button:
height: 40
width: 100
size_hint: (None, None)
text: 'Click Me'
on_press: app.process_button_click()
I created a class for a Popup and set the title property in the kv file.
When the popup shows, it does not have the title as in the kv file, but instead shows No Title as if it was never set.
It is exactly the same problem as here, but I do not understand from this link what the problem is or how to make it work:
https://github.com/kivy/kivy/issues/751
I understand how to do this using IDs in kv lang, but that only works if the Popup is put as a child widget of the root widget (ex. MainUI). Then I can link an instance of a python class to a widget in the kv file.
But then the popup displays as part of the root widget.
What I want to do, is instantiate a new instance of the popNewDB class when the New button is clicked and have this instance use the values such as "title" in the KV file.
Can you please explain how to do this?
Here is my code:
py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
class popNewDB(Popup):
pass
class MainUI(Widget):
pop = ObjectProperty(popNewDB())
def showpopup(self):
self.pop.open()
class VerseReminder(App):
def build(self):
return MainUI()
if __name__ == '__main__':
VerseReminder().run()
kv file:
#:kivy 1.9.1
<popNewDB>
title: 'Hallo'
<MainUI>
Label:
pos: root.center_x - self.width/2,root.center_y + 200
text: "Quote Reminder"
BoxLayout:
size_hint: None,None
width: 400
height: 200
pos: root.center_x-200,root.center_y-50
orientation: 'vertical'
spacing: 20
Button:
size_hint: 1,1
text: "New..."
on_press: root.showpopup()
Button:
size_hint: 1,1
text: "Open..."
Button:
size_hint: 1,1
text: "Quit"
At the time pop = ObjectProperty(popNewDB()) is evaluated, the rules haven't been loaded, so only a barebones Popup will be created. Instead, you could do this:
class MainUI(Widget):
pop = ObjectProperty()
def showpopup(self):
if self.pop is None:
self.pop = PopNewDB()
self.pop.open()
Here, the first time the button is pressed, a new instance of PopNewDB will be created and stored in self.pop.
(NB: I renamed the Popup subclass to start with a Capital Letter, to be consistent with language standards and kivy expectations)