Problem with kivyMD TCPModbus app not reading from the server - kivymd
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 :-)...
Related
Get the status of a checkbox from a Custom List in Kivy/KivyMD
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)
kivyMD HotReloadViewer error: NoneType' object has no attribute 'fbind'
I am trying to run the kivyMD HotReloadViewer script and when I run this code I get an error message inside the HotReloadViewer widget that says: 'NoneType' object has no attribute 'fbind'. I have researched the error and it is explained that somewhere in the code a variable is not defined or a function is returning None. I am wondering if there is something I am missing here, or advice on debugging where undefined variable or function is located. I am running kivymd-0.104.2.dev0 and have watchdog installed. from kivy.lang import Builder from kivymd.app import MDApp KV = ''' #:import KivyLexer kivy.extras.highlight.KivyLexer #:import HotReloadViewer kivymd.utils.hot_reload_viewer.HotReloadViewer BoxLayout: CodeInput: lexer: KivyLexer() style_name: "native" on_text: app.update_kv_file(self.text) size_hint_x: .7 HotReloadViewer: size_hint_x: .3 path: app.path_to_kv_file errors: True errors_text_color: 1, 1, 0, 1 errors_background_color: app.theme_cls.bg_dark ''' class Example(MDApp): path_to_kv_file = "kv_file.kv" def build(self): self.theme_cls.theme_style = "Dark" return Builder.load_string(KV) def update_kv_file(self, text): with open(self.path_to_kv_file, "w") as kv_file: kv_file.write(text) Example().run()
class Example(MDApp): path_to_kv_file = "kv_file.kv" # path to your KV file
Problem in creating forgot-password screen in python using kivy
I'm creating a new application called "MyApp". I am creating a forgot-password-screen where I will ask the user to enter the last password he/she remembers, then a new password and finally to confirm the password. Here is the code and an image of the login-screen and forgot-password-screen: main.py file: from kivy.app import App from kivy.lang import Builder from kivy.uix.screenmanager import ScreenManager, Screen from difflib import get_close_matches Builder.load_file('design.kv') class LoginScreen(Screen): def forgot_pwd(self): self.manager.current = "forgotpwd_screen" class RootWidget(ScreenManager): pass class ForgotPwdScreen(Screen): def reset_pwd(self, lastpwd, newpwd, confirmpwd): npwd = newpwd cnfpwd = confirmpwd lstpwd = lastpwd with open("passwords.txt", 'w+') as file: oldpassword = file.read() if len(get_close_matches(lstpwd, oldpassword)) > 0 : if npwd == cnfpwd: with open("passwords.txt", 'w+') as file: for line in file: file.write(line.replace(oldpassword, npwd)) else: print("Invalid Pwd!") else: print("Invalid Password.") class MainApp(App): def build(self): return RootWidget() if __name__ == "__main__": MainApp().run() design.kv file: <LoginScreen>: GridLayout: cols: 1 GridLayout: cols: 4 rows: 1 Label: text: "MyApp" Button: text: "LOGIN" Button: text: "CLOSE" Label: text: "Version 1.0" GridLayout: cols: 1 rows: 2 Label: text: "User Image" TextInput: hint_text: "Enter Password" GridLayout: cols: 1 Button: text: "Forgot Password" on_press: root.forgot_pwd() <ForgotPwdScreen>: GridLayout: cols:1 GridLayout: cols: 4 rows: 1 Label: text: "MyApp" Button: text: "LOGIN" Button: text: "CLOSE" Label: text: "Version 1.0" GridLayout: rows: 1 Label: text: "FORGOT PASSWORD" GridLayout: cols: 2 rows: 3 Label: text: "Enter last password :" TextInput: id: lastpwd Label: text: "New Password : " TextInput: id: newpwd Label: text: "Confirm New Password :" TextInput: id: confirmpwd GridLayout: rows: 1 cols: 1 Button: text: "DONE" on_press: root.reset_pwd(root.ids.lastpwd.text, root.ids.newpwd.text, root.ids.confirmpwd.text) <RootWidget>: LoginScreen: name: "login_screen" ForgotPwdScreen: name: "forgotpwd_screen" passwords.txt file: admin screen_image: LoginScreen Please click this link to view screenshot of login screen. Forgot Password Screen Please click this link to view screenshot of forgot password screen. Problem: I want that when user gives input in all three inputboxes and clicks done, then the program should use the get_close_matches function to compare the lastpwd(i,e Enter last password) with the text in the passwords.txt file which I have created and I have also given a default password as 'admin' in the txt file. So, If it's len value is greater than 0, then it should check whether npwd(new password) and cnfpwd(confirm password) are same, if they are same, then it should replace the original text in the passwords.txt file with the npwd(newpassword) that user has entered. But when I execute the program and give correct lastpassword and enter new and confirm password and click done, it removes the original password(i.e 'admin') from the passwords.txt file but it doesn't add the new password in the file. I haven't got any errors. Please execute this code to see more clearly what I'm trying to tell. Please help. Thanks.
The problem is that you are opening the passwords.txt file with mode of w+. From the documentation: 'w' open for writing, truncating the file first So, the entire contents of the file are truncated before you try to read it. I suggest modifying the reset_pwd() method to something like: def reset_pwd(self, lastpwd, newpwd, confirmpwd): npwd = newpwd cnfpwd = confirmpwd lstpwd = lastpwd with open("passwords.txt", 'r') as file: oldpassword = file.read() if len(get_close_matches(lstpwd, [oldpassword])) > 0 : if npwd == cnfpwd: with open("passwords.txt", 'w+') as file: file.write(npwd) else: print("Invalid Pwd!") else: print("Invalid Password.") This uses r for the mode when reading the file. Note that the second arg to get_close_matches() must be a list. If you pass a string (like just using oldpassword), each letter in the old password will get compared to lstpwd. Also, logic of comparing passwords does not need to be within the first with open() code block, since you are reading the entire file at once. The above code should work OK for just a single password in the file. But, will require modification to handle a file with more than one password.
Kivy .kv file passing argument
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.
How to dynamically update label texts in kivy that is imported from excel file?
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.