Hiya,
In my class our teacher gave us the choice to do any language from the list vcaa approved. Whilst the resources and tutorials were in Visual Basic. The teacher was pretty hands off about teaching how to program himself. About half the class of 15 did VB, 4 went Python, a few did C# and I believe someone did web stuff.
Worked out fine. I did Python cause I wanted to learn it and had GREAT fun with tkinter (HA, decent intro to OOP tho) Python + tkinter was alright to learn by yourself. The hard parts is finding relevant code examples with a consistent style (hmu for the best documentation websites, and my particular setup/templates I ended up with). Most documentation for it was pre 2010 which is pretty sad. One of my friends decided to do Python + qt and their stuff looked really nice compared to old style defaults of tkinter.
Python is a great general language, just that there are much better ones to make GUIs which is what you are concerned about in Software Development. You want to do VB almost everytime due to the ease of the drag and drop separates the 2 parts into 2 problems and makes it easier to visualise the layouts, Python can be picked up in not much time at all especially if it's your second language.
I recall a time when I learnt that I could store those abstract Button widgets in an array and access them from that instead of calling them btnA, btnB, btnC... looping through them with string manipulation + the exec() function in Python haha.
First folio task, no idea about objects, arrays, pointers etc.
Spoiler
# Children's game 2
from tkinter import *
root = Tk()
feedback = StringVar()
feedback.set('Harro')
# Iterative buttons
button_names = [['a',0], ['b',1], ['c', 2]]
listy = []
listy2 = []
for name, num in button_names:
# Generates the buttons and assigns to iterative names
exec("photo%s = PhotoImage(file=name+'.png')" % (name))
exec("%s = Button(root, image=photo%s, command= lambda: \
change_colour(%s))" % (name, name, name))
eval(name).grid(row=1, column=num)
# Creates a multi-dimensional array of the variable name and the string of the variable name
listy += [[eval(name), name]]
button_names_pic = [['e',0], ['f',1], ['d', 2]]
function_list_pic = []
for name, num in button_names_pic:
# Generates the buttons and assigns to iterative names
exec("photo%s = PhotoImage(file=name+'.png')" % (name))
exec("%s = Button(root, image=photo%s, command= lambda: \
change_colour_pic(%s))" % (name, name, name))
eval(name).grid(row=2, column=num)
# Creates a list of variable name and the string of the variable name
listy2 += [[eval(name), name]]
butReset = Button(root, text="Reset", command=lambda: reset(), width=60)
butReset.grid(row=3, columnspan=3)
check_list = [['a', 'd'], ['b', 'e'], ['c', 'f']]
nice = PhotoImage(file='nice.png')
def reset():
for butname, picname in (check_list):
eval(butname).config(bg='blue')
eval(butname).config(image=eval('photo'+butname))
eval(picname).config(bg='blue')
eval(picname).config(image=eval('photo'+picname))
def check_active():
'''Checks whether the 2 buttons in row 1 and 2 are correct'''
first = 'poop'
second = 'poop'
first_but = None
second_but = None
for i, j in listy:
if i.cget("bg") == 'red':
first = j
first_but = i
for i, j in listy2:
if i.cget("bg") == 'red':
second = j
second_but = i
for i, j in check_list:
if first == i and second == j:
first_but.config(image=nice, bg='blue')
second_but.config(image=nice, bg='blue')
feedback.set('Noise')
def change_colour(name):
''' Changes pressed button to red but everything else to blue'''
name.config(bg='red')
for i, j in listy:
if i == name:
pass
else:
i.config(bg='blue')
feedback.set('Hmmm')
check_active()
def change_colour_pic(name):
''' changes pressed button to red but everything else to blue'''
name.config(bg='red')
for i, j in listy2:
if i == name:
pass
else:
i.config(bg='blue')
feedback.set('Okeh')
check_active()
title = Label(textvariable = feedback)
title.grid(row=0, columnspan=3)
mainloop()
Here is the main.py file for my SAT, still a monstrosity, no drag and drop remember.
Spoiler
"""Tkinter application to graphically plot your gains.
------------------------------------------------------
Steven Nguyen
21/08/16
------------------------------------------------------
This program is designed to be able to take workout data
and store it digitally. With the data and using the
tkinter, ttk and matplotlib libraries graphically
displays the information in a meaningful way for the user.
"""
import tkinter as tk
import tkinter.ttk as ttk
import pickle
import datetime
import ttkcalendar
from matgraph import MatGraph
from inputtab import InputTab
from sumpage import SumPage
from popupobj import Popup
from gainscontainer import work, workout
class Main(tk.Frame):
def __init__(s, m):
tk.Frame.__init__(s, m)
s.config(background='gray20')
# Initliase Data
s.dicExercise = s.loadData()
for key, value in s.dicExercise.items():
value.chronoSort()
s.ordKeyExercise = sorted(list(s.dicExercise.keys()))
# Lamba dictionary of all modes
s.dicMode = {'Avg Weight': lambda x: x.avgWeight(),
'Total Weight': lambda x: x.sumWeight(),
'Median Weight': lambda x: x.medWeight(),
'Highest Weight': lambda x: x.highestWeight()}
s.initialiseWidgets()
def initialiseWidgets(s):
# Expansion
s.grid_rowconfigure(0, weight=1)
s.grid_rowconfigure(1, weight=1)
s.grid_rowconfigure(2, weight=1)
s.grid_columnconfigure(0, weight=0)
s.grid_columnconfigure(1, weight=1)
# Icon Loading
iconPlus = tk.PhotoImage(file='icons\plus.png')
iconGraph = tk.PhotoImage(file='icons\graph.png')
iconSum = tk.PhotoImage(file='icons\sum.png')
# Buttons
s.btnInp = ttk.Button(s, image=iconPlus)
s.btnInp.config(command=lambda: s.switch(s.tabInp))
s.btnInp.image = iconPlus
s.btnMat = ttk.Button(s, image=iconGraph)
s.btnMat.config(command=lambda: s.switch(s.tabMat))
s.btnMat.image = iconGraph
s.btnSum = ttk.Button(s, image=iconSum)
s.btnSum.config(command=lambda: s.switch(s.tabSum))
s.btnSum.image = iconSum
s.btnInp.grid(row=0, column=0, sticky='news')
s.btnMat.grid(row=1, column=0, sticky='news')
s.btnSum.grid(row=2, column=0, sticky='news')
# Initialising tabs
s.initInp()
s.initMat()
s.initSum()
# Initilaised first tab to be the graph
s.currentTab = s.tabSum
s.switch(s.tabMat)
s.updatePlot(event=None)
def initInp(s):
"""
Initiates the InputTab object's widgets and data.
"""
s.tabInp = InputTab(s)
s.tabInp.chkExercise.config(values=s.ordKeyExercise)
if s.ordKeyExercise == []:
pass
else:
s.tabInp.varExercise.set(s.ordKeyExercise[0])
s.tabInp.btnDone.config(command=s.doneSesh)
s.tabInp.btnNew.config(command=s.callPop)
def initMat(s):
"""
Initiates the MatGraph object's widgets and data.
"""
s.tabMat = MatGraph(s)
if s.ordKeyExercise == []:
pass
else:
s.tabMat.varExercise.set(s.ordKeyExercise[0])
s.tabMat.chkExercise.config(values=s.ordKeyExercise)
s.tabMat.chkMode.config(values=list(s.dicMode.keys()))
s.tabMat.chkExercise.bind('<<ComboboxSelected>>', s.updatePlot)
s.tabMat.chkMode.bind('<<ComboboxSelected>>', s.updatePlot)
def initSum(s):
"""
Initiates the SumPage object's widgets and data.
"""
s.tabSum = SumPage(s)
s.tabSum.tree.bind('<Delete>', s.deleteSum)
s.tabSum.btnEdit.config(command=s.editSum)
s.tabSum.btnDelete.config(command=lambda: s.deleteSum(None))
s.updateSum()
def switch(s, chosenFrame):
"""
Function used to switch between tabs.
"""
if chosenFrame == s.currentTab:
return
else:
s.currentTab.grid_remove()
s.updatePlot(event=None)
chosenFrame.grid(row=0, column=1, rowspan=3, sticky='news')
s.currentTab = chosenFrame
s.updateDict()
def addNewInp(s, event):
"""
Adds a new, empty workout object to the dictionary while updating all widgets related.
"""
s.dicExercise[s.pop.add()] = workout(s.pop.add(), [])
s.pop.top.destroy()
s.updateDict()
s.initMat()
s.updatePlot(event=None)
def deleteSum(s, event):
"""
Deletes all selected in the tree and their respective entries in the dictionary.
"""
for i in reversed(list(s.tabSum.tree.selection())):
txt = s.tabSum.tree.item(i, 'text')
tags = s.tabSum.tree.item(i, 'tags')
if tags != '':
objWorkout = s.dicExercise[s.tabSum.tree.item(i, 'tags')[0]]
obj = s.tabSum.tree.item(i, 'tags')[1]
for i, objWork in reversed(list(enumerate(objWorkout.data))):
# Linear search, backwards to maintain indexing
if obj == str(objWork):
objWorkout.data.pop(i)
break
else:
s.dicExercise.pop(txt, None)
s.ordKeyExercise = sorted(list(s.dicExercise.keys()))
s.tabSum.delete()
s.updateDict(sumPage=False) # So the tree does not get collapsed
def editSum(s):
"""
Determines selected item and edits their name or data
for either workout or work object respectively
"""
if len(s.tabSum.tree.selection()) != 1:
return
item = s.tabSum.tree.selection()
itemDict = s.tabSum.tree.item(item)
# Determines object type and then proceeds
if itemDict['tags'] == '':
# Workout object
# Prompts user to rename
workoutName = itemDict['text']
s.callPop()
s.pop.btnAccept.config(command=lambda event=None: s.renameWorkout(event, workoutName))
s.pop.ent.bind("<Return>", lambda event: s.renameWorkout(event, workoutName))
else:
# Work object
# Switches tab to input page with details filled in
workoutName = itemDict['tags'][1]
workoutObj = s.dicExercise[itemDict['tags'][0]]
for workObj in workoutObj.data:
# Linear search through object data to compare strings
if str(workObj) == workoutName:
s.deleteSum(event=None)
s.switch(s.tabInp)
s.tabInp.tree.delete(*s.tabInp.tree.get_children())
s.tabInp.varDate.set(workObj.date.strftime('%d/%m/%y'))
s.tabInp.varExercise.set(itemDict['tags'][0])
for rep, weight in zip(workObj.reps, workObj.weight):
s.tabInp.tree.insert('', tk.END, value=[rep, weight])
break
def renameWorkout(s, event, oldname):
"""
Renames workout object by replacing the original and copying.
oldname: Current name of exercise selected to be edited.
"""
newname = s.pop.add()
s.pop.onEsc(event=None)
# Dictionary pop method returns the value
s.dicExercise[newname] = s.dicExercise.pop(oldname)
s.updateDict(sumPage=True)
def updatePlot(s, event):
"""
Updates plots to current dictionary.
"""
if s.ordKeyExercise == []: # Existence Check
s.tabMat.mainplot.clear()
s.tabMat.fig.autofmt_xdate()
s.tabMat.canvas.show()
return
exercise = s.tabMat.varExercise.get()
mode = s.tabMat.varMode.get()
x = s.dicExercise[exercise].dates()
y = s.dicMode[mode](s.dicExercise[exercise])
s.tabMat.mainplot.clear()
s.tabMat.mainplot.plot(x, y)
s.tabMat.mainplot.scatter(x, y)
s.tabMat.fig.autofmt_xdate()
s.tabMat.canvas.show()
def updateDict(s, sumPage=True):
"""
Updates all checkboxes linked to the s.dicExercise to the current.
sumPage: Boolean option to choose if summary page is to be
specifically updated or not. Used exactly once.
"""
s.ordKeyExercise = sorted(list(s.dicExercise.keys()))
if s.ordKeyExercise == []:
setter = ''
else:
setter = s.ordKeyExercise[0]
s.tabInp.chkExercise.config(values=s.ordKeyExercise)
s.tabInp.varExercise.set(setter)
s.tabMat.chkExercise.config(values=s.ordKeyExercise)
s.tabMat.varExercise.set(setter)
if sumPage == True:
# To allow for deletion in the sumpage without resetting the tree
s.updateSum()
def updateSum(s):
"""
Puts all the data from s.dicExercise into the tree in a sorted order.
"""
s.tabSum.tree.delete(*s.tabSum.tree.get_children())
for strEx in s.ordKeyExercise:
objEx = s.dicExercise[strEx]
id = s.tabSum.tree.insert('', tk.END, text=strEx)
for date, objWork in zip(objEx.dates(), objEx.data):
id2 = s.tabSum.tree.insert(
id, tk.END, values=(objWork.weight, objWork.reps),
text=str(date), tags=(strEx, objWork))
def doneSesh(s):
"""
Updates the s.dicExercise with the current entries of tabInp.tree into s.dixExercise.
"""
selectedDate = s.tabInp.selectedDate
lstReps = [float(s.tabInp.tree.item(i)['values'][0])
for i in s.tabInp.tree.get_children()]
lstWeight = [float(s.tabInp.tree.item(i)['values'][1])
for i in s.tabInp.tree.get_children()]
if lstReps == [] or lstWeight == []:
return
new = work(lstReps, lstWeight, selectedDate)
s.dicExercise[s.tabInp.varExercise.get()].data.append(new)
s.dicExercise[s.tabInp.varExercise.get()].chronoSort()
s.tabInp.tree.delete(*s.tabInp.tree.get_children()) # Clears the tree
s.updateDict()
s.save()
def onClose(s):
"""
Procedure to run before closing the app, saves all data.
"""
s.save()
root.destroy()
def loadData(s):
"""
Returns the save data within data.pkl.
"""
with open('data.pkl', 'rb') as file:
return pickle.load(file)
def save(s):
"""
Saves any changes to data.pkl.
Currently only connected to s.onClose.
"""
with open('data.pkl', 'wb') as output:
pickle.dump(s.dicExercise, output)
def callPop(s):
"""
Calls a general popup with an entry and accept button.
"""
try:
s.pop.top.destroy()
except:
None
s.pop = Popup(s)
s.pop.btnAccept.config(command=lambda: s.addNewInp(event=None))
s.pop.ent.bind("<Return>", s.addNewInp)
if __name__ == '__main__':
root = tk.Tk()
app = Main(root)
app.pack(expand=1, fill='both')
root.geometry('825x450')
root.protocol("WM_DELETE_WINDOW", app.onClose)
root.iconbitmap('icons\icon.ico')
root.title('Gains Grapher')
root.mainloop()
:/ unable to combine spoiler and code tags.