Store pandastable dataframe into variable - python

I currently have the below script that reads an imported csv file and displays as pandastable in the tkinter GUI.
As the file is imported its adds x2 additional columns self.table.addColumn("Current Status") and self.table.addColumn("Assign Technician").
How can I store the updated pandastable dataframe into a variable outside of class TestApp(tk.Frame): so that I can call other functions on the dataframe later in my code?
I have used the global variable before so that I can call a variable created from within a function outside of it later but not sure if thats what I need for this purpose.
import csv
import tkinter as tk
import tkinter.ttk as tkrttk
from tkinter import *
from tkinter import filedialog
import pandas as pd
from pandastable import Table, TableModel
root = tk.Tk()
root.geometry("2000x1000")
root.title('Workshop Manager')
def select_input_file():
global input_file_path
input_file_path = filedialog.askopenfilename(
filetypes=(("CSV files", "*.csv"),))
app = TestApp(root, input_file_path)
app.place(bordermode = INSIDE,height = 500, width = 2000, x =0, y=50)
class TestApp(tk.Frame):
def __init__(self, parent, input_file_path, editable = True, enable_menus = True):
super().__init__(parent)
self.table = Table(self, showtoolbar=False, showstatusbar=False)
self.table.importCSV(input_file_path)
self.table.show(input_file_path)
self.table.addColumn('Current Status')
self.table.addColumn('Assign Technician')
self.table.autoResizeColumns()
root.mainloop()

You don't necessarily need a global variable here. You can directly access the member attributes of a class by using the object itself. So in this case, you can access the table attr of the class TestApp using app.table, which would look something like this,
def select_input_file():
#...
app = TestApp(root, input_file_path)
app.place(bordermode = INSIDE,height = 500, width = 2000, x =0, y=50)
df = app.table # contains the updated table which can be passed to other functions
newFunc( df ) # sample call

Avoid global to achieve this.
Currently, all your stateful variables exist in the module (file). You can do the same for your table, outside of TestApp, and then pass it though __init__:
import csv
import tkinter as tk
import tkinter.ttk as tkrttk
from tkinter import *
from tkinter import filedialog
import pandas as pd
from pandastable import Table, TableModel
table = Table(showtoolbar=False, showstatusbar=False)
root = tk.Tk()
root.geometry("2000x1000")
root.title('Workshop Manager')
def select_input_file():
global input_file_path
input_file_path = filedialog.askopenfilename(
filetypes=(("CSV files", "*.csv"),))
app = TestApp(root, input_file_path)
app.place(bordermode = INSIDE,height = 500, width = 2000, x =0, y=50)
class TestApp(tk.Frame):
def __init__(self, parent, input_file_path, editable = True, enable_menus = True, table=table):
super().__init__(parent)
self.table = table
self.table.importCSV(input_file_path)
self.table.show(input_file_path)
self.table.addColumn('Current Status')
self.table.addColumn('Assign Technician')
self.table.autoResizeColumns()
root.mainloop()
Your table object is now accessible to anything that can see the module namespace. Here table is a mutable object, and all changes will be reflected to any function that accesses that object.
One suggestion: it is preferable to separate out your definitions (classes, functions) from your stateful bits that are created at run time. This will greatly help clarify dependencies. It is typical to use the line if __name__ == "__main__": at the bottom of the file to start the "script" part of your app, keeping all definitions above. I've seen some packages (looking at you, Flask!) break this convention a bit, and it can cause headaches.
On that note, your select_input_file function has a few issues. There's no good reason to create an app instance there. A better option would be to make this a method in the App class for example.

Related

How to store a value from Tkinter.Button on a var

I'm novice with python.
So i'm trying to make a GUI, when i click the button will open a directory selector(see on code 1),and i need storage the path from selection that i expected receive on "command" in a var, so i can use the directory path for another things.
Code 1
css.py
...
def selectFolder():
path = customtkinter.filedialog.askdirectory()
return path
Code 2
css.py
def button(root,txt,event = any):
button = customtkinter.CTkButton(master = root,text=txt,command = event)
return button;
...
Main code
main.py
root = css.root()
frame = css.frame(root)
frame.pack(pady=20, padx=60, fill='both',expand=True)
label = css.label(frame,'Tecverde - Engenharia S.A','Roboto',10)
label.pack(pady=12, padx=10)
path = css.button(frame,"Selecione a pasta",css.selectFolder)
path.pack()
root.mainloop()
You are using custontkinter, so all your widgets are 'CTk...'
Your button widgets can contain 'commands', my suggestion is, inside the function passed to the 'ask' command you can use tkinter's filedialog module, and use the output as needed.
This is an example of using Stringvar, a tkinter dynamic variable, whenever you use the command set('') in it you will modify its value for the entire code, and to get the value you can use .get () anywhere in the code.
Note that the debug label text changes after you dynamically select a directory.
from tkinter import filedialog
import customtkinter as css
root = css.CTk()
frame = css.CTkFrame(root)
frame.pack(pady=20, padx=60, fill='both', expand=True)
label = css.CTkLabel(frame, text='Tecverde - Engenharia S.A', font=('Roboto', 10))
label.pack(pady=12, padx=10)
def ask():
dir = filedialog.askdirectory()
path_var.set(dir)
path_var = css.StringVar(value='Empty Path')
path_button = css.CTkButton(frame, text="Selecione a pasta",
command=ask)
path_button.pack()
debug_label = css.CTkLabel(root, textvariable=path_var)
debug_label.pack()
root.mainloop()
In the second example, now using classes, self.path_var can be used anywhere in your code, even with a Stringvar as in the previous example, but now it has more options.
from tkinter import filedialog
import customtkinter as css
class App(css.CTk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
frame = css.CTkFrame(self)
frame.pack(pady=20, padx=60, fill='both', expand=True)
label = css.CTkLabel(frame, text='Tecverde - Engenharia S.A', font=('Roboto', 10))
label.pack(pady=12, padx=10)
self.path_var = 'Empty path'
path_button = css.CTkButton(frame, text="Selecione a pasta",
command=self.ask)
path_button.pack()
self.debug_label = css.CTkLabel(self, text=self.path_var)
self.debug_label.pack()
def ask(self):
dir = filedialog.askdirectory()
self.path_var = dir
self.debug_label.configure(text=dir)
if __name__ == '__main__':
app = App()
app.mainloop()

Import pandas table into tkinter project

I have used tkinter and its treeview widget thus far in my project to import and view some data from a csv file. However Im finding its functions limited as to what Im trying to achieve.
I have read in other SO questions that a Pandas data-frame can be imported to Tkinter project and display in the tkinter GUI. I have found some code online # https://gist.github.com/gugat/7cf57eb628f3bb0a3d54b3f8d0023b63 but I cant work out how to migrate this into my existing tkinter code.
import tkinter as tk
from tkinter import *
import tkinter.ttk as tkrttk
from PIL import Image, ImageFont, ImageTk
import csv
from tkinter import filedialog
import pandas as pd
from pandastable import Table, TableModel
root = tk.Tk()
root.geometry("2000x1000")
filepath = (r"C:/Users\James\Desktop\test_data.csv")
root.title('Workshop Manager')
style = tkrttk.Style()
style.configure("Treeview.Heading", foreground='Red', font=('Helvetica', 10))
df = pd.read_csv(filepath)
pt = Table(parent)
class TestApp(Frame):
"""Basic test frame for the table"""
def __init__(self, parent=root):
self.parent = parent
Frame.__init__(self)
self.main = self.master
self.main.geometry('600x400+200+100')
self.main.title('Table app')
f = Frame(self.main)
f.pack(fill=BOTH,expand=1)
df = TableModel.getSampleData()
self.table = pt = Table(f, dataframe=df,
showtoolbar=True, showstatusbar=True)
pt.show()
return
app = TestApp()
root.mainloop()
I get an error NameError name parent is not defined im assuming this pt = Table(parent) is my issue. I have tried pt = Table(root) as I thought this would place it on the tkinter root window. But this didnt work.
Part of your code is from the example used in the document of pandastable, but it is not a good example.
If you just want to show your CSV file using pandastable, below is a simple example:
import tkinter as tk
from pandastable import Table, TableModel
filepath = 'C:/Users/James/Desktop/test_data.csv'
root = tk.Tk()
root.geometry('1600x900+10+10')
root.title('Workshop Manager')
class TestApp(tk.Frame):
def __init__(self, parent, filepath):
super().__init__(parent)
self.table = Table(self, showtoolbar=True, showstatusbar=True)
self.table.importCSV(filepath)
self.table.show()
app = TestApp(root, filepath)
app.pack(fill=tk.BOTH, expand=1)
root.mainloop()

Python: how to share global states between classes and display them in the GUI?

I'd like to share global states (e.g. is_online or num_items) between classes like services and the GUI.
I'd like to be able to modify states everywhere: events, service init, etc.
Currently I store all shared states in globals.py, which can be modified everywhere. To display a state in the GUI in almost live, I just check the states every x seconds and update the GUI label.
It feels very dirty and as I'm new to Python I'd like to know how to do it properly.
This question is not opinion based. There should be a convention or best practice that I missed. The topic seems pretty common to me.
Any ideas? Thank you!
app.py
from Tkinter import *
from gui import *
from item_service import *
def main():
root = Tk()
GUI(root)
item_service = ItemService()
item_service.add_item()
root.mainloop()
main()
globals.py
num_items = 0
item_service.py
import globals
class ItemService(object):
def add_item(self):
globals.num_items += 1
def remove_item(self):
globals.num_items -= 1
GUI.py
from Tkinter import *
import globals
import time
import threading
from item_service import *
class GUI(Frame):
def __init__(self, parent, *args, **kwargs):
Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.render()
update_states_thread = threading.Thread(target=self.update_states).start()
def render(self):
self.root.title("My App")
self.root.geometry("500x500")
self.root.grid()
self.label = Label(self.root)
self.label.config(text = "Items: ?")
self.label.grid()
self.add_button = Button(self.root, text="+", command = self.add_item)
self.add_button.grid()
self.remove_button = Button(self.root, text="-", command = self.remove_item)
self.remove_button.grid()
def update_states(self):
while (True):
self.label.config(text = "Items: " + str(globals.num_items))
time.sleep(0.25)
def add_item(self):
item_service = ItemService()
item_service.add_item()
def remove_item(self):
item_service = ItemService()
item_service.remove_item()
There are several ways to store data like databases, files and so on.
For example, you can simply store your "state" in a sqlite database.
import sqlite3
# Connect to the sqlite database file
conn = sqlite3.connect('example.db')
c = conn.cursor()
# Create your table
c.execute('''CREATE TABLE states (num_items real)''')
# Update the state (update num_items to be 999)
c.execute("INSERT INTO states VALUES (999)")
# Save and close
conn.commit()
conn.close()
Source: https://docs.python.org/3/library/sqlite3.html

How to save askdirectory result in a variable I can use using tkinter with OOP?

I have ran into some trouble.
I'm quite new to OOP and working with tkinter and GUI's in general.
I have managed to find some code on the Internet and meshed it all together to create something and I'm nearly where I want to be.
So what I want is some help figuring this out.
How can I assign results of askdirectory to a variable I can use elsewhere?
# coding=utf-8
import tkinter as tk
from tkinter import font as tkfont
from tkinter import filedialog
class MainApp(tk.Tk):
....
class SelectFunction(tk.Frame):
....
class FunctionChangeName(tk.Frame):
....
a = Gui(self)
# this gets me the askdirectory but how to add it to a variable?
Above is the call to run askdirectory code, and it works, just need to find out how to save it to a variable so I can use it, I have tried to print it in several ways, but all I get is something along the lines .!frame.!functionchangename.!gui.
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
self.initial = initial
self.selected = initial
self.options = {'parent': container,'title': title,'initialdir':initial,}
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
def get(self):
return self.selected
class Gui(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
frame = tk.Frame(container)
frame.pack()
self.seldir = SelectDir(self, "Select directory", "D:\\MyPgm\\Python\\Tiles_8")
button = tk.Button(frame, text="Select directory", command=self.select_dir)
button.grid(column=0, row=0)
self.act_dir = tk.StringVar()
self.act_dir.set("D:\\MyPgm\\Python\\Tiles_8")
entry = tk.Entry(frame, textvariable=self.act_dir, width=30)
entry.grid(column=0, row=1)
def select_dir(self):
self.seldir.show()
self.act_dir.set(self.seldir.get())
# or
# result = seldir.show()
# self.act_dir.set(result)
if __name__ == "__main__":
app = MainApp()
app.mainloop()
I had an idea:
example, if you have f inside a function, you can make it global to have access as variable
def print_path():
# select working directory
global f #make f global to access the path
f = filedialog.askdirectory(parent=root, initialdir="/", title='Select Dir')

How to get value from a text field in another module?

I've written a simple program with a tkinter GUI. The entire code is in one big module and I'd like to split it into two or three modules to separate the logic from the GUI.
This is the example code:
main.py:
import gui
inst1 = gui.guitest()
gui.py:
import tkinter, defs
class guitest:
def __init__(self):
win1 = tkinter.Tk()
self.field1 = tkinter.Text(win1)
self.field1.grid(column = 0, row = 0)
self.but1 = tkinter.Button(win1, text='click',
command=defs.getVar)
self.but1.grid(column = 1, row = 0)
win1.mainloop()
defs.py:
def getVar():
captured = str(field1.get(1.0))
I can't get getVar to work; I'd like it to get the value from the Text field, but after trying different solutions all I get are Name or Attribute Errors.
Is there any possibility to make it work that way? Or maybe my idea is completely wrong? If it is, then please let me know how to do it. I wonder if there are more problems with this code.
Alright, let's start from the beginning, here's a working example of your code:
import tkinter
class guitest:
def __init__(self):
win1 = tkinter.Tk()
self.field1 = tkinter.Text(win1)
self.field1.grid(column=0, row=0)
self.but1 = tkinter.Button(win1, text='click', command=self.getVar)
self.but1.grid(column=1, row=0)
win1.mainloop()
def getVar(self):
captured = str(self.field1.get("1.0", tkinter.END))
print captured
inst1 = guitest()
Now, before breaking down that piece of code, you should ask yourself if the reason you want to is strong enough. In case your answer is affirmative (think it twice) one possible way to do it would be this:
# main.py
import gui
inst1 = gui.guitest()
# gui.py
import tkinter
import defs
class guitest:
def __init__(self):
win1 = tkinter.Tk()
self.field1 = tkinter.Text(win1)
self.field1.grid(column=0, row=0)
self.but1 = tkinter.Button(win1, text='click', command=self.getVar)
self.but1.grid(column=1, row=0)
win1.mainloop()
def getVar(self):
defs.getVar(self)
# defs.py
import tkinter
def getVar(guitest_inst):
captured = str(guitest_inst.field1.get("1.0", tkinter.END))
print captured
But again, think twice before breaking down widgets like this... just saying :)

Categories