I am completely new to Python and I wanted to create a simple script that will find me a specific value in a file and than perform some calculations with it.
So I have a .gcode file (it is a file with many thousands of lines for a 3D printer but it can be opened by any simple text editor). I wanted to create a simple Python program where when you start it, simple GUI opens, with button asking to select a .gcode file. Then after file is opened I would like the program to find specific line
; filament used = 22900.5mm (55.1cm3)
(above is the exact format of the line from the file) and extract the value 55.1 from it. Later I would like to do some simple calculations with this value. So far I have made a simple GUI and a button with open file option but I am stuck on how to get this value as a number (so it can be used in the equations later) from this file.
I hope I have explained my problem clear enough so somebody could help me :) Thank you in advance for the help!
My code so far:
from tkinter import *
import re
# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
# Define settings upon initialization. Here you can specify
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
#reference to the master widget, which is the tk window
self.master = master
#with that, we want to then run init_window, which doesn't yet exist
self.init_window()
#Creation of init_window
def init_window(self):
# changing the title of our master widget
self.master.title("Used Filament Data")
# allowing the widget to take the full space of the root window
self.pack(fill=BOTH, expand=1)
# creating a menu instance
menu = Menu(self.master)
self.master.config(menu=menu)
# create the file object)
file = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
file.add_command(label="Exit", command=self.client_exit)
#added "file" to our menu
menu.add_cascade(label="File", menu=file)
#Creating the button
quitButton = Button(self, text="Load GCODE",command=self.read_gcode)
quitButton.place(x=0, y=0)
def get_filament_value(self, filename):
with open(filename, 'r') as f_gcode:
data = f_gcode.read()
re_value = re.search('filament used = .*? \(([0-9.]+)', data)
if re_value:
value = float(re_value.group(1))
else:
print 'filament not found in {}'.format(root.fileName)
value = 0.0
return value
print get_filament_value('test.gcode')
def read_gcode(self):
root.fileName = filedialog.askopenfilename( filetypes = ( ("GCODE files", "*.gcode"),("All files", "*.*") ) )
self.value = self.get_filament_value(root.fileName)
def client_exit(self):
exit()
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("400x300")
#creation of an instance
app = Window(root)
#mainloop
root.mainloop()
You could use a regular expression to find the matching line in the gcode file. The following function loads the whole gcode file and does the search. If it is found, the value is returned as a float.
import re
def get_filament_value(filename):
with open(filename, 'r') as f_gcode:
data = f_gcode.read()
re_value = re.search('filament used = .*? \(([0-9.]+)', data)
if re_value:
value = float(re_value.group(1))
else:
print('filament not found in {}'.format(filename))
value = 0.0
return value
print(get_filament_value('test.gcode'))
Which for your file should display:
55.1
So your original code would look something like this:
from tkinter import *
import re
# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
# Define settings upon initialization. Here you can specify
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
#reference to the master widget, which is the tk window
self.master = master
#with that, we want to then run init_window, which doesn't yet exist
self.init_window()
#Creation of init_window
def init_window(self):
# changing the title of our master widget
self.master.title("Used Filament Data")
# allowing the widget to take the full space of the root window
self.pack(fill=BOTH, expand=1)
# creating a menu instance
menu = Menu(self.master)
self.master.config(menu=menu)
# create the file object)
file = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
file.add_command(label="Exit", command=self.client_exit)
#added "file" to our menu
menu.add_cascade(label="File", menu=file)
#Creating the button
quitButton = Button(self, text="Load GCODE",command=self.read_gcode)
quitButton.place(x=0, y=0)
# Load the gcode file in and extract the filament value
def get_filament_value(self, fileName):
with open(fileName, 'r') as f_gcode:
data = f_gcode.read()
re_value = re.search('filament used = .*? \(([0-9.]+)', data)
if re_value:
value = float(re_value.group(1))
print('filament value is {}'.format(value))
else:
value = 0.0
print('filament not found in {}'.format(fileName))
return value
def read_gcode(self):
root.fileName = filedialog.askopenfilename(filetypes = (("GCODE files", "*.gcode"), ("All files", "*.*")))
self.value = self.get_filament_value(root.fileName)
def client_exit(self):
exit()
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("400x300")
#creation of an instance
app = Window(root)
#mainloop
root.mainloop()
This would save the result as a float into a class variable called value.
Related
I have a python script for teachers that works very well on my terminal and I'm trying to use Tkinter so I can share this script with other people. In the script, the user uploads a file and other information to an SQLite database.
Below is part of the script that shows just the content for the 1st tab (of 3). The problem is that when people interact with the database the tab needs to get refreshed to show something has happened and that the list of files in the database has changed.
Everything I've read shows you need a button to refresh. But that is not user-friendly. What people want to do is upload a file and then see that file in the list. Is this possible with Tkinter and if so how?
class AppWindow():
my_list = [4]
def __init__(self, parent):
global my_list
# Create the window
self.window = parent
self.window.geometry("700x600")
#self.center_window()
self.window.title("Test app")
# Create a text label and place it in the window
self.hello_label = tk.Label(self.window, text="Hello world!")
self.hello_label.place(x=20, y=20)
# Create 3 tabs
self.tab_container = tk.Frame(self.window)
self.tab_container.place(x=0,y=0,width=700,height=400)
self.tabs = ttk.Notebook(self.tab_container)
self.tab_2 = tk.Frame(self.tabs)
self.tabs.add(self.tab_2, text="Parameters")
self.tabs.place(x=0,y=0,height=400,width=700)
# Content for tab 2
self.label2 = tk.Label(self.tab_2, text="")
self.label201=tk.Label(self.tab_2, text="Put in your target GPA")
self.label201.place(x=50, y=50)
btn1 = tk.Button(self.tab_2,text="Target GPA", command=self.getGPA)
btn1.place(x=50, y=80)
for lst in self.my_list:
btn99=tk.Button(self.tab_2,text=lst)
btn99.grid()
def getGPA(self):
userInput = sd.askstring('User Input','Enter target GPA')
self.my_list.append(userInput)
if __name__ == "__main__":
root = tk.Tk()
app = AppWindow(root)
root.mainloop()
This is merely a guess because of the all the reasons I've already mentioned in comments under your question. Generally speaking, to update or what you call "refresh" a tab requires adding, changing, or deleting the widgets you put on it.
The code below is based what's currently in your code. Every time the Target GP button is clicked another Button is added to the self.tab_2 frame as well as appended to my_list. Changing the widgets doesn't have to be triggered by clicking on a button, it could be the result of some other event (such as the completion of a file upload in the background for example).
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.simpledialog as sd
class AppWindow():
def __init__(self, parent):
# Create the window
self.window = parent
self.window.geometry("700x600")
#self.center_window()
self.window.title("Test app")
# Create and initialize data list.
self.my_list = [4]
# Create a text label and place it in the window
self.hello_label = tk.Label(self.window, text="Hello world!")
self.hello_label.place(x=20, y=20)
# Create 3 tabs
self.tab_container = tk.Frame(self.window)
self.tab_container.place(x=0, y=0, width=700, height=400)
self.tabs = ttk.Notebook(self.tab_container)
self.tab_2 = tk.Frame(self.tabs)
self.tabs.add(self.tab_2, text="Parameters")
self.tabs.place(x=0, y=0, height=400, width=700)
# Content for tab 2
self.label201 = tk.Label(self.tab_2, text="Put in your target GPA")
self.label201.place(x=50, y=50)
btn1 = tk.Button(self.tab_2, text="Target GPA", command=self.getGPA)
btn1.place(x=50, y=80)
# Create a Button for each item currently in list.
for item in self.my_list:
btn = tk.Button(self.tab_2, text=item)
btn.grid()
def getGPA(self):
userInput = sd.askstring('User Input', 'Enter target GPA')
if userInput is None: # User closed the dialog or clicked Cancel?
return
self.my_list.append(userInput)
btn = tk.Button(self.tab_2, text=userInput) # Create another Button.
btn.grid()
if __name__ == "__main__":
root = tk.Tk()
app = AppWindow(root)
root.mainloop()
I'm currently trying to create a desktop file converter application for myself using tkinter. It so far has a drag and drop area, and a button you can press to just select the file from the file explorer. However, I'm having trouble figuring out how to correctly position the widgets so they sit on top of each other. I want it so the drag and drop box is off the the left side of the screen, and in the middle of the box I want a text widget that says, "Drag and drop file, or select them", with a button widget below it that allows them to select from the file manager if they please.
import tkinter as tk
import tkinter.filedialog
from TkinterDnD2 import DND_FILES, TkinterDnD
from conversion import *
#global variables
path_to_file = " "
file_type = " "
compatable_converstion = []
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.master.title("File Converter")
self.master.minsize(1000,600)
self.master.maxsize(1200,800)
self.pack()
self.create_widgets()
def create_widgets(self):
#Drag and drop files area
self.drop_box = tk.Listbox(root, selectmode=tk.SINGLE, background="#99ff99")
self.drop_box.pack(ipadx=170)
self.drop_box.pack(ipady=120)
self.drop_box.pack(side="left")
self.drop_box.drop_target_register(DND_FILES)
self.drop_box.dnd_bind("<<Drop>>", open_dropped_file)
#Select file button
self.select_file = tk.Button(self)
self.select_file["text"] = "Select File"
self.select_file["command"] = self.open_selected_file
self.select_file.place(relx=1.0, rely=1.0, anchor="se")
#Instructional Text
sentence = "Drag and drop or select your file"
self.instructions = tk.Text(root)
self.instructions.insert(tk.END, sentence)
self.instructions.place(relx=1.0, rely=1.0, anchor="se")
def open_selected_file(self):
path_to_file = tk.filedialog.askopenfilename(initialdir="/", title="Select A File", filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
temp_str = " "
for chars in reversed(path_to_file):
if(chars == '.'):
break
temp_str += chars
file_type = temp_str[::-1]
compatable_converstion = retrieve_compatable_conversions(file_type)
def main():
global root
root = TkinterDnD.Tk()
app = Application(master=root)
app.mainloop()
if __name__ == "__main__":
main()
Just in case my explanation of how I want it laid out sucks, here is a picture:
You are creating the widgets self.drop_box and self.instructions on the root window. They should be created on self.
The place() geometry manager does not reserve space in a widget, so you will have to make the widget(self) as big as necessary with expand=True, fill='both'. I would advise using the grid() geometry manager for any design which is not really simple.
The widgets will be stacked in the order they are placed, which means that the Button will be hidden beneath the Text widget. Just change the order in which they are placed.
As for using ipadx and ipady in the pack() function, have a look at Why does the 'ipady' option in the tkinter.grid() method only add space below a widget?
Also; you don't need to make root a global variable because the application knows it as self.master.
Here is an example, not of the whole program but the parts I have mentioned:
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.master.title("File Converter")
self.master.minsize(1000,600)
self.master.maxsize(1200,800)
self.pack(expand=True, fill='both') # Fill entire root window
self.create_widgets()
def create_widgets(self):
#Drag and drop files area
self.drop_box = tk.Listbox(self, selectmode=tk.SINGLE, background="#99ff99")
self.drop_box.pack(side="left", ipadx=170, ipady=120)
self.drop_box.drop_target_register(DND_FILES)
self.drop_box.dnd_bind("<<Drop>>", open_dropped_file)
#Instructional Text
sentence = "Drag and drop or select your file"
self.instructions = tk.Text(self)
self.instructions.insert(tk.END, sentence)
self.instructions.place(relx=1.0, rely=1.0, anchor="se")
#Select file button
self.select_file = tk.Button(self, text="Select File",
command=self.open_selected_file)
self.select_file.place(relx=1.0, rely=1.0, anchor="se")
This should let you work out most of your problems.
I have a askopenfilename() function inside my main python file which gets a file directory from the user when a button is pressed. My main file is called payroll.py.
I have another file called dataframes.py which has a function that imports excel files using the read_excel() function and then manipulates the data.
This code is in payroll.py:
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import dataframes
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.title("SportClips Automation 0.0.1")
self.geometry('500x500')
self.resizable(width=False, height=False)
names = ['Instructions', 'Payroll']
self.nb = self.create_notebook(names)
self.menu = self.create_menus()
tab = self.nb.tabs['Instructions']
tk.Label(tab, text='-Select the "Payroll" tab to run Stylist
Compensation Worksheets').pack()
image = tk.PhotoImage(file="1.png")
tk.Label(tab, image=image).pack()
self.mainloop()
def create_notebook(self, names):
nb = MyNotebook(self, names)
nb.pack()
def add_label(parent, text, row, column):
label = ttk.Label(parent, text=text)
label.grid(row=row, column=column, sticky=tk.N, pady=10)
return label
def payroll():
nb.pr = dataframes.runpayroll(nb.name)
def getname():
nb.name = filedialog.askopenfilename(title="Select file",
filetypes=(("excel files",
"*.xls"), ("all files", "*.*")))
tab = nb.tabs['Payroll']
add_label(tab, 'Click this button to process payroll', 1, 8)
b1 = ttk.Button(tab, text="Run Payroll", command= payroll())
b1.grid(row=2, column=8)
b2 = ttk.Button(tab, text="Select SAR file", command=lambda:
getname())
b2.grid(row=3, column=8)
return nb
def create_menus(self):
menu = tk.Menu(self, tearoff=False)
self.config(menu=menu)
sub_menu = tk.Menu(menu, tearoff=False)
menu.add_cascade(label="File", menu=sub_menu)
sub_menu.add_separator()
sub_menu.add_command(label='Exit', command=self.destroy)
return menu
class MyNotebook(ttk.Notebook):
def __init__(self, master, names):
super().__init__(master, width=795, height=475)
# Create tabs & save them by name in a dictionary
self.tabs = {}
for name in names:
self.tabs[name] = tab = ttk.Frame(self)
self.add(tab, text=name)
GUI()
But I guess this error: AttributeError: 'MyNotebook' object has no attribute 'name'
If you want runpayroll to be able to access name, you have to pass it as an argument.
But the bigger problem is that you don't seem to have a name to pass it in the first place.
The only thing you have called name anywhere is a local variable in that getname() function, which isn't usable outside that function. All you do with it is return name, but the caller isn't doing anything with the return value, because it's just a Tkinter command callback.
You need to think about where you want to store these things. The usual answer is to create a class—either a subclass of Notebook, or a "controller" class that owns a Notebook—and store things as instance variables. As an inferior alternative, you can sometimes get away with storing things as global (or nonlocal) variables. But, however you decide to do it, someone has to assign the value to that instance or global variable somewhere. And then, you can pass it to run_payroll.
In this case, you seem to already have a MyNotebook class, and you're creating an instance of that, so maybe that's the right place to store things.
While we're at it, there's no reason to return anything from a function whose caller is just going to ignore the results. And also, you don't need lambda: payroll(); you can just use payroll for the same effect, but more readably (and even more efficiently).
So:
def create_notebook(self, names):
nb = MyNotebook(self, names)
nb.pack()
# …
def payroll():
nb.pr = dataframes.runpayroll(nb.name)
def getname():
nb.name = filedialog.askopenfilename(title="Select file",
filetypes=(("excel
files", "*.xls"), ("all files", "*.*")))
# …
And now, just change runpayroll to use that value:
def runpayroll(name):
df_sar = pd.read_excel(name,
sheet_name=0, header=None, skiprows=4)
I have made a small application with tkinter and Python 3 which has four buttons on the top of the window to form a menu. It works fine but I want to know how to make the buttons appear along the window over a period of time starting from a single button in the center when first started rather than being statically placed in the center.
Here is my script so far:
import tkinter as tk
class utilities(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.window()
def window(self):
self.pluginrun = tk.Button(self)
self.pluginrun["text"] = "Run Existing Plugin"
self.pluginrun["command"] = self.run_plugin
self.pluginrun.pack(side="left")
self.owning = tk.Button(self)
self.owning["text"] = "Add A New Plugin"
self.owning["command"] = self.plugin
self.owning.pack(side="left")
self.webpage = tk.Button(self)
self.webpage["text"] = "Webpage"
self.webpage["command"] = self.web
self.webpage.pack(side="left")
self.more_info = tk.Button(self)
self.more_info["text"] = "More"
self.more_info["command"] = self.more
self.more_info.pack(side="left")
def run_plugin(self):
print('Running Plugin')
def plugin(self):
print('Available Extensions')
def web(self):
print("Opening Webpage To Python.org")
def more(self):
print('Made Entirely In Python')
root = tk.Tk()
root.geometry('500x500')
show = utilities(master=root)
show.mainloop()
Which gives this result:
When first opened I would like it to look like this:
and over a period of time for more buttons to appear alongside one at a time until it looks like the first image.
How can this be done?
You can add all your buttons to a list and then use a repeating timed method to pack each button in the list one at a time at a set interval.
I created a counter that we can use to keep track of what button is going to be packed next from the list.
I also created a new list to store all the buttons in.
Then I modified your window() method to add each button to the list instead.
The last thing was to create a timed method that would use the self.counter attribute I created to keep track of what button is to be packed next.
In tkinter the best method to use to keep a timed loop or set a timer for anything is to use after(). Using sleep() or wait() in tkinter will only cause the entire tkinter app to freeze.
Take a look at the below code.
import tkinter as tk
class utilities(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.list_of_buttons = []
self.counter = 0
self.window()
def window(self):
for count in range(4):
self.list_of_buttons.append(tk.Button(self))
pluginrun = self.list_of_buttons[0]
pluginrun["text"] = "Run Existing Plugin"
pluginrun["command"] = self.run_plugin
owning = self.list_of_buttons[1]
owning["text"] = "Add A New Plugin"
owning["command"] = self.plugin
webpage = self.list_of_buttons[2]
webpage["text"] = "Webpage"
webpage["command"] = self.web
more_info = self.list_of_buttons[3]
more_info["text"] = "More"
more_info["command"] = self.more
self.timed_buttons()
def timed_buttons(self):
if self.counter != len(self.list_of_buttons):
self.list_of_buttons[self.counter].pack(side ="left")
self.counter +=1
root.after(1500, self.timed_buttons)
def run_plugin(self):
print('Running Plugin')
def plugin(self):
print('Available Extensions')
def web(self):
print("Opening Webpage To Python.org")
def more(self):
print('Made Entirely In Python')
root = tk.Tk()
root.geometry('500x500')
show = utilities(master=root)
show.mainloop()
Add the Buttons inside a Frame, which you centre, and then as you add more Buttons, the Frame should centre them. If not, you may need to call root.update(), to re-centre the Frame.
I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it