I have the following classes in my kivy app, and i would like to call the blink method in my mainApp class. The start pulsing and blink methods enable the MainWindow background to pulse. However it's not working inside the MainWindow class and i need to call it in my mainApp class. Commented out (build method in mainApp) is what i tried, which results to the error Exception: Invalid instance in App.root. My python file:
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.core.window import Window
from kivymd.app import MDApp
from kivy.uix.image import Image
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import ColorProperty
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from plyer import filechooser
data = ""
class MainWindow(Screen):
def analyze_data(self):
global data
data = self.ids.user_input.text
data = analyze(data)
animated_color = ColorProperty()
pulse_interval = 4
def blink(self):
x = Clock.schedule_once(self.start_pulsing, 5)
return x
def start_pulsing(self, *args):
d = self.pulse_interval / 2
anim = Animation(animated_color=(69/255, 114/255, 147/255, 1), duration=d) + Animation(animated_color=(1, 1, 1, 1), duration=d)
anim.repeat = True
anim.start(self)
class OutputScreen(Screen):
def on_enter(self, *args):
self.ids.output_label.text = data
class mainApp(MDApp):
def __init__(self):
super().__init__()
def choose_file(self):
try:
filechooser.open_file(on_selection = self.handle_selection)
except:
pass
def handle_selection(self,selection):
global path
selection_ls = selection[0]
path = selection_ls
#print(path)
def change_screen(self,screen):
screemanager = self.root.ids['screenmanager']
screemanager.current = screen
def change(self):
self.change_screen('output')
def back(self):
self.change_screen('main')
'''
def build(self):
x = MainWindow().blink()
return x'''
and my kv file:
#:import utils kivy.utils
GridLayout:
cols:1
ScreenManager:
id: screenmanager
MainWindow:
id: main
name: 'main'
OutputScreen:
id: output
name: 'output'
<MainWindow>:
BoxLayout:
orientation:'vertical'
MDBottomNavigation:
panel_color: utils.get_color_from_hex("#ffffff")
MDBottomNavigationItem:
name:'analytics'
text:'analytics'
icon:'account-circle'
FloatLayout:
size: root.width, root.height
canvas.before:
Color:
rgba: root.animated_color
Rectangle:
pos:self.pos
size:self.size
TextInput:
multiline:True
id: user_input1
pos_hint:{"x" : 0.05, "top" : 0.9}
size_hint: 0.9, 0.37
Label:
markup: True
id:input_label
pos_hint:{"x" : 0, "top":1}
size_hint: 1 ,0.08
font_size : 32
bold: True
canvas.before:
Color:
rgb: utils.get_color_from_hex("01121c")
Rectangle:
size: self.size
pos: self.pos
Button:
pos_hint:{"top" : 0.51, "x" : 0.05}
size_hint: (None,None)
width : 150
height : 40
font_size : 23
text:'Submit'
on_press: root.analyze_data()
on_release: app.change()
Button:
pos_hint:{"top":0.42, "x":0.05}
size_hint: (None,None)
width : 150
height : 40
font_size : 23
text:'Upload'
on_release:app.choose_file()
Button:
id:'info_button'
pos_hint:{"top":0.47, "x":0.8}
size_hint: (None,None)
width : 50
height : 22
font_size : 23
text:'i'
on_release:root.analytics_info()
<OutputScreen>:
ScrollView:
GridLayout:
cols:1
MDIconButton:
icon:'arrow-left'
pos_hint:{'top':1,'left':1}
size_hint: 0.1,0.1
user_font_size : '64sp'
on_release: app.back()
Label:
id: output_label
multiline:True
text_size: self.width, None
size_hint: 1, None
height: self.texture_size[1]
color: 0,0,0,1
padding_x: 15
Any help will be much appreciated.
The build() method of an App should return a Widget that will become the root of the App. But your build() method returns a ClockEvent (the return from Clock.schedule_once()). Try changing your build() method to:
def build(self):
x = MainWindow()
x.blink()
return x
Since you do not call Builder.load_file(), I assume that your kv file is named main.kv, and therefore will get loaded automatically. If that is true, then you do not need a build() method at all. Instead add an on_start() method to your mainApp class, like this:
def on_start(self):
self.root.ids.main.blink()
Related
I made a program with several buttons and I wish that when I press the button several times, the list of instructions only launches once.
I tried to do it with a global variable but when I want to use the same variable for both screens, I can’t do it.
I know I can do it using two global variables but I would like to use the same one in both screens.
How, when we get to the second screen, u takes the value "True"? Or is there a simpler solution with a decorator?
Here is my code:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
import random
from kivy.properties import ObjectProperty
from kivy.properties import ListProperty, StringProperty
kivy.uix.screenmanager.FadeTransition
kv = """
#: import NoTransition kivy.uix.screenmanager.NoTransition
#:import Clock kivy.clock.Clock
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import RiseInTransition kivy.uix.screenmanager.RiseInTransition
#: import CardTransition kivy.uix.screenmanager.CardTransition
MyScreenManager:
transition: RiseInTransition()
Question1:
name: "question1"
Question2:
name: "question2"
<Question1>:
label_wid : ratio
FloatLayout:
Button:
text: "+1"
pos: 270, 300
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press:
root.mauvais()
Button:
text: "following"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press:
Clock.schedule_once(root.suite, 0.75)
Label:
id: ratio
text: root.manager.theText
pos: 280,270
font_size: 17
color: 0,0,1,0.65
<Question2>:
label_wid2: ratio
FloatLayout:
Button:
text: "+1"
pos: 270, 300
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press:
root.mauvais()
Label:
id: ratio
text: root.manager.theText
pos: 280,270
font_size: 17
color: 0,0,1,0.65
"""
t=0
m=True
class MyScreenManager(ScreenManager):
theText = StringProperty('')
class Question1(Screen):
m= True
def mauvais(self):
global m
if m==True:
global t
t=t+1
self.manager.theText = str(t)
m=False
def suite(root,text):
root.manager.current = "question2"
pass
class Question2(Screen):
global m
m= True
def mauvais(self):
global m
if m==True:
global t
t=t+1
self.manager.theText = t
m=False
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return Builder.load_string(kv)
if __name__ == '__main__':
Quizz().run()
So, since I didn't get an answer to my question, I have a proposed solution that does both possibilities:
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, BooleanProperty, NumericProperty
kivy.uix.screenmanager.FadeTransition
kv = """
#: import NoTransition kivy.uix.screenmanager.NoTransition
#:import Clock kivy.clock.Clock
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import RiseInTransition kivy.uix.screenmanager.RiseInTransition
#: import CardTransition kivy.uix.screenmanager.CardTransition
MyScreenManager:
transition: RiseInTransition()
Question1:
name: "question1"
Question2:
name: "question2"
<Question1>:
label_wid : ratio
FloatLayout:
Button:
text: "+1"
pos: 270, 300
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press:
root.manager.mauvais() # method is now in MyScreenManager
Button:
text: "following"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press:
Clock.schedule_once(root.suite, 0.75)
Label:
id: ratio
text: root.manager.theText
pos: 280,270
font_size: 17
color: 0,0,1,0.65
<Question2>:
label_wid2: ratio
FloatLayout:
Button:
text: "+1"
pos: 270, 300
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press:
root.manager.mauvais() # method is now in MyScreenManager
Label:
id: ratio
text: root.manager.theText
pos: 280,270
font_size: 17
color: 0,0,1,0.65
"""
# time to ignore additional clicks
# if this is zero, ignore all additional clicks
CLICK_IGNORE_TIME = 3
class MyScreenManager(ScreenManager):
theText = StringProperty('')
m = BooleanProperty(True)
t = NumericProperty(0)
def mauvais(self):
if self.m==True:
self.t += 1
self.theText = str(self.t)
self.m=False
if CLICK_IGNORE_TIME > 0:
Clock.schedule_once(self.reset_m, CLICK_IGNORE_TIME)
else:
print('ignored click')
def reset_m(self, dt):
print('reset m')
self.m = True
class Question1(Screen):
def suite(root,text):
root.manager.current = "question2"
class Question2(Screen):
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return Builder.load_string(kv)
if __name__ == '__main__':
Quizz().run()
This code moves the mauvais() method to the MyScreenManager class, since both versions were identical.
The CLICK_IGNORE_TIME is how long to ignore additional clicks. If it is zero all additional clicks will be ignored
def Lnlau(self): #LNLAU = Load_Nodeid_List_And_Update
FRM = open('NodeidList.txt', 'r') #FileReadMode
while True:
for i, l in enumerate(FRM):
if (i)%2 == 1: #1 , 3 ,5 ,7...
l_new = l.rstrip('\n')
FSM = l_new
linecontents = BusInfo_Update()
linecontents.Update(FSM) #BusInfo_Update().Update(FSM) eg) FSM = 'ICB23221.txt'
else:
pass
i want start above this function when i start application.
so i added this function to class Loading_Screen(Screen)
and use Clock.schedule_once(self.Lnlau, 0) but all i could get on the screen was a black screen. no errors. if i get rid of this function on main.py, no problems. What should i do? make new class to use this function?
main.py
__version__ = "1.0"
#-*- coding: utf-8 -*-
import kivy
import os
kivy.require('1.10.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.animation import Animation
from kivy.clock import Clock
#from kivy.config import Config #windows size fixed
#Config.set('graphics', 'resizable', 0)
from kivy.core.window import Window
Window.size = (1080, 1920)
##########FOR BUS INFORMATION UPDATE#############
from urllib import urlencode, quote_plus
from urllib2 import Request as RQ
from urllib2 import urlopen as UO
import urllib
import xml.etree.ElementTree as etree
import os
import datetime
class Loading_Screen(Screen):
def __init__(self, **kwargs):
super(Loading_Screen, self).__init__(**kwargs)
Clock.schedule_once(self.callNext, 2)
def callNext(self,dt):
self.manager.current = 'Before_First_Screen'
class Load_BusInfo():
pass
class Before_First_Screen(Screen):
pass
class First_Screen(Screen):
pass
class Test_Screen(Screen):
pass
###################destination_station_name#####################
class DTN_TECHNOPARK(Screen):
pass
class DTN_BITZONE(Screen):
pass
class DTN_INUSTATION(Screen):
pass
class Bus_Information(Screen):
def __init__(self, **kwargs):
super(Bus_Information, self).__init__(**kwargs)
#oo = os.path.join('/data/data/org.test.tubuc/files/app/station/', 'ICB164000039.txt')
oo = os.path.join('/root/hi/station/', 'ICB164000039.txt')
with open(oo) as businfo:
Businfo= []
nolinenum=businfo.readline()
while nolinenum!='':
Businfo.append(nolinenum)
leftstations = (businfo.readline().rstrip('\n') + ' stations'.rstrip('\n'))
lefttime = (businfo.readline().rstrip('\n') + ' seconds'.rstrip('\n'))
nolinenum = businfo.readline().rstrip('\n')
Businfo.append(leftstations)
Businfo.append(lefttime)
self.businfolist = Businfo
self.lenbil = int(len(Businfo))
self.numberoflist = int(len(Businfo)/3)
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("main.kv")
class TubucApp(App):
def build(self):
return presentation
TubucApp().run()
main.kv
# -*- coding: utf-8 -*-
#:import NoTransition kivy.uix.screenmanager.NoTransition
#:import Label kivy.uix.button.Label
ScreenManagement:
transition: NoTransition()
Loading_Screen:
Before_First_Screen:
First_Screen:
Test_Screen:
DTN_TECHNOPARK:
DTN_BITZONE:
DTN_INUSTATION:
Bus_Information:
<Loading_Screen>:
name: 'Loading_Screen'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image/Loading_Screen.png'
<Before_First_Screen>:
name: 'Before_First_Screen'
Button:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image/BBG2.png'
on_release:
app.root.current = 'First_Screen'
<First_Screen>:
name: 'First_Screen'
BoxLayout:
orientation: 'horizontal'
spacing: 50 # spacing between button
padding: [50, 50, 50, 50]
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image/background.png' #backgroundimage
Button:
id: dongmak
width: 40
pos_hint: {'x' : 0, 'y':.45}
size_hint: [.6,.1]
background_normal:'image/2.png'
background_down:'image/after.png'
border: (0,0,0,0)
on_release: app.root.current = 'Bus_Information'
Button:
id: campustown
color: 100,100,100,100 #text_color
width: 40
pos_hint: {'x' : 0, 'y':.45}
size_hint: [.6,.1]
background_normal:'image/3.png'
background_down:'image/after.png'
border: (0,0,0,0)
on_release: app.root.current = 'DTN_BITZONE'
Button:
id: technopark
color: 100,100,100,100 #text_color
pos_hint: {'x' : 0, 'y':.45}
size_hint: [.6,.1]
background_normal:'image/4.png'
background_down:'image/after.png'
border: (0,0,0,0)
on_release: app.root.current = 'DTN_INUSTATION'
Button:
id: bitzone
width: 40
pos_hint: {'x' : 0, 'y':.45}
size_hint: [.6,.1]
background_normal:'image/5.png'
background_down:'image/after.png'
border: (0,0,0,0)
Button:
id: inustation
width: 40
pos_hint: {'x' : 0, 'y':.45}
size_hint: [.6,.1]
background_normal:'image/1.png'
background_down:'image/after.png'
border: (0,0,0,0)
#font_size: 15
#text: 'inustation'
<Test_Screen>:
name: 'Test_Screen'
Button:
on_release: app.root.current = 'First_Screen'
text: 'back to the home screen'
font_size: 50
############destionation_station_name################
<DTN_TECHNOPARK>:
name: 'DTN_TECHNOPARK'
Button:
on_release: app.root.current = 'First_Screen'
text: 'destionation >> technopark'
font_size: 50
<DTN_BITZONE>:
name: 'DTN_BITZONE'
Button:
on_release: app.root.current = 'First_Screen'
text: 'destionation >> bitzone'
font_size: 50
<DTN_INUSTATION>:
name: 'DTN_INUSTATION'
Button:
on_release: app.root.current = 'First_Screen'
text: 'destionation >> inustation'
font_size: 50
######################################################
<Bus_Information>:
name: 'Bus_Information'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image/BBG.png' #backgroundimage
#for sero in range(root.numberoflist):
# for garo in range(3):
GridLayout:
cols: 3
rows: root.numberoflist
spacing: 0
padding: [0,100]
on_parent:
for i in range(root.lenbil): txt = root.businfolist[i]; self.add_widget(Label(text = txt, text_size=(cm(2), cm(2)),
pos=self.pos, id=txt))
Problem: Endless While Loop in Kivy App
When you run your Kivy App which contains While True: in Lnlau(self) function, the program will never exit your loop, preventing Kivy from doing all of the other things that need doing. As a result, all you’ll see is a black window which you won’t be able to interact with.
Solution
Use Clock.schedule_interval() or Clock.create_trigger() functions to “schedule” your Lnlau() function to be called repeatedly.
Question:
How do you use an On-Press event to change screen for dynamically created buttons in python, and without using KV language?
Goal:
Be able to navigate to new screen from clicking on dynamically created button,
[without needing to create button in Kivy, and still getting to use Screenmanager in both Python and Kivy (not sure if you have to stick with either Python or Kivy throughout entire program?]
Things I've already tried:
Using button_share.bind(on_press = self.changer), then this:
def changer(self,*args):
ScreenManager()
screenmanager.current = 'MainScreen'
But I get the error ScreenManagerException: No Screen with name "MainScreen".
Suspicion:
I think this is because I'm creating a new instance of ScreenManager, instead of referencing the existing one. To combat this issue, I considered instantiating Screenmanager() in the App class, then referencing that instantiation in the my button def changer(self, *args) method, but that's useless if it's not the same ScreenManager I'm actually using for all my screens. And those are all defined in KV language. I wouldn't be able to switch them all over without a substantial amount of effort.
Using:
button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))`
But the error I get here is ValueError: ScreenManager.current accept only str
Below is a fully runnable example:
Note: In this example, I want to click 'Continue Editing' button, then click on 'Test 1', 'Test 2' or 'Test 3' button and have it take me to another screen.
Python Code:
from kivy.app import App
# kivy.require("1.10.0")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.uix.widget import Widget
#from kivy.base import runTouchApp
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from functools import partial
class ScrollableLabelDataEntryInstructions(BoxLayout):
pass
class NewGarageScreen(Screen):
pass
class ContinueEditingScreen(Screen):
pass
class GarageNameBoxLayout(BoxLayout):
box_share2 = ObjectProperty()
sm = ScreenManager()
def __init__(self, **kwargs):
super(GarageNameBoxLayout, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 5)
def create_button(self, *args):
self.box_share2.clear_widgets()
app = App.get_running_app()
#put GarageNameStartList data into app class, then pull from it in this class
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(len(app.GarageNameStartList)):
top_button_share -= .4
id_ = app.GarageNameStartList[i]
button_share = Button(background_normal='',
background_color = color,
id = id_,
pos_hint = {"x": 0, "top": top_button_share},
size_hint_y = None,
height = 60,
font_size = 30,
text = app.GarageNameStartList[i])
button_share.bind(on_press = self.changer)
#button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))
self.box_share2.add_widget(button_share)
def changer(self,*args):
ScreenManager()
#app = App.get_running_app()
screenmanager.current = 'MainScreen'
class BackHomeWidget(Widget):
pass
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("example_on_press.kv")
class MainApp(App):
GarageNameStartList = ["Test1", "Test2", "Test3"]
def Update_GarageNameStartList(self, *args):
self.GarageNameStartList = ["Test1", "Test2", "Test3"]
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
KV Code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
NewGarageScreen:
ContinueEditingScreen:
<SmallNavButton#Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<MedButton#Button>:
font_size: 30
size_hint: 0.25, 0.1
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
MedButton:
on_release: app.root.current = "edit"
text: "Edit"
pos_hint: {"x":0.3728, "top": 0.4}
<AnotherScreen>:
name: "edit"
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "main"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
FloatLayout:
MedButton:
on_release: app.root.current = "continueediting"
text: "Continue Editing"
pos_hint: {"x":0.25, "top": 0.6}
MedButton:
on_release: app.root.current = "newgarage"
text: "Create New"
pos_hint: {"x":0.3728, "top": 0.4}
<NewGarageScreen>:
name: "newgarage"
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "edit"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
FloatLayout:
MedButton:
text: "1. Groundfloor"
pos_hint: {"x":0, "top": 0.6}
<GarageNameBoxLayout>:
box_share2: box_share2
ScrollView:
GridLayout:
id: box_share2
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<ContinueEditingScreen>:
name: "continueediting"
GarageNameBoxLayout:
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "edit"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
Your code can be improved in the following things:
You do not have to create box_share2 in the .py since you're creating it in the .kv
When you use sm = ScreenManager() you are creating another ScreenManager different from the original, that is not necessary.
It is not necessary to use range and len, make your code less readable, you just have to iterate.
If we look at the structure of the .kv we see that the presentation object is the ScreenManager so you can get it via app.root.
Using the above your code the solution is:
[...]
class GarageNameBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(GarageNameBoxLayout, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 5)
def create_button(self, *args):
self.box_share2.clear_widgets()
app = App.get_running_app()
sm = app.root
#put GarageNameStartList data into app class, then pull from it in this class
top_button_share = 1.1
color = (.4, .4, .4, 1)
for text in app.GarageNameStartList:
top_button_share -= .4
id_ = text
button_share = Button(background_normal='',
background_color = color,
id = id_,
pos_hint = {"x": 0, "top": top_button_share},
size_hint_y = None,
height = 60,
font_size = 30,
text = text)
button_share.bind(on_press=lambda *args: setattr(sm, 'current', "main"))
self.box_share2.add_widget(button_share)
[...]
Goal:
Change background color of dynamically created widget with on-press.
Save this state with pickle such that when I open the program back up, the new color change is preserved
Note: You'll see in my code that I haven't made an attempt on saving the button bg color state to a file yet, as I'm still trying to get the on-press event to function.
I get the following error:
File "C:/Users/phili/scrollablelabelexample.py", line 45, in create_button
button_share.bind(on_press = self.update_buttons_departoverride(self))
TypeError: update_buttons_departoverride() takes 1 positional argument but 2 were given
Python code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from kivy.clock import Clock
import pandas as pd
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
class AnotherScreen(Screen):
pass
class BackHomeWidget(Widget):
pass
class Sequence(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class MainScreen(Screen):
pass
class CleanScreen(BoxLayout):
def __init__(self, **kwargs):
super(CleanScreen, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_once(lambda *args:self.create_button(self.ids.box_share))
def create_button(self, box_share):
top_button_share = 1.1
color = [.48,.72,.23,1]
for i in range(len(parts)):
top_button_share -= .4
button_share = Button(background_normal = '', background_color = color, id = "part"+str(i+1),pos_hint={"x": 0, "top": top_button_share}, size_hint_y=None, height=60, text=str(i))
button_share.bind(on_press = self.update_buttons_departoverride(self))
box_share.add_widget(button_share)
def update_buttons_departoverride(self):
self.background_color = 1.0, 0.0, 0.0, 1.0
presentation = Builder.load_file("garagemainexample.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
Kv Code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
Sequence:
<BigButton#Button>:
font_size: 40
size_hint: 0.5, 0.15
color: 0,1,0,1
<SmallNavButton#Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
BigButton:
on_release: app.root.current = "sequence"
text: "Sequence"
pos_hint: {"x":0.25, "top": 0.4}
<CleanScreen>:
ScrollView:
GridLayout:
id: box_share
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 90
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<Sequence>:
name: "sequence"
CleanScreen:
id: cleanscreen
BackHomeWidget:
With button_share.bind (on_press = self.update_buttons_departoverride (self)) you're calling the method, so you're trying to bind on_press with None (self.update_buttons_departoverride return None). If you want to pass arguments, use lambda or functools.partial:
from functools import partial
button_share.bind(on_press=partial(self.update_buttons_departoverride, arg1,...))
However, if you need to pass only the button's reference, it is already passed automatically. You just have to do:
button_share.bind(on_press=self.update_buttons_departoverride)
and:
def update_buttons_departoverride(self, button):
To store the configuration of your widgets you can use Storage. A simplified example using DictStore:
main.py:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.storage.dictstore import DictStore
class AnotherScreen(Screen):
pass
class BackHomeWidget(Widget):
pass
class Sequence(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class MainScreen(Screen):
pass
class CleanScreen(BoxLayout):
box_share = ObjectProperty()
config_file = DictStore('conf.dat')
def __init__(self, **kwargs):
super(CleanScreen, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_once(self.create_button)
def create_button(self, *args):
top_button_share = 1.1
color = (.48, .72, .23, 1)
for i in range(5):
top_button_share -= .4
id_ = "part" + str(i + 1)
if self.config_file.exists(id_):
btn_color = self.config_file[id_]["background_color"]
else:
self.config_file.put(id_, background_color=color)
btn_color = color
button_share = Button(background_normal='',
background_color=btn_color,
id=id_,
pos_hint={"x": 0, "top": top_button_share},
size_hint_y=None,
height=60,
text=str(i))
button_share.bind(on_press=self.update_buttons_departoverride)
self.box_share.add_widget(button_share)
def update_buttons_departoverride(self, button):
button.background_color = 1.0, 0.0, 0.0, 1.0
self.config_file.put(button.id, background_color=(1.0, 0.0, 0.0, 1.0))
presentation = Builder.load_file("garagemainexample.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
garagemainexample.kv:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
Sequence:
<BigButton#Button>:
font_size: 40
size_hint: 0.5, 0.15
color: 0,1,0,1
<SmallNavButton#Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
BigButton:
on_release: app.root.current = "sequence"
text: "Sequence"
pos_hint: {"x":0.25, "top": 0.4}
<CleanScreen>:
box_share: box_share
ScrollView:
GridLayout:
id: box_share
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 90
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<Sequence>:
name: "sequence"
CleanScreen:
id: cleanscreen
BackHomeWidget:
I am trying to implement a custom closable tab header in kivy.
What I did was combine a class:TabbedPanelHeader object with a custom class:CloseButton object. Both of these widgets are inside a class:BoxLayout, side-by-side.
However, once I add this into a class:TabbedPanel object, nothing shows up..
I am not sure how to move forward and would greatly appreciate all the help!
Below is the relevant part of the code.
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.graphics import *
from kivy.uix.tabbedpanel import TabbedPanelHeader
class CloseButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(CloseButton, self).__init__(**kwargs)
self.source = 'atlas://data/images/defaulttheme/close'
self.size_hint_x = .2
def on_press(self):
self.source = 'atlas://data/images/defaulttheme/checkbox_radio_off'
def on_release(self):
self.source = 'atlas://data/images/defaulttheme/checkbox_radio_off'
## do the actual closing of the tab
class ClosableTabHeader(BoxLayout):
def __init__(self, **kwargs):
super(ClosableTabHeader, self).__init__(**kwargs)
self.size = (100, 30)
self.size_hint = (None, None)
self.canvas.before.add(Color(.25, .25, .25))
self.canvas.before.add(Rectangle(size=(105, 30)))
self.add_widget(TabbedPanelHeader(background_color=(.65, .65, .65, 0), text='testing'))
self.add_widget(CloseButton())
if __name__ == '__main__':
from kivy.app import App
class TestApp(App):
def build(self):
return ClosableTabHeader()
TestApp().run()
Here is some code which comes close to achieve what you are trying to achieve
from kivy.app import App
from kivy.animation import Animation
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
from kivy.factory import Factory
from kivy.lang import Builder
class CloseableHeader(TabbedPanelHeader):
pass
class TestTabApp(App):
def build(self):
return Builder.load_string('''
TabbedPanel:
do_default_tab: False
FloatLayout:
BoxLayout:
id: tab_1_content
Label:
text: 'Palim 1'
BoxLayout:
id: tab_2_content
Label:
text: 'Palim 2'
BoxLayout:
id: tab_3_content
Label:
text: 'Palim 3'
CloseableHeader:
text: 'tab1'
panel: root
content: tab_1_content.__self__
CloseableHeader:
text: 'tab2'
panel: root
content: tab_2_content.__self__
CloseableHeader:
text: 'tab3'
panel: root
content: tab_3_content.__self__
<CloseableHeader>
color: 0,0,0,0
disabled_color: self.color
# variable tab_width
text: 'tabx'
size_hint_x: None
width: self.texture_size[0] + 40
BoxLayout:
pos: root.pos
size_hint: None, None
size: root.size
padding: 3
Label:
id: lbl
text: root.text
BoxLayout:
size_hint: None, 1
orientation: 'vertical'
width: 22
Image:
source: 'tools/theming/defaulttheme/close.png'
on_touch_down:
if self.collide_point(*args[1].pos) :\
root.panel.remove_widget(root); \
''')
if __name__ == '__main__':
TestTabApp().run()
It is based on https://github.com/kivy/kivy/blob/master/examples/widgets/tabbed_panel_showcase.py