Duplicate a string in Tkinter when redirecting output to a text field - python-3.x

I redirect all my output to the program text field in Tkinter and I wanted to add a date and time to the message:
class StdRedirector(object):
def __init__(self, text_field):
self.text_field = text_field
def write(self, string):
msg_time = datetime.now().strftime('%m-%d %H:%M:%S')
self.text_field.configure(state='normal')
self.text_field.insert('end', f'{msg_time} >> {string}')
self.text_field.see('end')
self.text_field.configure(state='disabled')
class App:
def __init__(self):
self.builder = pygubu.Builder()
self.__init_ui()
self.__init_callbacks()
self.mainwindow.mainloop()
def __init_ui(self):
self.builder.add_from_file(path.join(base_dir, 'assets', 'app.ui'))
self.mainwindow = self.builder.get_object('mainwindow')
self.output_text_field = self.builder.get_object('output_text_field')
sys.stdout = StdRedirector(self.output_text_field)
sys.stderr = StdRedirector(self.output_text_field)
def __init_callbacks(self):
callbacks = {
'update_balance_function': self.__update_balance
}
self.builder.connect_callbacks(callbacks)
def __update_balance(self):
print(True)
But the date line I added is duplicated:
As I understand it, the line is separated by the line separator \n and each substring is sent separately, including line break. Can I fix it somehow?

You can simply check whether the string argument in write() contains any meaningful content, e.g. using ìf string.strip():
class StdRedirector(object):
def __init__(self, text_field):
self.text_field = text_field
def write(self, string):
self.text_field.configure(state='normal')
if string.strip(): # add date before message
msg_time = datetime.now().strftime('%m-%d %H:%M:%S')
self.text_field.insert('end', f'{msg_time} >> {string}')
else: # simply insert string
self.text_field.insert('end', string)
self.text_field.see('end')
self.text_field.configure(state='disabled')

Related

Error handling in python if file written with int instead of string

I have a code that works and have a simple question. If I were to change the text-file from strings to integer I want to error handle that. Is it possible? I only get error-warning when I change the integers to strings however I want it to work for changing strings to integer. For example if I change "Football" into a integer I want to get a error-warning. I then want to create an error-handling to print for example: "Something is wrong inside the textfile"
textfile:
Football # 8-9 # Pitch
Basketball # 9-10 # Gym
Lunch # 11-12 # Home
Reading # 13-14 # Library
from pprint import pprint
class Activity:
def __init__(self, name, start_time, end_time, location):
self.name = str(name)
self.start = int(start_time)
self.end = int(end_time)
self.location = str(location)
def read_file(filename):
activities = []
with open(filename, 'r') as f:
for line in f:
activity, time, location = line.strip().split(' # ')
start, end = time.split('-')
activities.append(Activity(activity, start, end, location))
return activities
activities = read_file('sample.txt')
pprint(activities)
You can check if a string is an integer by using isnumeric.
So let's change the input file to:
Football # 8-9 # Pitch
Basketball # 9-10 # Gym
Lunch # 11-12 # Home
3 # 13-14 # Library
Now we want to verify the name, for which we will write a validator:
class Activity:
def __init__(self, name, start_time, end_time, location):
self.name = self.validate_name(name)
self.start = int(start_time)
self.end = int(end_time)
self.location = location
def validate_name(self, name):
if name.isnumeric():
raise TypeError(f"The name: {name!r}, is not a string, please check your input file.")
return name
def __repr__(self):
return f"<{type(self).__name__}, (name={self.name}, start={self.start}, end={self.end}, loc={self.location})>"
Which results in a TypeError:
line 13, in validate_name
raise TypeError(f"The name: {name!r}, is not a string, please check your input file.")
TypeError: The name: '3', is not a string, please check your input file.
Note that your input is already a string, so there was no need to use str(name) and str(location).
Edit
The above solution only verifies if the entire name is an integer. For a solution that checks if the input is using valid characters we can use the re module in python and the method:
import re
def validate_input(self, name):
regex = re.compile('\d')
if regex.match(name):
raise TypeError(f"The name: {name!r}, contains an integer, please check your input file.")
return name
This will break whenever there is an integer in the input name. The previous solution would continue on inputs such as: Football 3, 3 Football. This solution would raise an error.
You can try out the regex expression for yourself on regex101

Can we skip explicit object creation in Python

When I do not crate object for CP class, the operations are not captured. I am referring to the code below, Can somebody help me understand why we need obj creation in this case
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working")
CP().set_pre("test string added")
CP().execute()
It produces,
++++++++working
0
{0: 'test string added'}
[0]
+++++++not working
0
{}
[]
When you call the class the second time with CP.execute(), you have created a completely new instance of the CP class. It is not going to have the text string you specified.
If you actually wanted it to print the values like the working one you can make the functions return self after each call in the P class. If you did that you could do something like this.
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
##need to return self here
return self
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working: but now working after returning self in the P class")
CP().set_pre("test string added").execute()
++++++++ working
0
{0: 'test string added'}
[0]
+++++++ not working: but now working after returning self in the P class
0
{0: 'test string added'}
[0]
This would print the result you want.
The reason for the difference is the fact that in the first one, you are creating an instance, and using that instance the whole way through, whereas in the second one, you are using two different instances of your class.
The two different instances cannot share their attributes, so you are unable to recall what happened. If you really don't want to use a dedicated variable, change your P class to look like this:
class P(ABC):
...
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
return self
...
And use CP().set_pre("test string added").execute()

how to save user input string in init()

I don't understand how to save the input string and then for in init() to later use it in another function
Class Person:
def __init__ (self):
....
def cCount (self):
num_A = self.count('A')
return num_A
import sys
def main():
inX = input('sequence?')
while inX
myY = Person(inX)
myCnumber = myY.cCount()
print (" {0}".format(myCnumber))
...
I want the output to be the count the number of As in the user input string
You can rearrange your code a little bit like this to achieve what you want:
class Person:
def __init__ (self, sequence):
self.sequence = sequence
def cCount (self):
return self.sequence.count('A')
import sys
def main():
inX = input('sequence?')
while inX:
myY = Person(inX)
myCnumber = myY.cCount()
print (" {0}".format(myCnumber))
inX = input('sequence?')
main()
Result
sequence?ABCDEF
1
sequence?XAAAEWF
3
sequence?
Changes made
Class was replaced with class - lowercase
Init definition added with sequence parameter
Inside init added self.sequence and initialized it
In def cCount, changed to self.sequence.count('A') and returned it
Indented def main's body
Added colon after inX
added inX = input.. at the bottom of while so that while can continue until you just hit Enter

Formatting issue when printing

I have the following Class created:
class Student:
def __init__(self, name, id, email):
self.__name = name
self.__id = id
self.__email_id = email
self.__marks = []
#accessors
def get_name(self):
return self.__name
def get_email(self):
return self.__email_id
def get_id(self):
return self.__id
def get_marks(self):
return self.__marks
#mututators
def set_name(self, name):
self.__name = name
def set_id(self, id):
self.__id = id
def set_email(self, email):
self.__email_id = email
def set__marks(self, marks):
self.__marks = marks
#formatted string representation of the student
def __str__(self):
return "%s: %s, %s, marks: %s" % (self.__id, self.__name, self.__email_id, self.__marks)
#appends the marks to the end of the marks list
def append_marks(self, marks):
self.__marks.append(marks)
When I call this class in the following function:
def read_classlist():
#This function reads the classlist from file.
global studentslist
studentslist = []
try:
file=input("Enter name of the classlist file: ")
with open(file) as f:
for line in f:
data=line.split(",")
s=Student(data[0],data[1],data[2])
studentslist.append(s)
print("Completed reading of file %s" % file)
for student in studentslist:
print(student)
display_separator()
menu()
return
except IOError:
print("File %s could not be opened" % file)
display_separator()
menu()
return
It's not printing the text properly. For some reason, it messes up the formatting for everything but the last student.
Example:
Enter name of the classlist file: classlist.txt
Completed reading of file classlist.txt
N00000001: John, john#amail.com
, marks: []
N00000002: Kelly, kelly#bmail.com
, marks: []
N00000003: Nicky, nicky#cmail.com
, marks: []
N00000004: Sam, sam#dmail.com
, marks: []
N00000005: Adam, adam#amail.com, marks: []
I can't figure out why it's creating a new line after the email. It's doing that for every data file, but the last student being displayed will always be correct.
I don't quite understand it.
Any help would be great!
I think you should change this line
data=line.split(",")
to
data=line.rstrip().split(",")
This'll delete the return to a new line character \n from your data and you'll have your desired output.

can't set attribute in class instance in Python

I'm trying to create a instance of a class to test that the module I created is working properly.
Here is the module (fileWriter.py), the error appears to be in the init method:
class File(object):
'''process the data from a file'''
#fields
#fileName = name of file
#textData = data read from/written to file
#Constructor
def __init__(self, fileName = 'saved_data.txt', textData = ''):
#Attributes
self.fileName = fileName
self.textData = textData
#Properties
#property #getter
def fileName(self):
return self.__fileName
#fileName.setter #setter
def fileName(self, value):
self.__fileName = value
#property #getter
def textData(self, value):
self.__textData = value
#Methods
def saveData(self):
'''appends data to file'''
try:
fileObj = open(self.fileName, 'a')
fileObj.write(self.textData)
fileObj.close()
except Exception as e:
print('You have the following error: ' + str(e))
return('Data successfully saved to file.')
def toString(self):
'''returns text data explicitly'''
return self.fileName + ':' + self.textData
def __str__(self):
'''returns text data implicitly'''
return self.toString()
To test the class, I wrote the following test harness:
import fileWriter
import fileWriter
#test harness
processorObj = fileWriter.File()
processorObj.fileName = 'test.txt'
processorObj.textData = 'testing, 1, 2, 3...'
strMessage = processorObj.saveData()
print(strMessage)
if __name__ == '__main__':
raise Exception('Don\'t run a module by itself!')
When I run the test file, I get the error:
File "testFileWriter.py", line 4, in
processorObj = fileWriter.File()
File "/Users/Haruka/Documents/python_class/Employees/fileWriter.py", line 19, in init
self.textData = textData
AttributeError: can't set attribute
I can't figure out what's wrong with self.textData = textData. Can anybody help?
I'm not sure if you formatted your code after pasting, but there are a few typos:
def __init__(self, file_name = 'saved_data.txt', text_data = ''):
#Attributes
self.__file_name = file_name
self.__text_data = text_data
and
#property #getter
def text_data(self):
return self.__text_data
Later in test, you're also trying to set the text_data property without a setter in your example. You can add to your class:
#textData.setter
def text_data(self, value):
self.__text_data = value
The more pythonic way to do some of the file io stuff is with a context.
def save_data(self):
'''appends data to file'''
with open(self.file_name, 'a') as f:
f.write(self.text_data)
return('Data successfully saved to file.')

Resources