Read labels separately from a function that generates a set of buttons - python

I have the following development that I am working on with the Tkinter, ElementTree and Pandas modules in Python:
from tkinter import *
import xml.etree.ElementTree as ET
import pandas as pd
file_xml = ET.parse('example1.xml')
rootXML = file_xml.getroot()
root = Tk()
root.title("Graphical Analysis of Signals of a XML")
root.geometry("1024x820")
root.pack_propagate(False)
root.config(bd=15)
# Functions to open xml file
def open_file():
try:
global temxml, xml_Name
xml_Name = str(easygui.fileopenbox(title='Select xml file', default='*.xml'))
if str(os.path.abspath(xml_Name)) != os.path.join(os.path.abspath(os.getcwd()), os.path.basename(xml_Name)):
menssage.set("Opened xml file: ", xml_Name)
child_4_separate(os.path.basename(str(tempxml)))
else:
child_4_separate(os.path.basename(str(xml_Name)))
except FileNotFoundError:
print('XML file was not loaded.')
# Function to display buttons and choose the Child_4 to be plotted
def child_4_separate(xml_Name):
print("File: ", xml_Name)
file_xml = ET.parse(xml_Name)
data_xml = [
{
"Name": signal.attrib["Name"],
"Id": signal.attrib["Id"],
} for signal in file_xml.findall(".//Child_4")
]
# print(data_xml)
for i in data_xml:
print(i)
id_tc = i.get('Id')
dict_tc = str(i.values()).replace('dict_values([\'', '')
name_tc = dict_tc.replace('\'])', '')
Button(root, text=f"TC> {name_tc}", command=transfor_data_atri_child_4(xml_Name, id_tc)).pack()
# Function to transform xml file to DataFrame
def transfor_data_atri_child_4(rootXML, id_tc):
print("File: ", rootXML)
print("id_tc: ", id_tc)
What I'm trying to do is that every time I click a button, the child_4_separate (xml_Name) function it goes to the transform_data_atri_child_4 (rootXML, id_tc) function with a single id number and does not fetch me everything like it does in the last print that I show below, this is to be able to manipulate them separately.
I share the XML file in this link example1.xml because of how long it is.
I don't know if I need another for inside or what I need, because when trying another for inside the already existing for in the child_4_separate (xml_Name) function it is repeating it many times and it is not what I want, but simply that redirect to the following function with the two parameters that I am indicating, but separately; help me please! Beforehand, thank you very much!

Just for possible searches or related problems later, I share the solution:
Take the value command = lambda x = xml_Name, y = id_tc: transform_data_atri_child_4 (x, y) in the button attributes and it worked, my function is like this:
# Function to display buttons and choose the Child_4 to be plotted
def child_4_separate(xml_Name):
# print("File: ", xml_Name)
file_xml = ET.parse(xml_Name)
data_xml = [
{
"Name": signal.attrib["Name"],
"Id": signal.attrib["Id"],
} for signal in file_xml.findall(".//Child_4")
]
# print(data_xml)
for i in data_xml:
id_tc = i.get('Id')
dict_tc = str(i.values()).replace('dict_values([\'', '')
name_tc = dict_tc.replace('\'])', '')
Button(root, text=f"TC> {name_tc}", command=lambda x=xml_Name, y=id_tc: transfor_data_atri_child_4(x, y)).pack()
I appreciate the solution to #Sujay. Happy codification everyone!!

Related

Print a sentence with placeholder and function from an external file. Problem with importing forms, using and retrieving content

I made a program that prints some text in a sentence with two placeholders. When the user selects a tournament name from the left-hand combobox and a team name from the right-hand combobox, a template string is filled in with those values and written in the bottom text field. It looks like:
I was able to get this working with all the code in a single file. Now I am trying to organize the code into three separate Python files, thus:
Main.py: Contains the main code; accesses the template string from Template.py
Template.py: Contains the template string that will be filled out; calls the getArticle function from Grammar.py and also gets the team name from Main.py (by checking the combobox contents)
Grammar.py: provides getArticle, which uses dictionaries from Main.py to determine the article that should appear before the team name
PROBLEM Now when I start the program, I get the error:
AttributeError: partially initialized module 'Template' has no attribute 'test_template' (most likely due to a circular import).
I tried fixing this by moving an import into a function to defer it; but then subject = team.get() still does not work in Template.py.
HOW DOES MY CODE WORK? I select the element in the first combobox (for example Serie A), then I select the element of the second combobox (for example Atalanta). Then I click on Print. When I click on Print the purpose is to print test_template = f "Bla bla bla {article} {subject}". Subject is the second combobox (for example Atalanta). Article is selected by means of the GetArticle function and dictionaries. This function is used to understand which article to print in relation to the subject. When I had all the code in one file, it worked fine.
How can I print test_template in Main.py, while altering the code structure as little as possible? I specifically want to keep using three files, with test_template being located in Template.py and getArticle being in Grammar.py.
NOTE 1: Possibly, I don't need the sentence to be the return of a function, because my code is an example, but I will have to use hundreds of sentences, so I need a lot of variables (such as example test_template in my code). I would like to use test_template1 as I did in my code. If you really need the function, then know that in the future I will have to use many "random" variables (test_template1, test_template2, test_template3, etc.) and so I don't want to use the return only directly in the return row.
I would need a return of test_template (+ e.g. test_template2, test_template3, etc.) that is compatible with using "random" in Main.py, so for example have in Main: random_template_test = random.choice ([test_template, test_template2 , test_template3]).
NOTE 2: Also I need to pass {article} {subject} without distorting the reason too much (obviously modifying a little, but not too much), because then in the future I should also print other placeholders taking them from Main.py and Grammar.py.
For reference, here is the complete code:
Template.py
import Grammar
import Main
test_template = f"Bla bla bla {article} {subject}"
#Subject is created in the Main.py file
#While Article is created in the Grammar.py file
subject = team.get()
article = getArticle(subject)
Grammar.py
import Main
from Main import teams
from Main import article_words
from Main import nouns
#Subject is created in the Main.py file
def getArticle(subject):
for key, value in article_words.items():
if nouns[subject] == value:
return key
Main.py
from tkinter import ttk
import tkinter as tk
from tkinter import *
import Template
root = tk.Tk()
root.geometry("400x150")
tournament=ttk.Combobox(root, width = 18)
tournament.place(x=15, y=15)
tournament['value'] = ["Serie A", "Serie B"]
tournament.set("Tournament")
def on_tournament_selected(event):
# Clear the entry boxes: aggiunto io
team.delete(0,'end')
req_teams = [] # For all the required teams
sel_tournament = tournament.get() # Get the tournament
# get the names for selected gender
for _team in teams.items(): # Go through all the teams in the dictionary
key = _team[0] # Get the key
value = _team[1] # Get the value
if value['Tournament'] == sel_tournament: # If Tournament of the loop-ed team is our selected tourname, then
req_teams.append(key)
team.config(values=req_teams) # Change the values of the combobox
tournament.bind('<<ComboboxSelected>>', on_tournament_selected)
#############
team=ttk.Combobox(root, width = 18)
team.place(x=200, y=15)
team.set("Teams")
text = tk.Text(root,width=43,height=2)
text.place(x=15, y=50)
def printTeam():
#here first there was subject and article, then moved to another external file
def articolo1():
template1 = Template.test_template
text.insert(tk.END, template1)
articolo1()
button2 = Button(root, text="Print", command = printTeam)
button2.pack()
button2.place(x=15, y=100)
#DICT
teams = {
"Atalanta": {
"Nome Squadra": "Atalanta",
"Tournament": "Serie A",
},
"Bologna": {
"Nome Squadra": "Bologna",
"Tournament": "Serie A",
}
}
nouns = {
"Atalanta": {"genere" : "femminile", "unità" : "singolare", "apostrofo" : "si"},
"Bologna": {"genere" : "maschile", "unità" : "singolare", "apostrofo" : "no"},
}
article_words = {
"del" : {
"genere" : "maschile",
"unità" : "singolare",
"apostrofo": "no"
},
"dell'" : {
"genere" : "femminile",
"unità" : "singolare",
"apostrofo" : "si"
},
}
root.mainloop()
Move the dictionaries in dicts.py and create a class in template.py to return required template.You can create as many templates as you want in templates.py.
dicts.py
teams = {...}
nouns = {...}
article_words = {...}
template.py
from grammer import getArticle
import random
class Template():
def __init__(self,**kwargs):
self.__dict__.update(kwargs)
# must define other variables like article which are used
# in templates irrespective of the arguments passed
if self.subject:
self.article = getArticle(subject=self.subject)
def return_template(self):
test_template_list = ["Bla bla bla {subject} {article}", "Again bla bla bla {subject}"]
test_template = random.choice(test_template_list)
place_holders = ['{subject}','{article}', ]
for i in place_holders:
if i in test_template:
test_template=test_template.replace(i,(self.__dict__[i.lstrip("{").rstrip("}")]))
return test_template
grammer.py
from dicts import article_words, nouns
def getArticle(subject):
for key, value in article_words.items():
if nouns[subject] == value:
return key
main.py
from tkinter import *
from tkinter import ttk
from template import Template
from dicts import teams
root = Tk()
root.geometry("420x150")
tournament=ttk.Combobox(root, width = 18)
tournament.place(x=15, y=15)
tournament['value'] = ["Serie A", "Serie B"]
tournament.set("Tournament")
def on_tournament_selected(event):
team.delete(0,'end')
req_teams = []
sel_tournament = tournament.get()
for key,value in teams.items():
if value['Tournament'] == sel_tournament:
req_teams.append(key)
team.config(values=req_teams)
tournament.bind("<<ComboboxSelected>>", on_tournament_selected)
team=ttk.Combobox(root, width = 18)
team.place(x=215, y=15)
team.set("Teams")
text = Text(root,width=43,height=2)
text.place(x=15, y=50)
def printTeam():
template1 = Template(subject=team.get()) # pass keyword arguments
text.insert(END, template1.return_template())
button2 = Button(root, text="Print", command = printTeam)
button2.pack()
button2.place(x=15, y=100)
root.mainloop()
The error was due to circular imports in your files.

How to change a list depending on tkinter checkbutton

I have been trying to use tkinter CheckButton widget to edit items in a list - each item of the list is a new checkbutton. I want a save method to save the data to a text file, and a load method to load the info from the text file and mark the checkbutton boxes depending on the items in the list.
Here is my code so far, but the list doesn't seem to change when I check the buttons and update the list/file
Here is my code, I need to know why the list isn't updating when I check the boxes:
import tkinter.messagebox as box
modulesMain = Tk()
moduleChecks = []
def SaveChanges():
# Clear the text file
modules = open("modules.txt", "w") #Write mode to overwrite the whole file
modules.write("") # Put in blank text
modules.close()
modules = open("modules.txt", "a") # Append mode to append the file
for item in moduleChecks:
modules.write(item + "\n")
print(moduleChecks)
appButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[0]).grid()
searchButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[1]).grid()
Save = Button(modulesMain, text = "Save Changes", command = SaveChanges).grid()
The variable for each of your checkboxes needs to be an IntVar. Your moduleCheck list is currently un-initialised so when you try to access the elements inside it, you'll get an error.
In the below code (modified from yours), I've initialised the moduleCheck to contain two IntVars.
When you press the save button, it will print to the console the current state of the check boxes.
import tkinter as tk
modulesMain = tk.Tk()
moduleChecks = [tk.IntVar() for i in range(2)]
def SaveChanges():
for idx,item in enumerate(moduleChecks):
print(f"{idx} = {item.get()}")
appCheck = tk.Checkbutton(modulesMain, text = "App", variable = moduleChecks[0])
appCheck.grid()
searchCheck = tk.Checkbutton(modulesMain, text = "Check", variable = moduleChecks[1])
searchCheck.grid()
saveButton = tk.Button(modulesMain, text = "Save Changes", command = SaveChanges)
saveButton.grid()
modulesMain.mainloop()
Here is a basic solution.
import tkinter as tk
root = tk.Tk()
#list of options. doubles as checkbox label text
#appending to this list is all that is necessary to create more options
opts = ['App', 'Search']
#create an StringVar for every option
#this way there doesn't have to be type conversions for reading/writing files
vars = [tk.StringVar(value='0') for _ in opts]
#create checkboxes for every option
for opt, var in zip(opts, vars):
tk.Checkbutton(root, text=opt, variable=var, onvalue='1', offvalue='0').grid(sticky='w')
#write the checkbox values to file
def save_options_state():
with open("modules.txt", "w") as file:
file.write(''.join([v.get() for v in vars]))
#set checkbox values from file
def load_options_state():
with open("modules.txt", "r") as file:
for n, var in zip(list(file.read().strip()), vars):
var.set(n)
#save/load buttons
tk.Button(root, text="save", command=save_options_state).grid()
tk.Button(root, text="load", command=load_options_state).grid(row=2, column=1)
root.mainloop()
Here's a runnable example which I think shows how to do everything you've asked about. It assumes the modules.txt file contains data in the following format, where each line consists of a module's name and whether it's checked or not.
Mod1,0
Mod2,0
Mod3,0
The data in the file is used to create a list of Checkbuttons — one for each module — and initially sets to its current status as indicated in the file.
Here's the sample code:
from tkinter import *
import tkinter.messagebox as box
dataFilename = "modules.txt"
moduleChecks = []
def ReadModules():
with open(dataFilename, "r") as file:
for line in (line.strip() for line in file):
moduleName, moduleState = line.split(',')
moduleChecks.append((moduleName, int(moduleState)))
print('read:', moduleChecks)
def SaveChanges():
print('writing:', moduleChecks)
with open(dataFilename, "w") as file:
for checkButton in moduleCheckbuttons:
moduleName = checkButton.cget('text')
moduleState = checkButton.var.get()
file.write(','.join((moduleName, str(moduleState))) + '\n')
ReadModules()
modulesMain = Tk()
moduleCheckbuttons = []
# Create a Checkbutton for each module based on moduleChecks values.
for moduleName, moduleState in moduleChecks:
intVar = IntVar(value=moduleState)
checkButton = Checkbutton(modulesMain, text=moduleName, variable=intVar)
checkButton.var = intVar # Attach reference to variable.
checkButton.grid()
moduleCheckbuttons.append(checkButton)
saveButton = Button(modulesMain, text="Save Changes", command=SaveChanges)
saveButton.grid()
modulesMain.mainloop()

Trace Function not working as intended with imported files

I'm currently working on a program that displays variables line by line as code executes from a file (think trace tables). I'm able to get it working fine when the code is encased within a function inside the program itself but when I import the same code, it tracks new variables that I don't want to track (as they don't appear in the actual script). Is there any way I can isolate the variables specific to the program? (it'll also be useful for when I'm ignoring variables from pulled libraries in Python).
Here's my program so far:
import sys
import dis
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog
line_record = [] #where data on variables will be stored line by line
def trace(frame, event, arg_unused): #tracing function
global line_record
relevant_locals = {}
temp_vars = frame.f_locals.copy()
for k,v in temp_vars.items():
if not k.startswith("__"):
relevant_locals[k] = v
line_record.append([frame.f_lineno, relevant_locals])
return trace
def print_results(v_list):
print_record="" #variable which holds the formatting of the data
for item in v_list: #iterating for data in each recorded line
print_record += "Line " + str(item[0]) + "= "
if item[1]=={}: #catching lines with no variables
print_record += "no variables"
else:
for key in item[1]: #iterating through each variable to fetch associated data
print_record += str(key)+"="+str(item[1].get(key,"error"))+"|"
print_record += "\n"
return print_record
root = Tk() #creating the main window
root.title("frame")
root.iconbitmap("c:/Users/hamza/Documents/Other/Python/GUI/6 images/apple.ico")
root.filename = filedialog.askopenfilename(initialdir="/", title="select file",filetypes=[("python files","*.py")]) #import python files through tkinter dialog box
user_code = compile(open(root.filename, "rb").read(), root.filename, 'exec') #compiling imported code in order to use later in project
sys.settrace(trace)
exec(user_code)
sys.settrace(None)
print(print_results(line_record))
root.mainloop()
And for reference (if needed), my test code is:
a = 1
b = 2
a = a + b
If you need any more information please don't hesitate to ask, I'm happy to clarify, thanks.

Altering python variable identifiers in a loop which defines a tkinter button

I've got a function in a class in which I am creating several tkinter buttons within, where the number of buttons and the button properties depends on a file (so I can't create a specific number of variables to hold the buttons).
The code I have looks like this (sparing the complexities of the whole code):
import os
import tkinter as tk
Class(GUI):
def ButtonCreator(self):
self.HomeworkList = open("Files\HWNameList.txt", "r")
x = self.HomeworkList.readline()
while not x == "END":
x = x[0:-1]
HomeworkFileName = str("Files\HW-" + x + ".txt")
locals()["self.Button" + x] = tk.Button(master, text = x, command = lambda: self.DisplayHomeworkFile(FileName))
locals()["self.Button" + x].pack()
x = self.HomeworkList.readline()
self.HomeworkList.close()
def DisplayHomeworkFile(self, filename):
os.startfile(filename)
The file I am opening looks like this...
HomeworkName1
HomeworkName2
HomeworkName3
END
When the code runs, it displays the buttons with the correct text written on them but when i click on them they only ever display the file who's file name is written last in the HomeworkList file. Not sure what I've done wrong.
If there is another way to achieve what I'm trying I'm open to all suggestions.
Thanks.
This is a classic beginners problem that comes from misunderstanding how lambda works. For this case you need to use functools.partial.
You also need to forget about modifying locals(). Make a list or dictionary to hold the button instances.
from functools import partial
def ButtonCreator(self):
self.HomeworkList = open("Files\HWNameList.txt", "r")
x = self.HomeworkList.readline()
self.buttons = []
while not x == "END":
x = x[0:-1]
HomeworkFileName = str("Files\HW-" + x + ".txt")
btn = tk.Button(master, text = x, command = partial(self.DisplayHomeworkFile, HomeworkFileName))
btn.pack()
self.buttons.append(btn)
x = self.HomeworkList.readline()
self.HomeworkList.close()

raw_input stops GUI from appearing

I have written a program in Python that allow me to change the names of many files all at once. I have one issue that is quite odd.
When I use raw_input to get my desired extension, the GUI will not launch. I don't get any errors, but the window will never appear.
I tried using raw_input as a way of getting a file extension from the user to build the file list. This program will works correctly when raw_input is not used.The section of code that I am referring to is in my globList function. For some reason when raw_imput is used the window will not launch.
import os
import Tkinter
import glob
from Tkinter import *
def changeNames(dynamic_entry_list, filelist):
for index in range(len(dynamic_entry_list)):
if(dynamic_entry_list[index].get() != filelist[index]):
os.rename(filelist[index], dynamic_entry_list[index].get())
print "The files have been updated!"
def drawWindow(filelist):
dynamic_entry_list = []
my_row = 0
my_column = 0
for name in filelist:
my_column = 0
label = Tkinter.Label(window, text = name, justify = RIGHT)
label.grid(row = my_row, column = my_column)
my_column = 1
entry = Entry(window, width = 50)
dynamic_entry_list.append(entry)
entry.insert(0, name)
entry.grid(row = my_row, column = my_column)
my_row += 1
return dynamic_entry_list
def globList(filelist):
#ext = raw_input("Enter the file extension:")
ext = ""
desired = '*' + ext
for name in glob.glob(desired):
filelist.append(name)
filelist = []
globList(filelist)
window = Tkinter.Tk()
user_input = drawWindow(filelist)
button = Button(window, text = "Change File Names", command = (lambda e=user_input: changeNames(e, filelist)))
button.grid(row = len(filelist) + 1 , column = 1)
window.mainloop()
Is this a problem with raw_input?
What would be a good solution to the problem?
This is how tkinter is defined to work. It is single threaded, so while it's waiting for user input it's truly waiting. mainloop must be running so that the GUI can respond to events, including internal events such as requests to draw the window on the screen.
Generally speaking, you shouldn't be mixing a GUI with reading input from stdin. If you're creating a GUI, get the input from the user via an entry widget. Or, get the user input before creating the GUI.
A decent tutorial on popup dialogs can be found on the effbot site: http://effbot.org/tkinterbook/tkinter-dialog-windows.htm

Categories

Resources