Displaying Pandas dataframe in tkinter - python

I'm creating a tkinter gui that will take user input for a variable that then gets passed to SQL and the queried data (in this case a single column data frame and boxplot). However, at this moment I can not find a means of displaying my pandas dataframe in the tk gui. I have not found any module or means of displaying this, and I've spent hours going through possible solutions to no avail. Only thing I need is for the dataframe to display in the gui and be re-rendered each time I change the dataframe through the user input function. My code atm is:
### Starting first GUI/Tkinter Script
##First load libs
import pyodbc
import numpy as np
import pandas.io.sql as sql
import pandas
import matplotlib.pyplot as plt
import pylab
import matplotlib as mpl
from tkinter import *
import sys
plt.ion()
def userinput():
global PartN, pp, df
##a = raw_input(v.get())
a = E1.get()
##print a
PartN = a
conn = pyodbc.connect('DRIVER={SQL Server};SERVER=my server;
DATABASE=PackingList;UID=myid;PWD=mypword')
sqlr = "SELECT partmadeperhour FROM Completions WHERE PartNumber = ?
AND endtime > '2012-12-31 23:59:00' ORDER BY partmadeperhour"
df = pandas.read_sql_query(sqlr, conn, params=[PartN])
conn.close()
print(df)
stats = df['partmadeperhour'].describe()
print(stats)
print("Part Name is %s" %PartN)
##df.plot(kind='box', fontsize='20')
pp = df.plot(kind='box', fontsize='20')
plt.show(block=True)
def clear_textbox():
E1.delete(0, END)
master = Tk()
master.title('Title')
v = StringVar()
PartN = None
L1 = Label(master, text = 'Name')
L1.pack(side = LEFT)
E1 = Entry(master, textvariable = v, bd = 5)
E1.pack(side = RIGHT)
b = Button(master, text = 'Submit', command = userinput)
b.pack(side = TOP)
b2 = Button(master, text = 'Clear', command=clear_textbox)
b2.pack(side=BOTTOM)
master.mainloop()
An example of my data frame
rate
0 [0.25]
1 [0.67]
2 [0.93]
... ...
1474 [5400.00]
If someone could just point me in the right direction, I don't even need code corrections just yet, I just need to hear someone to say yes it is possible (which I know it is), and to give me some kind of example. Thanks

I don't have enough rep to comment, otherwise I would, but I used this video to get a grasp of interacting with pandas/numpy/matplotlib and tkinter. He ends up building a large application with it if you stick through all the videos.
Even though you aren't likely doing the exact same thing, I think there might still be useful methods of interacting with your data and GUI gleamed from the videos.
If the link ever dies, you can search on YouTube "Tkinter tutorial sentdex"
Best of luck.

Related

Calling a function from another python file takes 15 seconds, compared to running it in the same module

So, in an effort to refactor my code into a more modular project, i decided to export some of my most used functions, method and classes to separate py modules, so that i can simply "from x import y" and then use method y instead of copy-pasting the code (yes, i know that's horrible, but when you get "implement this yesterday" requests there is only so much you can do".
As an example, i often have to scan COM ports in my system and pick one or more, so i created this module:
from serial.tools import list_ports
from serial import Serial, SerialException
import tkinter as tk
from functools import partial
def COMScanner():
allports = []
ports = list_ports.comports()
for port, desc, hwid in sorted(ports):
hwid = hwid.split(" SER")[0]
# print("{}: {} [{}]".format(port, desc, hwid)) #DBG
try:
s = Serial(port)
s.close()
state = "FREE"
except (OSError, SerialException):
state = "BUSY"
pass
allports.append([port, hwid, state])
return allports
def targetSelector(COMS):
global sel_com
sel_com = None
# Once all COM ports are detected, if there are more than one, the user must pick one
# to connect to the testing board, the user must click the button with the name of
# the desired COM port and dismiss the dialog
if COMS and len(COMS) > 1:
COMSelector = tk.Tk()
baseWidth = 8 * len("".join(max(COMS, key=len)))
baseHeight = 35 * len(COMS)
finalWidth = baseWidth + 10
finalHeight = baseHeight # * len(COMS)
screen_width = COMSelector.winfo_screenwidth()
screen_height = COMSelector.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (finalWidth / 2))
y_coordinate = int((screen_height / 2) - (finalHeight / 2))
COMSelector.geometry("{}x{}+{}+{}".format(finalWidth, finalHeight, x_coordinate, y_coordinate))
COMSelector.update_idletasks()
COMSelector.title("Select Target")
def save_action(selection):
global sel_com, user_pass
sel_com = str(selection)
COMSelector.destroy()
for id, COM in enumerate(COMS):
COM_HANDLE = str(COM[0])
COM_PID = str(COM[1])
COM_STATE = str(COM[2])
action = partial(save_action, COM_HANDLE)
COMLabel = tk.Label(master=COMSelector, text=COM_PID, borderwidth=0.5, relief="solid")
COMButton = tk.Button(master=COMSelector,
text=("[" + COM_STATE + "] " + COM_HANDLE),
command=action
)
if COM_STATE == "FREE":
COMButton.config(state="active")
else:
COMButton.config(state="disabled")
COMButton.grid(row=id, column=0, pady=5, padx=5, sticky="WE")
COMLabel.grid(row=id, column=1, pady=5, padx=5, sticky="WE")
COMSelector.mainloop()
else:
sel_com = COMS[0]
try:
return sel_com
except:
return "No target selected."
A typical usage might be, use COMScanner() to get the relevant port info and then targetSelector() to display the UI and let the user choose.
By defining the functions inside my program, both functions execute instantly (as expected)
By instead creating a module, in a folder I.E. "Snippets\COMSelector.py" and then using something like
from Snippets import COMSelector
[...]
start_time datetime.now()
available_COMS = COMSelector.COMScanner()
target = COMSelector.targetSelector(available_COMS)
print(datetime.now() - start_time)
with only one COM interface connected to the system, to eliminate the human clicking speed factor
i get 0:00:15.245072
Since i'm a beginner, i might be missing something crucial, please help!
There apparently was an issue with my USB controller, rebooting didn't fix it, had to disable and re-enable the driver in my device manager, somehow it took a really long time executing the
"from serial.tools import list_ports" and
"from serial import Serial, SerialException" lines
about 7.4 - 7.5s each, not only that, each time i tried importing anything from the pyserial module it took that long.
i have no idea what caused this but, it's fixed now, marking as solved.

Python, Exel sharing violation with python automatic inventory balance checking

First I have to say that I just started Python programming. I do automatic inventory balance checking, mainly as a hobby.
I need help with my program...
This opens the excel file and looks for the value 0, in the B column.
if there is a 0 in the column which means that the part has run out, the code will send an e-mail with the title of the article from column A
When I did the debugging I noticed if i update the the Excel file that code uses in Exel at the same time the program happens to read the table, i get "Excel sharing violation error"
this is not a big problem because the final program will read the file once or twice a day but there is a small chance of an error, probably something else will crash, hopefully not.
I tried all these common ways with Exel
-Authorize Indexing for file Folders
-Permitting Sharing Wizard
-Saved file to a different location
-Renamed file
-i don't use third party Antivirus program
So would you have any tips on how i can improve the code, get rid of the above error and continue learning.
import threading
import tkinter as tk
from tkinter import BOTTOM,Label, Menu
import os
import smtplib
import datetime
import time
from time import sleep
import pandas as pds
root = tk.Tk()
root.title('Timer')
sec = 0
def timer():
global sec
sec += 1
time['text'] = sec
time.after(1000, timer)
time = Label(root, fg='black')
time.pack()
contact_num = os.environ.get("Contact_number")
contact_email = os.environ.get("Contact_email")
L = Label(root, text="Automatic warehouse alarm").pack()
L_2 = Label(root, text="START, Run program").pack()
root.title("Warehouse 1.4.2")
root.geometry("460x360")
class get_data:
def get_empty_stock(self):
while(True):
Email_adress = os.environ.get("email_user")
Email_password = os.environ.get("email.password")
file = ("C:\Exel_files\SpareParts_Vers25.xlsx")
try:
dataFrame = pds.read_excel(file)
except PermissionError:
print("PermissionError")
else:
pass
booleans = []
for Amount in dataFrame.Amount:
if Amount <= 0:
booleans.append(True)
else:
booleans.append(False)
if True in booleans:
empty_stock = dataFrame.loc[dataFrame.Amount <= 0]
with smtplib.SMTP("smtp.gmail.com",587) as smtp_server:
time_date = datetime.datetime.now()
time = time_date.strftime("%H:%M:%S")
date = time_date.strftime("%d/%m/%Y")
smtp_server.ehlo()
smtp_server.starttls()
smtp_server.ehlo()
smtp_server.login(Email_adress,Email_password)
subject = "Warehouse"
body = f"""{empty_stock}\nPart is out of stock
{date}\n
{time}\n
"""
msg = f"subject: {subject}\n\n{body}"
smtp_server.sendmail(Email_adress,Email_adress, msg)
sleep(25)
def __init__(self):
t = threading.Thread(target=self.get_empty_stock)
t.start()
Start = tk.Button(root, text = "START", width= 25, command=lambda:[get_data(),timer()]).pack()
Stop = tk.Button(root, text = "QUIT", width= 25, command= root.quit).pack(side=BOTTOM)
menu = Menu(root)
root.config(menu=menu)
helpmenu = Menu(menu)
menu.add_cascade(label='menu', menu=helpmenu)
helpmenu.add_command(label=contact_email)
helpmenu.add_command(label=contact_num)
helpmenu.add_command(label= "Exit", command=root.quit)
root.mainloop()
To avoid multi-user sharing violation, consider copying the last saved instance of Excel file using a tempfile and point read_excel to this temp file which is automatically deleted after the with context:
import shutil
import tempfile
...
file = "C:\Exel_files\SpareParts_Vers25.xlsx"
with tempfile.NamedTemporaryFile() as tmp:
# COPY EXCEL FILE TO TEMP FILE
shutil.copyfile(file, tmp.name)
try:
# READ IN TEMP FILE
dataFrame = pds.read_excel(tmp.name)
except PermissionError:
print("PermissionError")

Is any way the function taking the values from the imported entries from the the Tk?

So I want to run the Tk() and import there some values. Then these values will be checked from a function that will replacing the value in a particular cell in excel. This cell that matches with one imported entry. My challenge here is that the function works and replacing if I put manually the values inside the program BUT how can I make to read the values from the Tk() that I enter? I made a button so the function will be running after I have imported the values in the entry fields but still not. The "sntake" and "sngive" in the replace() function seems to not working... What am I missing?
Code:
from tkinter import *
import pandas as pd
from xlutils.copy import copy
from openpyxl import *
from openpyxl.utils.cell import get_column_letter
import openpyxl
app = Tk()
app.geometry("500x500")
app.title("S/N Management")
heading = Label(text="S/N Management",fg="black",bg="green",width="500",height="3",font="10")
heading.pack()
sngive_text = Label(text="S/N of the delivered ")
sntake_text = Label(text="S/N of the recieved ")
sngive_text.place(x=15,y=80)
sntake_text.place(x=15,y=160)
sngive = StringVar()
sntake = StringVar()
sngive_entry = Entry(textvariable=sngive,width="30")
sntake_entry = Entry(textvariable=sntake,width="30")
sngive_entry.place(x=15,y=100)
sntake_entry.place(x=15,y=180)
def replace():
wb = openpyxl.load_workbook('Tracker.xlsx')
wb.sheetnames
sheet = wb["serials"]
amountOfRows = sheet.max_row
amountOfColumns = sheet.max_column
for i in range(amountOfColumns):
for k in range(amountOfRows):
cell = str(sheet[get_column_letter(i+1)+str(k+1)].value)
if( str(cell) == sntake):
newCell = sngive
sheet[get_column_letter(i+1)+str(k+1)]=newCell
wb.save('tracker_updated.xlsx')
button = Button(app,text="Submit Data",command=replace,width="30",height="2",bg="grey")
button.place(x=140,y=420)
mainloop()

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

Dynamic Widget creation based of configparser file

I am trying a piece of python based tkinter code with following objective:
(Please go through the objective, then I will take an example to explain what exactly i require and then in the end will post the script I have written)
Reads from a config file, which is implemented using configparser module.
Based on options read from this file it automatically generates widget.
These widgets are restricted to only labels and entry box as of now.
Every entry box is associated with a variable. It is hence needed to generate a variable
automatically whenever a entry box is declared.
Now when the user enters any value in the entry box, and presses calculate button a list is
generated with combination of values entered by user( in a specific format).
Example:
Let the configparser file has following content:
[widget]
label = ani_label,sham_label
entry = ani,sham
The list generated for this case will be like this:
out_list = ['-ani','< ani >','-sham','< sham >']
< ani > means value stored in ani variable
And below is the code that i have tried.
from Tkinter import *
from Tkinter import Tk
import Tkinter as tk
import ttk
import ConfigParser
import sys
############ Initialize ###############################
parser_read = ConfigParser.ConfigParser()
parser_read.read('option_read.config')
config_list = {}
config_list['label'] = parser_read.get('widget','label').split(',')
config_list['entry'] = parser_read.get('widget','entry').split(',')
######
def calculate():
#Will include the list generation part
pass
#######
root = Tk()
root.title("NRUNTEST GUI VERSION 1")
#
menuframe = ttk.Frame(root)
menuframe.grid(column=0,row=0)
menuframe.columnconfigure(0,weight=1)
menuframe.rowconfigure(0,weight=1)
#
mainframe_label = ttk.Frame(root)
mainframe_label.grid(column=1,row=0)
mainframe_label.columnconfigure(0,weight=1)
mainframe_label.rowconfigure(0,weight=1)
#
mainframe_entry = ttk.Frame(root)
mainframe_entry.grid(column=2,row=0)
mainframe_entry.columnconfigure(0,weight=1)
mainframe_entry.rowconfigure(0,weight=1)
#
general_label= Label(menuframe,text="Please Enter the Values Below").grid(column=1,row=0,sticky=(E))
compiler_label= ttk.Label(menuframe,text="Compiler")
compiler_label.grid(column=1,row=1,sticky=W)
#
calculate_button = ttk.Button(menuframe, text="Calculate", command=calculate).grid(column=1,row=2,sticky=(W,E))
#Automatic Widget declaration ###
for x in config_list['label']:
x = ttk.Label(mainframe_label,text=x).grid()
for x in config_list['entry']:
#print x
var = lambda: sys.stdout.write(x)
x = ttk.Entry(mainframe_entry,textvariable = x).grid()
root.mainloop()
The content of option_read.config is
[widget]
label : animesh_label,sharma
entry : animesh_entry,sharma
STATUS:
I can create the required widgets automatically. But I am not able to create the variables dynamically to store the entry box values.
Once the variable has been calculated, I can write the calculate function on my own.
Please advice how i can proceed.
If you have any better way to meet my requirements, please do suggest.
Also do ping me if you require any more inputs or my query is not clear.
The easiest way to do this, IMO, is to use a dict to store the references to the dynamically created variables. You could use the label as the key. For example:
vars = {}
for x in config_list['entry']:
vars[x] = StringVar()
entry = ttk.Entry(mainframe_entry, textvariable=vars[x])
entry.grid()
By the way... are you aware that if you do something like x=ttk.Entry(...).grid(...), x does not contain a reference to the widget? It contains the result of the call to grid, which is None.

Categories

Resources