Requirement :
1.create a gui using Tkinter
2.Update the excel by fetching values from Tkinter entry widget
3.Read another sheet of the same workbook
4.plot graph using inside the Tkinter window.
Problem: All the functionality is working fine, except when modifying and reading one after another at same time.
Loaded the work book with data_only=False to preserve formulas. I have modified the excel successfully in "INPUT" sheet.Then when I am reading the cells from "SIMULATION" sheets which are linked to the "Input" sheets with formulas , no data is coming.
Opening the excel file with Excel and closing it ,and now if i run the python program again without the modify functionality, program is able to read cell value and plot graph.
During read functionality of the program workbook is loaded 2nd time using data_only = True to get cell values .
Any suggestions will be very helpful for me.
from tkinter import *
from tkinter import ttk
from openpyxl import load_workbook
import datetime
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class Root(Tk):
f = Figure(figsize=(20,8))
a = f.add_subplot(111)
entryString1=0
entryString2=0
entryString3=0
entryString4=0
entryString5=0
entryString6=0
def __init__(self):
super(Root, self).__init__()
self.title("Python Tkinter Dialog Widget")
self.state("zoomed")
self.frame = ttk.Frame(self,borderwidth=2, relief="solid")
self.frame.pack(side='top',fill=X,padx=10,pady=20)
self.entry()
self.button()
self.canvas = FigureCanvasTkAgg(self.f, self.frame)
self.canvas.get_tk_widget().pack(fill="both",padx=10,pady=20)
self.canvas.draw()
def entry(self):
self.frame2 = ttk.Frame(self)
self.frame2.pack(fill=X,padx=10,pady=20)
self.labelentry1 = ttk.Label(self.frame2,text = "V Past Main begin period: ")
self.labelentry1.pack(fill=X,side='left',anchor=W)
self.entry1 = ttk.Entry(self.frame2)
self.entry1.pack(fill=X,side='left')
self.labelentry2 = ttk.Label(self.frame2,text = " V Past PS begin period: ")
self.labelentry2.pack(fill=X,side='left')
self.entry2 = ttk.Entry(self.frame2)
self.entry2.pack(fill=X,side='left')
self.labelentry3 = ttk.Label(self.frame2,text = " Adv 0% Main: ")
self.labelentry3.pack(fill=X,side='left')
self.entry3 = ttk.Entry(self.frame2)
self.entry3.pack(fill=X,side='left')
self.labelentry4 = ttk.Label(self.frame2,text = " Adv 0% PS: ")
self.labelentry4.pack(fill=X,side='left')
self.entry4 = ttk.Entry(self.frame2)
self.entry4.pack(fill=X,side='left')
self.labelentry5 = ttk.Label(self.frame2,text = " Single premium already paid: ")
self.labelentry5.pack(fill=X,side='left')
self.entry5 = ttk.Entry(self.frame2)
self.entry5.pack(fill=X,side='left')
self.labelentry6 = ttk.Label(self.frame2,text = " Yearly premium already paid: ")
self.labelentry6.pack(fill=X,side='left')
self.entry6 = ttk.Entry(self.frame2)
self.entry6.pack(fill=X,side='left')
def button(self):
self.frame3 = ttk.Frame(self)
self.frame3.pack(side='bottom')
self.labelFrame = ttk.LabelFrame(self.frame3,text = "Regenrate Graph")
self.labelFrame.pack(padx=10)
self.button = ttk.Button(self.labelFrame, text = "Click",command = self.fileDialog)
self.button.pack(padx=10)
def fileDialog(self):
self.entryString1 = self.entry1.get()
self.entryString2 = self.entry2.get()
self.entryString3 = self.entry3.get()
self.entryString4 = self.entry4.get()
self.entryString5 = self.entry5.get()
self.entryString6 = self.entry6.get()
#Load excel file
self.filename = "C:\\Users\\ramit\\Desktop\\Projection V0.3_Shankha .xlsx"
#load excel file to modify
self.work_book = load_workbook (self.filename)
self.sheet = self.work_book['Inputs']
if len(self.entryString1) != 0:
self.sheet.cell(row=4,column=5).value=int(self.entryString1)
if len(self.entryString2) != 0:
self.sheet.cell(row=5,column=5).value=int(self.entryString2)
if len(self.entryString3) != 0:
self.sheet.cell(row=6,column=5).value=int(self.entryString3)
if len(self.entryString4) != 0:
self.sheet.cell(row=7,column=5).value=int(self.entryString4)
if len(self.entryString5) != 0:
self.sheet.cell(row=9,column=5).value=int(self.entryString5)
if len(self.entryString6) != 0:
self.sheet.cell(row=10,column=5).value=int(self.entryString6)
self.work_book.save(self.filename)
self.work_book = None
#load excel file to read
self.work_book = load_workbook (self.filename,data_only=True)
self.sheet_1 = self.work_book['Simulation']
self.x = []
self.y = []
for i in range(10, 17):
self.x.append (self.sheet_1.cell(row=i + 1,column=1).value)
self.y.append (self.sheet_1.cell(row=i + 1,column= 77).value)
self.a.clear()
print(self.x)
print(self.y)
self.a.set_xlabel('Simulation date')
self.a.set_ylabel('Reserve')
self.a.plot(self.x, self.y, color='cyan', label='Projection')
self.canvas.draw()
root = Root()
root.mainloop()
The issue is that openpyxl doesn't evaluate the formula in excel. it will only return the last value saved by excel or 'None' (with data_only=True). The latter is what is happening when you change an input cell and use [cell].value to call the value of the cell with the formula. When you don't change the sheet, you get the value set in excel, which is why your code works when you disable/don't do the input to excel functionality.
Easiest way to around the issue is to use xlwings, this should work the way you intend it to. There are also a few other options. such as directly using windows excel commands to update the sheet then using openpyxl to read but I feel swapping modules is the simpler solution. You may also want to consider bringing the function to the python side of the process and just writing the result to the excel sheet.
how to get formula result in excel using xlwings
import xlwings as xw
sheet = xw.Book(r'C:/path/to/file.xlsx').sheets['sheetname']
result = sheet['X2'].value
Related
Title, it returns the error "_tkinter.TclError: image "pyimage3" doesn't exist" I have tried changing the root = tk.Tk
() to root = tk.TopLevel() as suggested by previous users who received the same error.
The script goes through a list of image files on a CSV file, the idea is to create a slideshow and the CSV file saves the formats of the different slides.
The "iam" if statement refers to an "image and message" slide which is what's currently drawing the errors. I have tried opening the image using the same calls as the regular image slide type but it crashes in a new and unique way every time.
I can post more information as needed but if anybody has any ideas as to how I could fix this I would love to hear them.
# import required modules
import tkinter as tk
from tkinter import *
from PIL import Image
from PIL import ImageTk
import pandas as pd
import datetime
import display_save
# Initialize tkinter window
root = tk.Tk()
# Retreive data table
frame = pd.read_csv("data.csv")
# Establish variables
show_size = len(frame.index)
img = ImageTk.PhotoImage(Image.open("teapot.png"))
bg_img = ImageTk.PhotoImage(Image.open("teapot.png"))
time_step = 1
# Initialize image label as empty
img_lbl = Label(root)
img_lbl.pack()
# Initialize text label
txt_lbl=Label(root)
txt_lbl.pack()
img_txt_lbl = Label(root)
img_txt_lbl.pack()
# Keypress event management
res_lbl = Label(root)
def keypress(event):
global res_lbl
if(event.char == "f"):
root.attributes('-fullscreen', False)
elif(event.char == "r"):
res_lbl.pack()
res_lbl.config(text= str(root.winfo_width()) + " x " + str(root.winfo_height()))
def keyrelease(event):
global res_lbl
if (event.char == "f"):
root.attributes('-fullscreen', True)
elif (event.char == "r"):
res_lbl.pack_forget()
# bind key events
root.bind("<KeyPress>",keypress)
root.bind("<KeyRelease>",keyrelease)
x = 0
# Function to rotate images
def runtime():
global x
global img
global img_lbl
global txt_lbl
global img_txt_lbl
global bg_img
if(x <= show_size):
df = pd.read_csv('data.csv')
df = df.iloc[[x - 1]]
t = df.iloc[0]['type']
date_remv = df.iloc[0]['date_remove']
# If type is image, initialize
if(t == "img"):
img_lbl.pack()
txt_lbl.pack_forget()
img_txt_lbl.pack_forget()
root.config(bg='white')
p = df.iloc[0]['data']
temp = Image.open(p)
temp = temp.resize((root.winfo_width(), root.winfo_height()))
img = ImageTk.PhotoImage(temp)
# If type is message, initialize
elif (t == "msg"):
txt_lbl.pack()
img_lbl.pack_forget()
img_txt_lbl.pack_forget()
m = df.iloc[0]['data']
c = df.iloc[0]['data2']
txt_lbl.config(bg =c, text=m, anchor=CENTER, height=20, wraplength=1000, font=("Arial", 50))
root.config(bg=c)
# If type is an image and a message, initialize
elif (t == "iam"):
img_txt_lbl.pack()
txt_lbl.pack_forget()
img_lbl.pack_forget()
p = df.iloc[0]['data']
temp = Image.open("teapot.png")
temp = temp.resize((root.winfo_screenwidth(), root.winfo_screenheight()))
temp = ImageTk.PhotoImage(temp)
bg_img = temp
m = df.iloc[0]['data2']
img_txt_lbl.config(text=m, height=root.winfo_screenheight(), width=root.winfo_screenwidth(), wraplength=1000, font=("Arial", 50), compound='center')
root.config(bg='white')
# Check to make sure the slides list is up-to date
if(datetime.datetime.strptime(date_remv, display_save.format) <= datetime.datetime.now()):
index = df.iloc[0]['id']
display_save.delete_row(index)
root.after(time_step * 1000, runtime)
else:
x = 0
root.after(0, runtime)
x = x + 1
img_lbl.config(image=img)
img_txt_lbl.config(image=bg_img)
runtime()
root.attributes('-fullscreen', True)
root.mainloop()
Whenever you create a new image using ImageTk.PhotoImage() you have to make sure that image variable stays during the entire time your code runs. This means for example if you create the first image in a variable img then you must not use the SAME variable for a new image because once the old image is rewritten it's gone. So the trick here is to store the old image somewhere where it doesn't change. To do this simply append the old image to a new list where you can store all PHOTOIMAGEs as a memory. I recently answered the same type of question here and there might be a better explanation: How to create multiple images in tkinter that can scale.
I haven't tested if this works but I believe it should work.
I am currently struggling trying to use the panel library in Python, in order to build an interactive dashboard to analyze and display CSV data. My current goal is to let the user enter an initial and a final date, which will be used to filter a DataFrame once a button is pressed. However, whenever I press the button, the on_click function is not completely executed before the script stops running. The code snippet is the following:
import panel as pn
pn.extension()
def acquire_data(dateBeginning, dateEnd):
eventDF = pd.read_csv('multi.csv')
eventDF['Date']= pd.to_datetime(eventDF['Date'])
dateDF = eventDF[eventDF.upvotes > 8]
print(eventDF)
def register_dates(event, save=True):
dateBeginning = date1Picker.value
dateEnd = date2Picker.value
if dateBeginning < dateEnd:
text = pn.widgets.StaticText(name='Static Text', value='A string')
spinner = pn.indicators.LoadingSpinner(width=50, height=50, value=True, color='info', bgcolor='light')
layout = pn.Column(text, spinner, align='center')
layout.app()
print('getting in')
acquire_data(dateBeginning, dateEnd)
print('getting out')
spinner.value = False
else:
print('Not working')
#pn.pane.Alert('## Alert\nThis is a warning!')
return save
date1Picker = pn.widgets.DatePicker(name='Date Initiale', margin=25)
date2Picker = pn.widgets.DatePicker(name='Date Finale', margin=25)
button = pn.widgets.Button(name="Analyse", button_type='primary', margin=(25, 0, 20, 200), width=200)
button.on_click(register_dates)
dateLayout = pn.Row(date1Picker, date2Picker)
layout = pn.Column(dateLayout, button, width=200, align='center')
layout.app()
I was also aiming at having the first layout be replaced by the one with the spinner and the text once the button is pressed, but I haven't found anything in the doc mentioning how to do so. If anyone could give me a hint regarding these issues, that would really help me!
In def acquire_data(dateBeginning, dateEnd):
pd.read_csv('multi.csv'), pd.to_datetime(eventDF['Date'])
For start, in this function I think you forgot to import panda and your app just crash.
add: import pandas as pd
Ex:
import panel as pn
import pandas as pd
So I have designed a program that allows you to open 3 CSV files, and translate them into json, and 'clean' them then merge them into one file.
The way i have designed it, is that on display, there are 3 buttons for loading each file, but i am wondering if there is a better design that shows only 1 button, which the user can load multiple files that are assigned to different variables, depending on the column headings in these csv files.
The problem that I had, is when you load one file, it overrides the previously loaded file.
For reference, the csv files can be only 1/3 different formats so the column headings are known.
As you can see from a short part of my code, that i have created 2 different loadfile buttons, for the 2 separate files, but is there a way i can have just 1 button, that assisgns different files to different variables?
import tkinter as tk
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
import pandas as pd
import json
import csv
import sys
import sqlite3
#
def loadInspection():
global inspectionFile
global label_inspectionFile
loadingCSV = filedialog.askopenfilename()
inspectionFile = pd.read_csv(loadingCSV)
#Making buttons for load and translate and clean
loadButton = Button(window1, text="Load Inspections File", command=loadInspection)
loadButton.grid(row=0, column=1)
#loading first file called inspections
def inspectionClean():
global inspectionFile
inspectionFile = inspectionFile
#Check and remove any duplicates in the Inspection Data
inspectionFile = inspectionFile[inspectionFile.duplicated() == False]
cleanInsButton = Button(window1, text="Clean", command=inspectionClean)
cleanInsButton.grid(row=0, column=3)
#loading second file called inventroy
def loadInventroy():
global inventroyFile
loadingCSV = filedialog.askopenfilename()
inventroyFile = pd.read_csv(loadingCSV)
#Making buttons for load and translate and clean
loadButton = Button(window1, text="Load Inventroy File", command=loadInventroy)
loadButton.grid(row=3, column=1)
def inventroyClean():
global inventroyFile
inventroyFile = inventroyFile
#Check and remove any duplicates in the Inspection Data
inventroyFile = inventroyFile[inventroyFile.duplicated() == False]
cleanInvButton = Button(window1, text="Clean", command=inventroyClean)
cleanInvButton.grid(row=3, column=3)
My guess would be this may be a problem with global variables. In general I would recommend using instance/class variables instead, using global variables is generally not advised.
Your load functionality could instead be structured like this:
class MyCSVLoader:
def __init__(self):
self.inventory = None
self.inspection = None
def loadCSV(self):
loadingCSV = filedialog.askopenfilename()
return pd.read_csv(loadingCSV)
def loadAll(self):
self.inspection = self.loadCSV()
self.inventory = self.loadCSV()
However, let me know if this works as I don't have csv files on hand to test it with.
Then, you could implement it with one button like this:
csvLoader = MyCSVLoader()
loadButton = tk.Button(window1, text="Load All Files", command=csvLoader.loadAll)
loadButton.grid(row=0, column=1)
Which should pop up 1 window after the other for you to select 2 files. The files selected can then be accessed through csvLoader.inventory and csvLoader.inspection.
Note that your clean function would also have to go into this class to function properly.
I am fooling around with GUI programming using Tkinter on python, with the goal being a GUI app which will allow a user to add a task or delete (to be saved to a txt file).
Right now, I am just trying to figure out the basics of adding/removing checkbuttons.
The issue I am having is that when I run the script, the check boxes are not aligned with the text for them. The picture below shows the issue.
The code used to pull tasks from a txt file and add them as checkbuttons is as follows:
tasks = []
with open(r"C:\Users\patte\Desktop\tasks.txt") as file:
tasks = file.readlines()
row = 2
for task in tasks:
self.task_checkbox = Checkbutton(self.master,text=task,font=("Arial",12),relief='groove')
self.task_checkbox.grid(row=row,column=0,sticky=W)
row+=1
Note that row is initialized to 2 as the title and entry bar take up rows 0 and 1. I have tried changing fonts, etc., but the result stays the same.
Any sort of advice or criticism is greatly appreciated.
The full code is as follows:
from tkinter import *
class ToDo():
def __init__(self,master):
self.master = master # Geometry and layout when initialized
master.geometry("800x800")
master.title("TO DO")
self.gui_title = Label(self.master,text="Tasks:",font = ("Arial Bold",26))
self.gui_title.grid(row=0,column=0,sticky=W)
self.task_entry_label = Label(self.master,text="Enter Task:",font = ("Arial",12))
self.task_entry_label.grid(row=2,column=0,sticky=W)
self.task_entry = Entry(self.master)
self.task_entry.grid(row=2,column=1,sticky=W)
self.task_entry.focus_set()
tasks = []
with open(r"C:\Users\patte\Desktop\tasks.txt") as file:
tasks = file.readlines()
row = 3
for task in tasks:
self.task_checkbox = Checkbutton(self.master,text=task,font=("Arial",12),relief='groove')
self.task_checkbox.grid(row=row,column=0,sticky=W)
row+=1
self.QuitButton = Button(self.master,text="Quit",font=("Arial Bold",12),command = lambda: self.master.quit())
self.QuitButton.grid(sticky=SW,row=7)
root = Tk()
Tasks = ToDo(root)
root.mainloop()
Thanks!
I am trying to use a entry to append the dbf part of a shapefile. So far I can only make a change to the dbf file by directly assigning the variable a value. I can only get the entry box to print a variable. What am I missing? I am using python 3.3.
import shapefile
from tkinter import filedialog
import tkinter as tk
class application:
def __init__(self,window):
""" Initalize the Application """
self.myentrybox = tk.Entry(window)
self.myentrybox.pack()
self.myentrybox.insert(0,"some default value")
self.myentrybox.bind("<Return>",self.Enter)
def Enter(self,event):
""" Someone Pressed Enter """
print (self.myentrybox.get())
aep = 'self.myentrybox.get()'
root=tk.Tk()
myapp = application(root)
r = shapefile.Reader('Mississippi')
w = shapefile.Writer()
w.fields = list(r.fields)
w.field (aep, 'C', '40')
i=1
for rec in r.records():
rec.append(i)
i+=1
w.records.append(rec)
w._shapes.extend(r.shapes())
w.save('AMissl6KM')
root.mainloop()