Why does ConfigParserProperty only work for Widgets created with kv in examples below - properties

The following code works as I expect. When I enter a Number in TextInput and press RETURN, the number is shown in all 3 Labels.
#!/usr/bin/python
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ConfigParserProperty
Builder.load_string("""
<MyBoxLayout>
TextInput:
text: "insert Number <RETURN>"
multiline: False
on_text_validate: root.numberprop=self.text
Label:
text: str(root.numberprop)
Label:
text: str(root.numberprop)
Label:
text: str(root.numberprop)
""")
class MyBoxLayout(BoxLayout):
numberprop= ConfigParserProperty(3, 'one', 'two', 'app',
val_type=int, errorvalue=41)
class TstApp(App):
def build_config(self, config):
config.setdefaults('one', {'two' : '70'})
def build(self, **kw):
return MyBoxLayout()
if __name__ == '__main__':
TstApp().run()
The following code does not work as I expect. When I enter a number in Textinput and press RETURN, only the last Label shows the number.
#!/usr/bin/python
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.properties import ConfigParserProperty
def MyConfigParserProperty():
return ConfigParserProperty(3, 'one', 'two', 'app',
val_type=int, errorvalue=41)
Builder.load_string("""
<MyBoxLayout>
TextInput:
text: "insert Number <RETURN>"
multiline: False
on_text_validate: root.numberprop=self.text
<MyLabel>
text: str(root.numberprop)
""")
class MyLabel(Label):
numberprop=MyConfigParserProperty()
class MyBoxLayout(BoxLayout):
numberprop=MyConfigParserProperty()
def __init__(self, **kw):
super(MyBoxLayout, self).__init__(**kw)
for i in range(3):
self.add_widget(MyLabel())
class TstApp(App):
def build_config(self, config):
config.setdefaults('one', {'two' : '70'})
def build(self, **kw):
return MyBoxLayout()
if __name__ == '__main__':
TstApp().run()
I need a way to create Labels dynamically. How can I do it ?

The following code solves the problem.
#!/usr/bin/python
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.properties import ConfigParserProperty
Builder.load_string("""
<MyBoxLayout>
TextInput:
text: "insert Number <RETURN>"
multiline: False
on_text_validate: app.numberprop=self.text
<MyLabel>
text: str(app.numberprop)
""")
class MyLabel(Label):
pass
class MyBoxLayout(BoxLayout):
def __init__(self, **kw):
super(MyBoxLayout, self).__init__(**kw)
for i in range(3):
self.add_widget(MyLabel())
class TstApp(App):
numberprop=ConfigParserProperty(3, 'one', 'two', 'app',
val_type=int, errorvalue=41)
def build_config(self, config):
config.setdefaults('one', {'two' : '70'})
def build(self, **kw):
return MyBoxLayout()
if __name__ == '__main__':
TstApp().run()

Related

unable to update matplotlib figure in pyqt5

I am studying MDIwindow in pyqt5
I want to update the ForcemapWidget figure when I click on the HeatmapWidget figure. However, the diagram did not update.
It is confirmed that the canvas in the ForcemapWidget is updated by the print function. We know that the coordinates are also obtained. But for some reason, the chart doesn't update.
import numpy as np
import matplotlib.pyplot as plt
from PyQt5.QtWidgets import QVBoxLayout,QApplication,QWidget,QMainWindow,QMdiArea,QAction,QMdiSubWindow,QTextEdit, \
QComboBox,QLineEdit,QPushButton,QCheckBox,QFormLayout
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
class MDIWindow(QMainWindow):
count=0
def __init__(self):
super ().__init__()
self.mdi=QMdiArea()
self.setCentralWidget(self.mdi)
bar=self.menuBar()
file=bar.addMenu("File")
file.addAction("New")
file.addAction("Cascade")
file.addAction("Tiled")
file.triggered[QAction].connect(self.tritri)
self.setWindowTitle("MDI Application")
# 最初のサブウィンドウを作成
sub=QMdiSubWindow()
form_widget=FormWidget()
sub.setWidget(form_widget)
sub.setWindowTitle("Sub Window 1")
self.mdi.addSubWindow(sub)
sub.show()
sub2=QMdiSubWindow()
# ForcemapWidget.force_update()
form_widget=ForcemapWidget()
# form_widget.update()
# print(form_widget)
sub2.setWidget (form_widget)
sub2.setWindowTitle("Sub Window 2")
self.mdi.addSubWindow(sub2)
sub2.show()
def tritri(self,p):
if p.text()=="New":
MDIWindow.count=MDIWindow.count+1
sub=QMdiSubWindow()
heatmap_widget=HeatmapWidget()
sub.setWidget(heatmap_widget)
sub.setWindowTitle("Sub Window"+str(MDIWindow.count))
self.mdi.addSubWindow(sub)
sub.show()
if p.text()=="Cascade":
self.mdi.cascadeSubWindows()
if p.text()=="Tiled":
self.mdi.tileSubWindows ()
class HeatmapWidget(QWidget):
def __init__(self):
super().__init__()
self.forcemapWidget=ForcemapWidget
fig,ax=plt.subplots (figsize = (1,1))
heatmap=np.random.rand (256,256)
im=ax.imshow(heatmap,cmap = "hot")
fig.colorbar(im)
ax.set_title("Heatmap")
fig.canvas.mpl_connect("button_press_event",self.onclick)
canvas=FigureCanvas(fig)
layout=QVBoxLayout()
layout.addWidget(canvas)
self.setLayout (layout)
def onclick(self,event):
x,y=event.xdata,event.ydata
forcemap=ForcemapWidget(x)
class ForcemapWidget (QWidget):
def __init__(self,x=None):
super().__init__()
self.figure=plt.figure()
self.initUI(x)
def initUI(self,x):
self.figure.clear()
ax=self.figure.add_subplot(111)
if x is not None:
heatmap=np.random.rand(int (x),256)
else:
heatmap=np.random.rand(256,256)
im=ax.imshow(heatmap,cmap = "hot")
# self.figure.colorbar (im)
canvas=FigureCanvas(self.figure)
layout=QVBoxLayout()
layout.addWidget(canvas)
self.setLayout(layout)
self.update()
class FormWidget(QWidget):
def __init__(self):
super().__init__()
form_layout = QFormLayout()
combo = QComboBox()
combo.addItems(["Option 1", "Option 2", "Option 3"])
form_layout.addRow("Dropdown:", combo)
line_edit = QLineEdit()
form_layout.addRow("Input:", line_edit)
button = QPushButton("Submit")
form_layout.addRow(button)
check_box = QCheckBox()
form_layout.addRow("Checkbox:", check_box)
self.setLayout(form_layout)
app = QApplication(sys.argv)
mdi = MDIWindow()
mdi.show()
app.exec_()

Problem with changing the value of main button in KivyMD Menu

I have created a KivyMD Menu. When I click on the main Button, the menu is opening without a problem. However when I click on a menu button the value of the main button is not changing. Nothing happens. I thought the code is sufficient to achieve it. Does anyone know a solution? Thank you in advance!
py file:
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.menu import MDDropdownMenu
Window.size = (400, 800)
class homescreen(Screen):
pass
GUI = Builder.load_file("main.kv")
class MainApp(MDApp, homescreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.homescreen = Builder.load_string("homescreen")
menu_items = [{"icon": "git", "text": f"Item {i}"} for i in range(5)]
self.menu = MDDropdownMenu(
caller=self.ids.drop_item,
items=menu_items,
#position="center",
width_mult=4,
)
self.menu.bind(on_release=self.set_item)
def set_item(self, instance_menu, instance_menu_item):
self.ids.drop_item.set_item(instance_menu_item.text)
self.menu.dismiss()
def build(self):
return self.homescreen
if __name__ == "__main__":
MainApp().run()
main.kv:
<homescreen>:
MDDropDownItem:
id: drop_item
pos_hint: {'center_x': .5, 'center_y': .5}
text: "Select"
on_release: app.menu.open()
Here is your improved code
py file:
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.menu import MDDropdownMenu
Window.size = (400, 800)
class homescreen(Screen):
pass
GUI = Builder.load_file("main.kv")
class MainApp(homescreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.homescreen = Builder.load_string("homescreen")
menu_items = [{"icon": "git", "text": f"Item {i}"} for i in range(5)]
self.menu = MDDropdownMenu(
caller=self.ids.drop_item,
items=menu_items,
callback=self.set_item,
width_mult=4,
)
def set_item(self, instance_menu_item):
self.ids.drop_item.text = instance_menu_item.text
self.menu.dismiss()
def build(self):
return self.homescreen
class app(MDApp):
def build(self):
return MainApp()
if __name__ == "__main__":
app().run()
main.ky
<homescreen>:
MDDropDownItem:
id: drop_item
pos_hint: {'center_x': .5, 'center_y': .5}
text: "Select"
on_release: root.menu.open()

how to transmit true value to formal parameter in decorator function(pyqtSlot())?

first, see code below:
import sys
from PyQt5.QtCore import (Qt, pyqtSignal, pyqtSlot)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
QVBoxLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def printLabel(self, str):
print(str)
#pyqtSlot(int)
def on_sld_valueChanged(self, value):
self.lcd.display(value)
self.printLabel(value)
def initUI(self):
self.lcd = QLCDNumber(self)
self.sld = QSlider(Qt.Horizontal, self)
vbox = QVBoxLayout()
vbox.addWidget(self.lcd)
vbox.addWidget(self.sld)
self.setLayout(vbox)
self.sld.valueChanged.connect(self.on_sld_valueChanged)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Signal & slot')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I'm a little puzzled about how the true value in sld is transmitted to the formal parameter 'value' in the slot function : def sld_valChanged(self, value).
Because i can't see something like this: self.sld.valueChanged.connect(partial(self.sld_valChanged, self.sld.value))
Could someone explain that?

KIVY python: Slider inside ScrollView

I created a scroll view in which i put some labels and 2 sliders.
The scroll works perfectly, but I can't change the slider's value with my mouse...
Please run this code and see:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.slider import Slider
from kivy.uix.textinput import TextInput
class Home(BoxLayout):
def __init__(self, **kwargs):
super(Home, self).__init__(**kwargs)
self.layout = GridLayout(cols=1, padding=5, spacing=20, size_hint=(1, None))
self.layout.bind(minimum_height=self.layout.setter('height'))
for i in range(50):
if i%25==0:
btn = Slider(min=1, max=10, value=4)
else:
btn = Label(text=str(i), color=(0,0,0,1), size=(32, 32), size_hint=(1, None))
self.layout.add_widget(btn)
self.scrll = ScrollView(size_hint=(1, .6), pos_hint={'center_x': .5, 'center_y': .5}, do_scroll_x=False)
self.scrll.add_widget(self.layout)
self.add_widget(self.scrll)
class MyAppli(App):
def build(self):
Window.clearcolor = (1,1,1,1)
return Home()
if __name__ == '__main__':
MyAppli().run()
Okay when you work with slider you shall redefine the on_touch_down, on_touch_up and on_touch_move method to handle those events:
-main.py :
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.properties import NumericProperty
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.slider import Slider
class Home(BoxLayout):
def __init__(self, **kwargs):
super(Home, self).__init__(**kwargs)
self.b = []
self.layout = GridLayout(cols=1, padding=5, spacing=20, size_hint=(1, None))
self.layout.bind(minimum_height=self.layout.setter('height'))
for i in range(50):
if i % 25 == 0:
self.b.append(MySlider(min=1, max=10, value=4, height=32, size_hint=(1, None)))
else:
self.b.append(Label(text=str(i), color=(0,0,0,1), height=32, size_hint=(1, None)))
self.layout.add_widget(self.b[i])
self.scrll = ScrollView(size_hint=(1, .6), pos_hint={'center_x': .5, 'center_y': .5}, do_scroll_x=False)
self.scrll.add_widget(self.layout)
self.add_widget(self.scrll)
def update(self, *args):
for i in range(50):
if i % 25 == 0:
self.b[i].begin = self.b[i].pos[0]
self.b[i].len = self.b[i].size[0]
class MySlider(Slider):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
super(MySlider, self).on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
super(MySlider, self).on_touch_up(touch)
def on_touch_move(self, touch):
if self.collide_point(*touch.pos):
super(MySlider, self).on_touch_move(touch)
class MyAppli(App):
def build(self):
Window.clearcolor = (1,1,1,1)
return Home()
if __name__ == '__main__':
MyAppli().run()
-some outputs :
I hope this helps !

Kivy Scrollview in kv language - id not defined

Trying to figure out how to implement a straight-forward ScrollView in KV language based on the examples given in the documentation. I cannot believe I cannot find a single example of this (only parts of solution), so I thought it would be easy. Turns out it's not.
My issue is that I need to populate my scrollable grid layout with a list of labels from within the kivy script, using add_widget. That's because I have a variable number of of labels to add (although that number is fixed in the below example to make things simple). However the program wouldn't let me do it, saying that the ID I defined for the grid layout object is not defined. Therefore I am not able to add the labels to the grid layout.
NameError: name '_gridlayout' is not defined
Any help appreciated. Thanks
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<MainScreen>:
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
size_hint: 1, .2
Label:
text: "Random text"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
size_hint: 1, .8
ScrollView:
GridLayout:
id: _gridlayout
cols: 1
padding: 10
spacing: 10
size_hint_y: None
width: 500
''')
class MainScreen(FloatLayout):
def __init__(self, **kwargs):
self.buildList()
super(MainScreen, self).__init__(**kwargs)
def buildList(self):
for i in range(30):
btn = Label(text=str(i), size_hint_y=None, height=40)
_gridlayout.add_widget(btn) # <- ERROR
class SMApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
SMApp().run()
UPDATE: Corrected script below.
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<MainScreen>:
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
size_hint: 1, .2
Label:
text: "Random text"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
size_hint: 1, .8
ScrollView:
GridLayout:
id: _gridlayout
cols: 1
padding: 10
spacing: 10
size_hint_y: None
width: 500
''')
class MainScreen(FloatLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.buildList()
def buildList(self):
for i in range(30):
btn = Label(text=str(i), size_hint_y=None, height=40)
self.ids._gridlayout.add_widget(btn)
self.ids._gridlayout.bind(minimum_height=self.ids._gridlayout.setter('height'))
class SMApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
SMApp().run()
Kivy is very picky about calling Widget.__init__() first. So if you overwrite __init__ of a widget be sure to first call super().__init__, otherwise you can get errors like you encountered or "random" crashes.
To fix change
class MainScreen(FloatLayout):
def __init__(self, **kwargs):
self.buildList()
super(MainScreen, self).__init__(**kwargs)
to
class MainScreen(FloatLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.buildList()