4

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()  
Bart
  • 101
  • 4
  • 11

1 Answers1

1

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.

Martin Evans
  • 45,791
  • 17
  • 81
  • 97
  • Thank you for the fast answer ! It looks nice, I will try to use it in a moment. I am unsure about one thing though, how to connect this bit that you wrote to the file that will be uploaded when you use the button. Because in this program different files will be used with varying names. So this function you wrote has to read the previously chosen file. Or maybe it does and I don't understand? – Bart Jan 07 '16 at 10:25
  • You pass the filename you want, so you could pass `root.fileName` for example. – Martin Evans Jan 07 '16 at 10:29
  • I have edited my main question with the new code that I have updated with your suggestions but I get this message: "expected an indented block" when I run it. – Bart Jan 07 '16 at 11:01
  • There is probably a mix of tab characters and spaces in the code, remove all leading characters from each line and respace them. Also you probably want to change my function to be a member function of your class by adding `self` as the first argument, and calling it as `self.get_filament_value()` – Martin Evans Jan 07 '16 at 11:04
  • Ok I edited the code again(changes are in the first post), but still I get this message. I am really sorry but I am a super total beginner in Python (and coding in general) – Bart Jan 07 '16 at 11:13
  • Indentation is very important in Python, changing it will change the meaning of the code. I have updated my solution, try testing this version. – Martin Evans Jan 07 '16 at 11:23
  • In the mean time I read about indentation, now I understand what it is and why it is so important. Your new code fixed this issue but now I get a following message: " Missing parentheses in call to 'print' ". And it indicates the line : `print 'filament value is {}'. value ` – Bart Jan 07 '16 at 11:32
  • I am guessing you are using Python 3. Print statements need to be in the form `print(....)` – Martin Evans Jan 07 '16 at 11:33
  • Yes I have a Python 3.5.0a4 and adding those parentheses allowed to compile. I got following error when after I opened the sample gcode file. – Bart Jan 07 '16 at 11:42
  • `Exception in Tkinter callback Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1540, in __call__ return self.func(*args) File "/Users/domagalski/Desktop/test.py", line 63, in read_gcode self.value = self.get_filament_value(root.fileName) File "/Users/domagalski/Desktop/test.py", line 55, in get_filament_value print ('filament value is {}'. value) AttributeError: 'str' object has no attribute 'value'` – Bart Jan 07 '16 at 11:42
  • Splendid ! :D Thank you so much !!! Now it works perfect, I still have some stuff to figure out but I think this one was the most difficult. Just to confirm now if I would like to use this value for further calculations I call it just "value" or I have to call it .format(value) – Bart Jan 07 '16 at 11:49
  • Great. It is saved in the Window class as `self.value`. It depends where you want to use it. Outside of the class you would use `app.value` – Martin Evans Jan 07 '16 at 11:51
  • Ok, thanks again, I still have a lot to read up about python but this will help me a lot to understand. – Bart Jan 07 '16 at 12:08
  • So I though that displaying this value in a tkinter window, under the button (after the file is uploaded) would be much easier. I am searching how to do it for last 2h with no luck, would it be to much to ask for your help in this matter too? – Bart Jan 07 '16 at 14:40
  • It would be better to raise a new StackOverflow question, I don't currently have that installed. – Martin Evans Jan 07 '16 at 14:45