Kivy: FileChooser redrawing - kivy

I want to use FileChooser for basic operation. I select folder using FileChooser, I will remove it using own function. Folder is removed and I want to show new contents of disc but contents is incorrect How do I show current right contents of disc?

My question based from problem with next code. When I removed folder in root directory, contens of FileChooserListView was incorrect. Source of problem was in discname. Last symbol in disc name wasn't '**'. After added this ( function delete_dir() isn't problem.
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty('')
def __init__(self,**kwargs):
self.register_event_type('on_answer')
super(ConfirmPopup,self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupYesNo(GridLayout):
def __init__(self, save_as, task):
self.save_as = save_as
self.task = task
def show_widget(self, question):
self.content = ConfirmPopup(text= question)
self.content.bind(on_answer = self._on_answer)
self.popup = Popup(title="Answer Question",
content=self.content,
size_hint=(None, None),
size=(480,400),
auto_dismiss= False)
self.popup.open()
def _on_answer(self, instance, answer):
if answer == 'yes':
self.save_as.act_task()
self.popup.dismiss()
return
class SaveAs(BoxLayout):
def __init__(self, **kwargs):
super(SaveAs,self).__init__(**kwargs)
self.orientation = 'vertical'
self.fichoo = FileChooserListView(size_hint_y = 0.8)
self.add_widget(self.fichoo)
control = GridLayout(cols = 5, row_force_default=True, row_default_height=35, size_hint_y = 0.14)
self.tein_dir = TextInput(size_hint_x = None, width = 350)
self.tein_dir.multiline = False
bt_del_dir = Button(text = 'Remove',size_hint_x = None, width = 80)
bt_del_dir.bind(on_release = self.on_delete_dir)
control.add_widget(self.tein_dir)
control.add_widget(bt_del_dir)
self.fichoo.bind(path = self.on_path_select)
self.add_widget(control)
return
def on_path_select(self, inst, val):
self.tein_dir.text = str(self.fichoo.path)
return
def on_delete_dir(self, obj):
question = 'Do You want to remove: '+ self.tein_dir.text+ '?'
self.act_task = self.delete_dir
popup = PopupYesNo(self, SaveAs.delete_dir)
popup.show_widget(question)
return
def delete_dir(self):
pos = self.fichoo.path.rfind('\\', 0, len(self.fichoo.path))
new_path = str(self.fichoo.path)[0:pos]
if new_path[-1] == ':':
new_path += '\\' # last symbol in discname is '\'
self.tein_dir.text = new_path
os.chdir(new_path)
shutil.rmtree(str(self.fichoo.path))
self.fichoo.path = new_path
return
class ExplorerApp(App):
def build(self):
self.save_as = SaveAs()
return self.save_as
if __name__ == '__main__':
ExplorerApp().run()

Related

.kv is loaded multiples times, you might have unwanted behaviors

I can run my code but I'm having such unwanted behavior enter image description here, I combined my source code with the https://dev.to/ngonidzashe/how-to-create-a-simple-to-do-list-application-with-kivymd-d89 , and I guess I didn't combine it right here are my code:
import mysql.connector
from kivy.lang import Builder
from kivymd.uix.dialog import MDDialog
from kivymd.uix.button import MDFlatButton
from kivy.uix.screenmanager import ScreenManager
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.picker import MDDatePicker
from datetime import datetime
from kivymd.uix.list import TwoLineAvatarIconListItem, ILeftBodyTouch
from kivymd.uix.selectioncontrol import MDCheckbox
from database import Database
from kivy.core.window import Window
Window.size = (350, 600)
db = Database()
class MainApp(MDApp):
def build(self):
global screen_manager
screen_manager = ScreenManager()
screen_manager.add_widget(Builder.load_file("login.kv"))
screen_manager.add_widget(Builder.load_file("register.kv"))
screen_manager.add_widget(Builder.load_file("main.kv"))
return screen_manager
database = mysql.connector.connect(host='localhost',
database='loginform',
user='root',
password='****')
cursor = database.cursor()
def send_data(self, user, password):
self.cursor.execute(f"insert into example2 values('{user.text}','{password.text}')")
self.database.commit()
def receive_data(self, user, password):
self.cursor.execute("select * from example2")
user_list = []
for i in self.cursor.fetchall():
user_list.append(i[0])
if user.text in user_list and user.text != "":
self.cursor.execute(f"select password from example2 where user='{user.text}'")
for j in self.cursor:
if password.text == j[0]:
open_button = MDFlatButton(text='Open the App', on_release=self.open_app)
close_button = MDFlatButton(text='Close', on_release=self.close_dialg)
self.dialog = MDDialog(text='Welcome to the *****',
buttons=[close_button, open_button])
self.dialog.open()
else:
try_button = MDFlatButton(text='Try Again', on_release=self.close_dialg)
self.dialog = MDDialog(title='Incorrect Password',
text='You entered wrong password please try again',
buttons=[try_button])
self.dialog.open()
else:
open_regist = MDFlatButton(text="Register", on_release=self.open_regist)
close_button = MDFlatButton(text='Close', on_release=self.close_dialg)
self.dialog = MDDialog(title='Incorrect User',
text='There are no such user',
buttons=[close_button, open_regist])
self.dialog.open()
def open_regist(self, *args):
screen_manager.current = "register"
self.dialog.dismiss()
def close_dialg(self, *args):
self.dialog.dismiss()
def open_app(self, *args):
screen_manager.current = "tasks"
self.dialog.dismiss()
task_list_dialog = None # Here
# Add the below functions
def show_task_dialog(self):
if not self.task_list_dialog:
self.task_list_dialog = MDDialog(
title="Create Task",
type="custom",
content_cls=DialogContent(),
)
self.task_list_dialog.open()
def on_start(self):
"""Load the saved tasks and add them to the MDList widget when the application starts"""
try:
completed_tasks, uncomplete_tasks = db.get_tasks()
if uncomplete_tasks != []:
for task in uncomplete_tasks:
add_task = ListItemWithCheckbox(pk=task[0], text=task[1], secondary_text=task[2])
self.root.ids.container.add_widget(add_task)
if completed_tasks != []:
for task in completed_tasks:
add_task = ListItemWithCheckbox(pk=task[0], text='[s]' + task[1] + '[/s]',
secondary_text=task[2])
add_task.ids.check.active = True
self.root.ids.container.add_widget(add_task)
except Exception as e:
print(e)
pass
def close_dialog(self, *args):
self.task_list_dialog.dismiss()
def add_task(self, task, task_date):
"""Add task to the list of tasks"""
# Add task to the db
created_task = db.create_task(task.text, task_date) # Here
# return the created task details and create a list item
self.root.ids['container'].add_widget(
ListItemWithCheckbox(pk=created_task[0], text='[b]' + created_task[1] + '[/b]',
secondary_text=created_task[2])) # Here
task.text = ''
class DialogContent(MDBoxLayout):
"""OPENS A DIALOG BOX THAT GETS THE TASK FROM THE USER"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
# set the date_text label to today's date when user first opens dialog box
self.ids.date_text.text = str(datetime.now().strftime('%A %d %B %Y'))
def show_date_picker(self):
"""Opens the date picker"""
date_dialog = MDDatePicker()
date_dialog.bind(on_save=self.on_save)
date_dialog.open()
def on_save(self, instance, value, date_range):
"""This functions gets the date from the date picker and converts its it a
more friendly form then changes the date label on the dialog to that"""
date = value.strftime('%A %d %B %Y')
self.ids.date_text.text = str(date)
# create the following two classes
class ListItemWithCheckbox(TwoLineAvatarIconListItem):
"""Custom list item"""
def __init__(self, pk=None, **kwargs):
super().__init__(**kwargs)
# state a pk which we shall use link the list items with the database primary keys
self.pk = pk
def mark(self, check, the_list_item):
"""mark the task as complete or incomplete"""
if check.active == True:
# add strikethrough to the text if the checkbox is active
the_list_item.text = '[s]' + the_list_item.text + '[/s]'
db.mark_task_as_complete(the_list_item.pk) # here
else:
the_list_item.text = str(db.mark_task_as_incomplete(the_list_item.pk)) # Here
def delete_item(self, the_list_item):
"""Delete the task"""
self.parent.remove_widget(the_list_item)
db.delete_task(the_list_item.pk) # Here
class LeftCheckbox(ILeftBodyTouch, MDCheckbox):
"""Custom left container"""
if __name__ == '__main__':
app = MainApp()
app.run()
and here is my kv file with this:
MDScreen:
name: "tasks"
MDFloatLayout:
MDLabel:
id: task_label
halign: 'center'
markup: True
text: "[u][size=48][b]TASKS[/b][/size][/u]"
pos_hint: {'y': .45}
ScrollView:
pos_hint: {'center_y': .5, 'center_x': .5}
size_hint: .9, .8
MDList:
id: container
MDFloatingActionButton:
icon: 'plus-thick'
on_release: app.show_task_dialog() #functionality to be added later
elevation_normal: 12
pos_hint: {'x': .8, 'y':.05}
<DialogContent>:
orientation: "vertical"
spacing: "10dp"
size_hint: 1, None
height: "130dp"
GridLayout:
rows: 1
MDTextField:
id: task_text
hint_text: "Add Task..."
pos_hint: {"center_y": .4}
max_text_length: 50
on_text_validate: (app.add_task(task_text, date_text.text), app.close_dialog())
MDIconButton:
icon: 'calendar'
on_release: root.show_date_picker()
padding: '10dp'
MDLabel:
spacing: '10dp'
id: date_text
BoxLayout:
orientation: 'horizontal'
MDRaisedButton:
text: "SAVE"
on_release: (app.add_task(task_text, date_text.text), app.close_dialog())
MDFlatButton:
text: 'CANCEL'
on_release: app.close_dialog()
<ListItemWithCheckbox>:
id: the_list_item
markup: True
LeftCheckbox:
id: check
on_release:
root.mark(check, the_list_item)
IconRightWidget:
icon: 'trash-can-outline'
theme_text_color: "Custom"
text_color: 1, 0, 0, 1
on_release:
root.delete_item(the_list_item)
I would be very thankful if someone can help or give some tips, how to improve. I'm open to have private conversation.
A kv file with the name main.kv will be loaded automatically by Kivy for your application (See documentation). So your code:
screen_manager.add_widget(Builder.load_file("main.kv"))
is loading main.kv a second time. The easiest fix is to just rename the main.kv file.

PySide & QWT object disable/destroy

I am just learning OOP and PySide. I have created a code as below.
The application doesn't do anything much (it's a development project in learning stages).
import numpy as np
import sys
from qtpy.QtWidgets import (
QWidget,
QMainWindow,
QVBoxLayout,
QAction,
QMenu,
QLabel,
QApplication,
QMessageBox,
QDesktopWidget,
)
from qtpy.QtCore import Qt, Slot, QPoint, QObject
from qwt import (
QwtPlot,
QwtPlotMarker,
QwtPlotGrid,
QwtLegend,
QwtPlotCurve,
QwtLegendData,
)
class contexMenuHelper(QObject):
def __init__(self, plot, legend, legendItem):
super(contexMenuHelper, self).__init__()
self.plot = plot
self.legend = legend
self.legendItem = legendItem
#Slot(QPoint)
def contextMenuSlot(self, pos):
context = QMenu(self.legendItem)
context.addAction(QAction("Delete", self))
context.exec_(self.legendItem.mapToGlobal(pos))
class Plot(QwtPlot, QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAxisTitle(QwtPlot.xBottom, "X-axis")
self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
self.setCanvasBackground(Qt.white)
self.setAxisScale(QwtPlot.yLeft, -2, 2)
QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)
legend = QwtLegend()
legend.setDefaultItemMode(QwtLegendData.Checkable)
self.insertLegend(legend, QwtPlot.RightLegend)
x = np.arange(-5.0, 5.0, 0.1)
curves = []
curves.append(
QwtPlotCurve.make(
x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
)
)
curves.append(
QwtPlotCurve.make(
x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
)
)
self.helpers = dict()
for a in curves:
legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
h = contexMenuHelper(self, legend, legend.legendWidget(a))
self.helpers[a] = h
legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.HLine,
color="black",
plot=self,
)
for keys, value in self.helpers.items():
print(keys)
print(value)
# insert a vertical marker at x = 0
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.VLine,
color="black",
plot=self,
)
legend.checked.connect(self.showCurve)
self.replot()
#Slot(object, bool, int)
def showCurve(self, obj, condition, num):
obj.setVisible(not condition)
self.replot()
#Slot(object, bool, int)
def __del__(self, obj, condition):
print('Destructor called, vehicle deleted.')
class SimplePlot(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
plot = Plot()
plot.setTitle("Trigonometric")
self.setWindowTitle("Trigonometric")
layout.addWidget(plot)
label = QLabel("Press the legend to en/disable a curve")
layout.addWidget(label)
self.center()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self, event):
reply = QMessageBox.question(
self,
"Message",
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimplePlot()
window.show()
window.resize(800, 600)
sys.exit(app.exec_())
I made the active legend and the context menu:
I want to make it so that when I select "Delete" from the context menu, the corresponding function waveform in the graph and the corresponding object in the legend will be deleted.
I have implemented it as follows. Perhaps someone will find my thinking useful. It works correctly and as I expected although there is a tiny error in the operation itself .
Do you see what error I mean?
class contexMenuHelper(QObject):
def __init__(self, plot, legend, legendItem):
super(contexMenuHelper, self).__init__()
self.plot = plot
self.legend = legend
self.legendItem = legendItem
self.emlSel = QAction("Delete")
#Slot(QPoint)
def contextMenuSlot(self, pos):
context = QMenu(self.legendItem)
context.addAction(self.emlSel)
context.exec_(self.legendItem.mapToGlobal(pos))
self.emlSel.triggered.connect(self.destroy())
#Slot()
def destroy(self):
QwtPlotCurve.detach(self.legend)
class Plot(QwtPlot, QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAxisTitle(QwtPlot.xBottom, "X-axis")
self.setAxisTitle(QwtPlot.yLeft, "Y-axis")
self.setCanvasBackground(Qt.white)
self.setAxisScale(QwtPlot.yLeft, -2, 2)
QwtPlotGrid.make(self, color=Qt.lightGray, width=0, style=Qt.DotLine)
legend = QwtLegend()
legend.setDefaultItemMode(QwtLegendData.Checkable)
legend.resize(100,100)
self.insertLegend(legend, QwtPlot.RightLegend)
x = np.arange(-5.0, 5.0, 0.1)
curves = []
curves.append(
QwtPlotCurve.make(
x, np.cos(x), "Cosinus", self, linecolor="red", antialiased=True
)
)
curves.append(
QwtPlotCurve.make(
x, np.sin(x), "Sinus", self, linecolor="blue", antialiased=True
)
)
self.helpers = dict()
for a in curves:
legend.legendWidget(a).setContextMenuPolicy(Qt.CustomContextMenu)
h = contexMenuHelper(self, a, legend.legendWidget(a))
self.helpers[a] = h
legend.legendWidget(a).customContextMenuRequested.connect(h.contextMenuSlot)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.HLine,
color="black",
plot=self,
)
QwtPlotMarker.make(
align=Qt.AlignRight | Qt.AlignTop,
linestyle=QwtPlotMarker.VLine,
color="black",
plot=self,
)
legend.checked.connect(self.showCurve)
self.replot()
#Slot(object, bool, int)
def showCurve(self, obj, condition, num):
obj.setVisible(not condition)
self.replot()
class SimplePlot(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.setLayout(layout)
plot = Plot()
plot.setTitle("Trigonometric")
self.setWindowTitle("Trigonometric")
layout.addWidget(plot)
label = QLabel("Press the legend to en/disable a curve")
layout.addWidget(label)
self.center()
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self, event):
reply = QMessageBox.question(
self,
"Message",
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimplePlot()
window.show()
window.resize(850, 600)
sys.exit(app.exec_())

Is there a way to move a kivy slider with button's and text box?

I have a project I would like to try and use kivy as a user interface.
The interface would have 1 slider 2 buttons and a text box. Basically the slider can be moved by sliding or finely incremented by the buttons. With a text box to read out the values but also able to manual enter a desired value.
I have googled around a bit and have not seen any examples of bonding all 3 inputs to 1 slider. I have only seen taking values from a slider and displaying it, I have no idea how to move a slider with a value.
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang.builder import Builder
class CustomSlider(BoxLayout):
def on_kv_post(self, base_widget):
super().on_kv_post(base_widget)
self.ids["button_less"].bind(on_press=self.on_button_less_click)
self.ids["button_more"].bind(on_press=self.on_button_more_click)
self.ids["text_input"].bind(text=self.on_text_input_changement)
self.ids["slider"].bind(on_touch_move=self.on_slider_move)
def on_slider_move(self, w, touch):
self.ids["slider"].value = 5 * round(self.ids["slider"].value / 5)
self.ids["text_input"].text = str(self.ids["slider"].value)
def on_button_less_click(self, w):
self.ids["text_input"].text = str(int(self.ids["text_input"].text) - 5)
def on_button_more_click(self, w):
self.ids["text_input"].text = str(int(self.ids["text_input"].text) + 5)
def on_text_input_changement(self, w, value):
self.ids["text_input"].text = self.get_value(value)
self.ids["slider"].value = int(self.ids["text_input"].text)
def get_value(self, value):
if not self.isInt(value):
return "0"
value = int(value)
if value < 0: return "0"
elif value > 100: return "100"
else: return str(value)
def isInt(self, value):
try:
int(value)
return True
except:
return False
class MyFirstKivyApp(App):
def build(self):
return Builder.load_file("main.kv")
MyFirstKivyApp().run()
main.kv
<CustomSlider>:
orientation: "vertical"
BoxLayout:
Slider:
id: slider
min: 0
max: 100
BoxLayout:
Button:
id: button_less
text: "-"
Button:
id: button_more
text: "+"
BoxLayout:
TextInput:
id: text_input
input_filter: "int"
text: "0"
CustomSlider:
you can try the following code
Kivy File:
Button:
id: button_more
text: "+"
on_release: root.on_button_more_click()
Python File inside the CustomSlider class:
def on_button_more_click(self):
self.ids.slider.value += 1

How to link screen to list items in a Kivymd list

I'm using ScreenManager. I would like to link the items in a OneLineListItem to new screen.
This is my KV file
screen_helper = """
ScreenManager:
GroupsScreen:
MyGroupScreen:
<GroupsScreen>:
name: 'groups'
ScrollView:
MDList:
id: container_groups
<MyGroupScreen>:
name: 'my_group'
ScrollView:
MDList:
id: container_group
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'groups'
and this is the python file
class GroupsScreen(Screen):
def on_enter(self, *args):
for i in range(5):
item = OneLineListItem(text='Gruppo ' + str(i))
self.ids.container_groups.add_widget(item)
class MyGroupScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(GroupsScreen(name='groups'))
sm.add_widget(MyGroupScreen(name='my_group'))
class DemoApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Red"
self.theme_cls.primary_hue = "500"
screen = Builder.load_string(screen_helper)
return screen
I would like to click on an item in the groups screen and go to MyGroupScreen.
The OneLineListItem class actually extends ButtonBehavior, so you can treat it like a Button. Just assign a on_press or on_release method to the OneLineListItem:
class GroupsScreen(Screen):
def on_kv_post(self, *args):
for i in range(5):
item = OneLineListItem(text='Gruppo ' + str(i))
item.on_release = self.switch_to_my_groups
self.ids.container_groups.add_widget(item)
def switch_to_my_groups(self, *args):
self.manager.current = 'my_group'
I changed your on_enter() method to a on_kv_post() to be sure that the kv rules have been executed (so that the ids are available).
Another problem is the lines:
sm = ScreenManager()
sm.add_widget(GroupsScreen(name='groups'))
sm.add_widget(MyGroupScreen(name='my_group'))
These lines should be removed. They attempt build the GUI, and then the result is not used. The line:
screen = Builder.load_string(screen_helper)
then rebuilds the GUI again, and this time it is actually used.

How do i create a countdown timer in ModalView after pressing a button?

I am trying to create a modalview with a timer in it. Upon pressing the "begin" button, a modal view should appear and the countdown should start. However, I am getting an valueerror message. ValueError: TimerView.timer has an invalid format (got main.TimerView object at 0x0000017AD40D6180>>>).May I know which part of the code is wrong? Thanks in advance.
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition, WipeTransition, NoTransition, SlideTransition
from kivymd.theming import ThemeManager
from kivy.properties import StringProperty, NumericProperty
from kivy.uix.modalview import ModalView
from kivymd.uix.label import MDLabel
from kivy.clock import Clock
class TimerView(ModalView):
number = NumericProperty(15)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.background = "transparent_image.png"
self.background_color = [1/3,1/3,1/3,0.8]
self.auto_dismiss = False
self.size_hint = (None,None)
self.size = (150,50)
timer_countdown = MDLabel(font_style = 'H1', theme_text_color = 'Primary',
text = str(self.number), halign = 'center',
text_color = (1,1,1,1), size_hint_y = None)
self.add_widget(timer_countdown)
def decrement_time(self, dt):
self.number -= 1
def start(self,*args):
self.timer = Clock.schedule_interval(self.decrement_time, 1)
def stop(self):
Clock.unschedule(self.timer)
class MainScreen(Screen):
pass
class BeginScreen(Screen):
pass
class MyScreenManager(ScreenManager):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.view = TimerView()
def open_view(self):
self.view.bind(on_open=self.view.start)
self.view.open()
main_widget_kv = ('''
#: import ScrollEffect kivy.effects.scroll.ScrollEffect
MyScreenManager:
BeginScreen:
<BeginScreen>:
begin_button:begin_button
name: "begin"
canvas.before:
Color:
rgb: .1, .1, .1
FloatLayout:
id: begin_layout
Button:
id: begin_button
text: 'Begin'
font_size: 24
on_press: app.root.open_view()
size_hint: (.4,.25)
pos_hint: {"center_x":.5, "center_y":.2}
color: [0,0,0,1]
''')
class TestApp(MDApp):
def __init__(self,**kwargs):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Red"
super().__init__(**kwargs)
def build(self):
main_widget = Builder.load_string(main_widget_kv)
return main_widget
if __name__ == '__main__':
TestApp().run()
When you set the text of a Label in python code, it uses the value at that time, and will not change automatically. If you do the same thing in kv, it will update automatically (provided that the text references a Property). So just changing self.number has no effect on your timer_countdown Label.
So, you need to update that text explicitly. Here is a modified version of your code that does that:
class TimerView(ModalView):
number = NumericProperty(15)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.background = "transparent_image.png"
self.background_color = [1/3,1/3,1/3,0.8]
self.auto_dismiss = False
self.size_hint = (None,None)
self.size = (150,50)
self.timer_countdown = MDLabel(font_style = 'H1', theme_text_color = 'Primary',
text = str(self.number), halign = 'center',
text_color = (1,1,1,1), size_hint_y = None)
self.add_widget(self.timer_countdown)
def decrement_time(self, dt):
self.number -= 1
# self.timer_countdown.text = str(self.number)
def on_number(self, instance, value):
self.timer_countdown.text = str(value)
def start(self,*args):
self.t = Clock.schedule_interval(self.decrement_time, 1)
def stop(self):
Clock.unschedule(self.t)
A reference to the MDLabel is kept in self.timer_countdown and the on_number() method gets executed whenever number changes, and just updates the MDLabel. Note that you can also do the update by just uncommenting the line:
# self.timer_countdown.text = str(self.number)
In that case, number does not need to be a Property, and the on_number() method is not needed.

Resources