I created an app, based on a teacher's videoclass program. It turned out I had some issues, returning me the following exceptions and Id badly apreciate your attention:
Error during data status reading.
Error during data setpoint reading.
Error during data tempOven reading.
Error during data tempOven reading.
Error during data tempOven reading.
I know it's a theoric script, but I'd appreciate If u guys could run in your computers, because I hand-copied the teacher's scripts but It simply doesnt work althought it has no errors. As long as I post here the codes, Ill keep writting here its overviews and the files will be 3 (the kivyMD .kv, the main.py and the datacards). The app appearence is like that:
The issues trigger exactly on the page below (data acquisition) because theonnection step is succesful.
Starting with the main, this code instantiates the MDApp and sequentially the Screen. It has a button, as seen on the picture1, for connecting. The connecting method himself instantiates the clock scheduler for eventual updates on the holding card and on the coil card.
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from datacard import CardCoil, CardHoldingRegister, CardInputRegister
from pyModbusTCP.client import ModbusClient
from kivymd.uix.snackbar import Snackbar
from kivy.clock import Clock
class MyWidget(MDScreen):
"""
Contructor
"""
def __init__(self,tags,**kwargs):
self._clientMOD= ModbusClient()
super().__init__(**kwargs)
self._tags=tags
self._ev=[] #pt4 min 58
for tag in self._tags:
if tag["type"]=="input":
self.ids.modbus_data.add_widget(CardInputRegister(tag,self._clientMOD))
elif tag["type"]== "holding":
self.ids.modbus_data.add_widget(CardHoldingRegister(tag,self._clientMOD))
elif tag["type"]=="coil":
self.ids.modbus_data.add_widget(CardCoil(tag,self._clientMOD))
def connect(self):
if self.ids.bt_con.text== "CONNECT":
try:
self.ids.bt_con.text= "DISCONNECT"
self._clientMOD.host= self.ids.hostname.text
self._clientMOD.port = int(self.ids.port.text)
self._clientMOD.open()
Snackbar(text="Connected succesfully",bg_color=(0,1,0,1)).open()
self._ev=[]
for card in self.ids.modbus_data.children:
if card.tag['type'] == "holding" or card.tag['type']== "coil":
self._ev.append(Clock.schedule_once(card.update_card))
else:
self._ev.append(Clock.schedule_interval(card.update_card,1))
except Exception as e:
print("Erro de conexao com servidor: ", e.args)
else:
self.ids.bt_con.text="CONNECT"
for event in self._ev:
event.cancel()
self._clientMOD.close()
Snackbar(text="Disconnected",bg_color=(1,0,0,1)).open()
class BasicApp(MDApp):
__tags= [{'name':'tempOven','description':'Oven Tempture','unit':'ºC','address':1000,'type':"input"},
{'name':'setpoint','description':'Desired Tempture','unit':'ºC','address':2000,'type': "holding"},
{'name':'status','description':'Actuator state','address':1000,'type':"coil"},
]
def build(self):
self.theme_cls.primary_palette= "Orange"
self.theme_cls.primary_hue= "700"
self.theme_cls.accent_palette="Orange"
return MyWidget(self.__tags)
if __name__=='__main__':
BasicApp().run()
The second file and next step is the datacard module. Its goal is to define 2 classes the MDcard one, and the 3 different MDcard children. Actually the 3 of them are repreenting the methodology differences between Mbus functions Holding, Input or Coil. Their goil is to interface with the server:
from kivymd.uix.card import MDCard,MDCard
from pyModbusTCP.client import ModbusClient
class DataCard(MDCard):
title= "Data Card"
def __init__(self,tag,mbusClient,**kwargs):
self.tag= tag
self.title=self.tag['description']
self._mbusclient = mbusClient
super().__init__(**kwargs)
def update_card(self,dt):
try:
if self._mbusclient.is_open():
self.set_data(self._read_data(self.tag['address'],1)[0])
except Exception as e:
print("Error during data",self.tag['name'] ,"reading.")
def write_card(self):
try:
if self._mbusclient.is_open():
self._write_data_fcn(self.tag['address'],self._get_data())
except Exception as e:
print("Error during data" ,self.tag['name'] ,"writting.")
class CardHoldingRegister(DataCard):
def __init__(self,tag,mbusClient,**kwargs):
super().__init__(tag,mbusClient,**kwargs)
self._read_data=self._mbusclient.read_holding_registers
self._write_data_fcn= self._mbusclient.write_single_register
def set_data(self, data):
self.ids.textfield.text= str(data)
def get_data(self):
return int(self.ids.textfield.text)
class CardInputRegister(DataCard):
def __init__(self,tag,mbusClient,**kwargs):
super().__init__(tag,mbusClient,**kwargs)
self._read_data= self._mbusclient.read_input_registers
def set_data(self, data):
self.ids.label.text= str(data)
class CardCoil(DataCard):
def __init__(self,tag,mbusClient,**kwargs):
super().__init__(tag,mbusClient,**kwargs)
self._read_data= self._mbusclient.read_coils
self._write_data_fcn= self._mbusclient.write_single_coil
def set_data(self, data):
self.ids.switch.active= data #True / False
def get_data(self):
return self.ids.switch.active #RETURN = ajuste
At last, we have the kivy .kv module, it diallogues with the datacrd module (e.g. when u want to show on the MDcard boxes the data exchanges between server and client) and with the main one (e.g connection and MD)
#:kivy 1.11.1
<MyWidget>:
MDBoxLayout:
orientation:'vertical'
MDTopAppBar:
title: "Informatica industrial"
MDBottomNavigation:
panel_color: app.theme_cls.accent_color
text_color_normal:0.4,0.4,0.4,1
text_color_active:0.8,0.8,0.8,1
MDBottomNavigationItem:
name:"config"
text:"CONFIGURATION"
icon: "cog"
MDBoxLayout:
orientation: 'vertical'
padding: "20p"
spacing: "50dp"
Image:
source:"imgs/modbus.png"
pos_hint: {"center_x":0.5,"center_y":0.5}
size_hint: {1,0.2}
MDTextField:
id:hostname
text:"127.0.0.1"
hint_text: "IP Address"
size_hint: {0.3, None}
height: "60dp" #tentatativa e erro
pos_hint: {"center_x":0.5,"center_y":0.5}
MDTextField:
id:port
text:"502"
hint_text: "Port"
size_hint: {0.3, None}
height: "60dp"
pos_hint: {"center_x":0.5,"center_y":0.4}
MDRoundFlatIconButton:
id: bt_con
text:"CONNECT"
icon:'connection'
pos_hint:{"center_x":0.5,"center_y":0.3}
on_release: root.connect()
MDBottomNavigationItem:
name:"data"
text:"DADOS"
icon:"chart-donut"
ScrollView:
size_hint: (1,None)
size: 800,600 #define o tamanho como o da janela 800x600
bar_pos_y: 'left'
bard_width: 20
effect_cls: 'ScrollEffect'
MDStackLayout:
id: modbus_data
size_hint: (1, None)
padding: 0.05*600,"150dp"
spacing: (800/5 - 2*0.05*800)/3
adaptive_height: True
<DataCard>:
orientation: 'vertical'
padding: '10dp'
size_hint:None,None
size: 600/5 , "90dp"
pos_hint:{"center_x": 0.5, "center_y":0.5}
MDLabel:
text: root.title
size_hint_y: None
height:self.texture_size[1]
pos_hint: {'top':1}
MDSeparator:
height: "1dp"
<CardHoldingRegister>:
MDTextField:
id:textfield
helper_text: "Pressione Enter para enviar os dados"
helper_text_mode:'persistent'
multiline: False
on_text_validate: root.write_card
<CardInputRegister>:
MDLabel:
id:label
<CardCoil>:
MDSwitch:
id: switch
Im sorry if the question went too long, but that's it. If u prefer, I leave here the Youtube playlist which I'm basing on (https://www.youtube.com/watch?v=DqO-KJXv6UE&list=PLDBnf2G73PkBqYVoxUoGQe7htYYE4fIsX&index=20). The teacher also shared the server module and It can be downloaded on the Youtube link above (on video descriptions). I appreciate any kind of tips and I thank u all beforehand.
As per the docs ModbusClient.is_open is a boolean (not a function). This means that attempting to use it as a function if self._mbusclient.is_open() will trigger the exception you are seeing.
To fix replace if self._mbusclient.is_open() with if self._mbusclient.is_open (i.e. remove the (). I'm not guaranteeing that is the only issue :-)...
I'm making a GUI program that there are two buttons. When I click the first button, I want to generating a random code. When I click the second button, I want to see a list of the codes that generated from the first button. (For now, just to print the codes-list, into the 'Run' window.) My script is the following:
class Ui_Main_Window(object):
# For the first button:
def Code_maker(self):
import random
Codes_list = []
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$%^&*`#/\?[]{}-_~"
for x in range(1):
password = ""
for y in range(0, 10):
password_char = random.choice(chars)
password += password_char
print(password)
Codes_list.append(password)
# For the second button:
def show_list(self):
print (Codes_list)
# To connect the actions with the buttons:
def setupUi(self, Main_Window):
# After some code...
self.Button_New_code.clicked.connect(self.Code_maker)
# After some code...
self.Button_Show_list.clicked.connect(self.show_list)
# The continuation of the program...
My problem is that when I click the first button, the code genarating successfully and it printing into the 'Run' window. But when I click the second button the program crashes and after some seconds, it closes. I get this error:
> Unresolved reference 'Codes_list'.
(I use Pycharm and the error doesn't showing into the 'Run' window, but into the 'Problems' window.)
I tried to repair it with a global list, but the program works worse. It crashes when I click anything of the two buttons... If I write code with this way, it becomes:
class Ui_Main_Window(object):
Codes_list = []
# For the first button:
def Code_maker(self):
import random
global Codes_list
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$%^&*`#/\?[]{}-_~"
for x in range(1):
password = ""
for y in range(0, 10):
password_char = random.choice(chars)
password += password_char
print(password)
Codes_list.append(password)
# For the second button:
def show_list(self):
print (Codes_list)
# To connect the actions with the buttons:
def setupUi(self, Main_Window):
# After some code...
self.Button_New_code.clicked.connect(self.Code_maker)
# After some code...
self.Button_Show_list.clicked.connect(self.show_list)
I searched about my problem in Google, but I tried so many things, that I don't remember what I tried. Nothing worked.
This should work. I have added comments on the lines I have added or modified for your reference. Can you please check?
class Ui_Main_Window(object):
def __init__(self):
# added the constructor to attach the list with the object itself
self.Codes_list = [] #this will contain the numbers
# For the first button:
def Code_maker(self):
import random
#Codes_list = [] # Removed
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$%^&*`#/\?[]{}-_~"
for x in range(1):
password = ""
for y in range(0, 10):
password_char = random.choice(chars)
password += password_char
print(password)
self.Codes_list.append(password) # modified to update the object attribute
# For the second button:
def show_list(self):
print (self.Codes_list) # modified to access the object attribute
# To connect the actions with the buttons:
def setupUi(self, Main_Window):
# After some code...
self.Button_New_code.clicked.connect(self.Code_maker)
# After some code...
self.Button_Show_list.clicked.connect(self.show_list)
# The continuation of the program...
I'm trying a custom List item based on the KivyMD's documentation examples, but I'm having a problem identifying which of my chechkboxes has been activated.
With * args I can access its state and which object it is but it prints like this:
{<__ main __. RightCheckbox object at 0x000001B1A62E6970>, False}.
The problem is that "0x000001B1A62E6970" is not a constant value (this value may change with the execution of the code) that represents the checkbox as its id does.
My minimal code KV:
KV = '''
MDCard:
orientation : 'vertical'
size_hint : (0.8,0.3)
pos_hint : {"center_x":.5,"center_y":.5}
elevation : 15
padding : 20
spacing : 30
id: box
MDList:
id: scroll
<ListItemWithCheckbox>:
IconLeftWidget:
icon: root.icon
RightCheckbox:
'''
And the MainApp and Custom Class definitions:
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivymd.uix.list import IRightBodyTouch, OneLineAvatarIconListItem
from kivymd.uix.selectioncontrol import MDCheckbox
from kivymd.icon_definitions import md_icons
class ListItemWithCheckbox(OneLineAvatarIconListItem):
'''Custom list item.'''
icon = StringProperty("android")
def on_press(self):
print(self.text)
class RightCheckbox(IRightBodyTouch, MDCheckbox):
'''Custom right container.'''
def on_active(self, *args):
print(args)
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
icons = list(md_icons.keys())
for i in range(5):
self.root.ids.scroll.add_widget(
ListItemWithCheckbox(text=f"Item {i}", icon=icons[i])
)
MainApp().run()
What I have tried:
I have tried to give a default id for the RightCheckbox to later change it to a unique one when putting the widgets in the list and thus access their states in something like "root.ids.checkboxid" but I don't know how to put it when doing self.root.ids.scroll.add_widget (ListItemWithCheckbox (text = f "Item {i}", icon = icons [i]))
Also in the on_active method (when any checkbox is selected) of the RightCheckbox class I have tried to print various attributes such as MDCheckBox.active .icon .ids. .text but none of them prints anything to help me identify which specific checkbox has been selected
I would really appreciate if anyone can help Thanks
One way to do this is to create a reference to the ListItemWithCheckbox within the RightCheckbox like this:
<ListItemWithCheckbox>:
IconLeftWidget:
icon: root.icon
RightCheckbox:
listItem: root
Then your on_active() method can use that reference:
class RightCheckbox(IRightBodyTouch, MDCheckbox):
'''Custom right container.'''
def on_active(self, rcb, value):
print(rcb.listItem.text, 'is', value)
OK so what I want to do is have a FloorWidget with a option to add rooms dynamically with a press of a button while the app is running. I want to load specific graphic based on the room number and I don't know how to achive that in .kv file
<RoomWidget>:
id: room_widget
room_num: # would be great if you could pass this as an argument
Image:
source: os.path.join(GRAPHICS_DIR_PATH, "room" + str(room_widget.room_num))
Is there a way to do this? Could I somehow initilize room_num in python file by passing it into the RoomWidget constructor and access it in .kv file?
class RoomWidget(Screen, Widget):
def __init__(self, room_num, **kwargs):
super().__init__(**kwargs)
self.room_num = room_num
<RoomWidget>:
size_hint: (.2, .35)
source: os.path.join(GRAPHICS_DIR_PATH, "room" + str(self.room_num))
Image:
source: source
If I do sth like this I get an error: AttributeError: 'RoomWidget' object has no attribute 'room_num'.
You can do that by making a property of RoomWidget Like this:
class RoomWidget(Screen):
room_num = NumericProperty()
def __init__(self, room_num, **kwargs):
super().__init__(**kwargs)
self.room_num = room_num
Then, in your 'kv':
#:import os os
#:set GRAPHICS_DIR_PATH '.'
<RoomWidget>:
size_hint: (.2, .35)
source: os.path.join(GRAPHICS_DIR_PATH, "room" + str(self.room_num) + '.png')
Image:
source: root.source
Note that you do not need to extend both Screen and Widget, because Screen is a Widget.
I am creating a questionnaire form in kivy. I have added few label widgets in my GUI. I don't want to define label texts statically in my code, instead my objective is to dynamically update label texts that is fetched from an excel file.
For example: my excel file has 2 questions:
Name of the company?
Department?
I have 2 label widgets in my GUI, and the text of widgets should be:
Name of the company?
Department?
respectively and has to be dynamically fetched from the excel file.
I encountered an error when i tried to run my code.
Questionnaire.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
import pandas as pd
class FetchData():
file = pd.read_excel("Questionnaire.xlsx")
Quest = file['QUESTIONS']
class Questions(Widget):
Data = FetchData().Quest
qvars =[]
company = ObjectProperty(None)
department = ObjectProperty(None)
qvars.append(company)
qvars.append(department)
def validate(self):
for i in range(len(self.qvars)):
self.qvars[i].text = self.Data[i]
class QuestionnaireApp(App):
def build(self):
return Questions()
if __name__=="__main__":
QuestionnaireApp().run()
Questionnaire.kv
<Questions>:
company:company
department:department
GridLayout:
cols:1
size:root.width, root.height
GridLayout:
cols:1
Label:
id:company
TextInput:
Label:
id:department
TextInput:
Button:
text:"process"
on_release: root.validate()
I am getting the following error:
File "C:/Users/pavan m sunder/virtual environments/android/Questionnaire.py", line 23, in validate
self.qvars[i].text = self.Data[i]
AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'
I referred to similar questions that had the same error but none matches specifically to my problem.
Using your qvars list messes things up because it's a list of the property objects, which doesn't have the right behaviour - Kivy Properties are descriptors, they only work at class level.
Instead, just access self.company or self.department in your methods.