I've been playing around with Tkinter this week. What is Tkinter? Tkinter allows you to build GUI based applications in Python. It's not the only GUI library for Python, but it is included when you install Python. So pretty much any install of Python is going to include Tkinter. I just repeated myself didn't I? Oh well. So why build GUI based applications. Well for ease of use. Most end-users today, would laugh if you gave them a console application. Despite the fact console applications can often be more efficient than GUI based ones. But at the end of the day, GUI applications just look and feel better.
After playing around with Tkinter, I found I was impressed with how simple it was to get a basic form going. It was so simple, that it was fun. Doing the same in .NET forms would have taken a bit more time. Honestly, unless you need a lot of bells and whistles, Tkinter just works. There have been many times where I needed a simple forms application to make doing certain tasks easier. For example data collection. But sometimes we just don't have time to build tools. This is where I think Tkinter could come in. I thought, what if I made myself a template for data collection. So I did and I'm happy to share it with you.
I present to you, "Quick Form." It's very simple to edit. It doesn't validate what you input, but if you need something quick and dirty to collect some data. This will do the trick! All you need to do is edit the following script, change or add the field names and Bob's your uncle. You can even edit the call back function to do whatever you need. It will pass the data from the form to your call back function. The call back I wrote in the example below creates or appends a CSV file with the data entered in the form. After clicking submit, the call back is called, the form is cleared and the focus is returned to the first field. You can enter hundreds of entries without ever touching your mouse. Now that's what I call efficiency.
This script is free for your use. Just has always, we're not responsible for possible damages. Till next time, happy programing!
import tkinter as tk
import os, csv
class formField:
def __init__(self, name, description):
self.name = name
self.description = description
class form:
def __init__(self, title, formFields, callback):
self.title = title
self.formFields = formFields
self.callback = callback
self.window = tk.Tk()
self.window.title("Quick Form")
self.window.columnconfigure(1, minsize=250)
self.fields = []
for idx, formField in enumerate(self.formFields):
label = tk.Label(master=self.window, text=formField.description)
label.grid(row=idx, column=0, padx=5, pady=5, sticky="w")
entry = tk.Entry(master=self.window)
entry.grid(row=idx, column=1, padx=5, pady=5, sticky="we")
self.fields.append(entry)
self.lblMessages = tk.Label(master=self.window, text="")
self.lblMessages.grid(row=len(self.fields), column=0, columnspan=2, padx=5, pady=5)
self.btnSubmit = tk.Button(master=self.window, text="Submit")
self.btnSubmit.grid(row=len(self.fields)+1, column=0, columnspan=2, padx=5, pady=5)
self.btnSubmit.bind("", self.submit)
self.fields[0].focus()
# clear messages when new data is entered
self.window.bind('', self.clearMessages)
self.window.mainloop()
def clearMessages(self, event):
self.lblMessages.config(text="")
def submit(self, event):
self.lblMessages.config(text="")
values = self.getFieldValues()
message = self.callback(values)
self.lblMessages.config(text=message)
self.clearForm()
self.fields[0].focus()
# returns an dictionary of form field values, keyed by field name
def getFieldValues(self):
values = {}
for idx, field in enumerate(self.fields):
values[self.formFields[idx].name] = field.get()
return values
def clearForm(self):
for idx, field in enumerate(self.fields):
field.delete(0, len(field.get()))
def callback(values):
filename = "output.csv"
# does the file exist?
exists = os.path.exists(filename)
try:
with open(filename, "a" if exists else "w") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=list(values.keys()), lineterminator="\n")
if not exists: writer.writeheader()
writer.writerow(values)
return "Wrote 1 Line"
except IOError:
return "I/O error"
formFields = []
formFields.append(formField(name="field1", description="Field 1"))
formFields.append(formField(name="field2", description="Field 2"))
formFields.append(formField(name="field3", description="Field 3"))
formFields.append(formField(name="field4", description="Field 4"))
formFields.append(formField(name="field5", description="Field 5"))
formFields.append(formField(name="field6", description="Field 6"))
form = form(title="Quick Form", formFields=formFields, callback=callback)
I haven't featured woodworking before on this blog. But I do enjoy doing some from time to time. I try to learn new techniques whenever possible. Sometimes taking on projects just so I can learn something new. Right now I'm working on building a display shelf for items found in a advent calendar. So I thought I would make a simple grid shelf design. The goal is to try and make everything by hand using only hand tools. Also to limit the use of mechanical fasteners. In order to do this, I needed to learn how to cut dados.
What are dados? A dado is a type of joint. You may have seen them before on bookcases to hold the shelves. It's this little groove that allows another piece of wood to fit into. They're not too hard to make. But making small dados can be challenging. I ended up finding a great video to help make this process easier. You can check it out below. The biggest advise I can give is take your time. Being in a hurry just makes mistakes happen faster. Below are the steps that work for me. These notes may be more useful for myself, but I thought I'd share them and the video I found helpful.
Steps
Mark cut lines with a pencil.
Using a knife, score the lines.
Go back over the lines with a chisel.
Chisel and bezel a saw guide on the top lines (inside out).
From time to time you find tools that make your life easier. But often we don't share or give credit to those tools. Today, I thought I would share a tool I've been using over the last year or so. This tool allows you to record your screen and produce GIFs. I've found a number of great uses for this tool. My first encounter was while doing troubleshooting for a software bug. While working as a manager of a software development team a support technician reported an issue with our software. Our developers could not reproduce the problem. The classic "it works on my machine" type scenario. Unfortunately we didn't have time setup a meeting for the technical support staff to demonstrate the bug. I got thinking, a picture is a thousand words. But a video much more. So I went looking for a simple way for them to record their screen. That's when I came across Screen To GIF.
Screen To GIF is a Windows application that can be installed or ran as a portable app. It's very simple to use. Just launch the software and you're presented with a simple record button. Click record and another window will open that behaves similar to a view finder on a camera. Whatever's in the window will be captured in the final GIF. Once you're done recording you're presented with a video editor. You can crop and edit to your hearts content. Then export the video as a GIF.
In my use case it worked perfectly. They were able to capture the bug. I shared it with the team and we solved the problem. Even as well as they described the behavior, they left out important details. Having a video captured the missing details. One thing to note is that GIFs can get rather large the longer they go. If what you're capturing is longer than 30 seconds, I would suggest using a video capturing application. But we'll save that for another post. Till then happy clip'n.
While working on the Matching Quiz, I realized Javascript is missing one of my favorite Python functions: Range. For those who don't use Python, Range allows you to generate a list of numbers. It doesn't sound useful on the surface, but trust me it is. Note I will be using the term list and array interchangeably throughout this article. I'm lazy like that. In Python, range is often used to generate enumeration for loops. In my case I needed to generate a list of numbers in random order. This controls the sorting for the Matching Quiz. Now Python's Range function doesn't do randomization by default, it's something extra I added for my use case. So how do you use Range? It's simple, let's walk through the parameters.
Parameters
The start parameter is the lowest number in your generated set of numbers. For example if you're wanting a list of numbers from 1 to 10, you would specify 1 for the start parameter. Next is the stop parameter. This tells the script to stop generating numbers, however it is not included in the list of numbers. For example if you wanted a list of numbers from 1 - 10, you would have to specify 11 for the stop parameter. This behavior may seem odd at first, but this is how Python implements the function. Next is the step parameter. This parameter is optional. But this allows you to control how many steps between each number in the list. For example, say you want only odd numbers from 1 - 10 in your list. You would start with 1, stop with 11 and step with 2 (range(1, 11, 2)). The end result would be [1, 3, 5, 7, 9]. Pretty neat huh? Ok Matt, what's the random parameter? "randomize_order" is an optional bonus parameter I added for my use case. I needed the numbers to be returned in a random order. It defaults to false, so if you don't pass it. You'll end up with a standard list of numbers.
function range(start, stop, step = 1, randomize_order = false) {
let arr = [];
for(let i = start; i < stop; i += step) {
arr.push(i);
}
if(randomize_order) arr = arr.sort(() => Math.random() - 0.5);
return arr;
}
Conclusion
Feel free to grab and use the code as you like. As always it's not our responsibility for damages (if they happen). Yotta yotta. But this is a great away to have the best of both worlds when switching back and forth between Python and Javascript.
I've been working on putting together a matching quiz app as part of an upcoming feature on my tv and movie car blog. But I wanted to make it reusable by myself and others. How did I pull that off? Well with the power of JSON! Wow, that really does sound like something He-man would say. But anyways. I wrote a basic script to build the quiz using some classes and objects. Then added in some objects to validate and score the quiz. To make it reusable I added in a parameter in the query string to allow the user to pass in a URL for the JSON configuration file (details below). When the parameter "source" is passed the script will load the JSON configuration file from that path and setup the quiz.
Build Your Own Quiz
Building your own quiz is pretty simple. All you need to do is create a JSON configuration file using the template below. Upload the completed JSON configuration file to a public site that allows CORS. I used npoint.io. If using npoint.io, once you've upload the file they'll issue you a special URL. Save this address. Now for the magic. Copy the following URL and replace "" with the special address you saved earlier. Open up the new address in your browser and the quiz will be live. You can share this link with anyone. Or in my case I'll be embedding it in my blog in an iframe like above.