Missing Argument / issues opening GUI program on macOS Big Sur - python-3.x

This is my first GUI program and I am having some major issues. I really need some help. First, I cannot get the program to open on my computer (mac). When running in Idle IDE I get this error message: import Tkinter
ModuleNotFoundError: No module named 'Tkinter'.
I have 3.9 installed which I thought had a GUI interface.
When debugging in VS Code i get this error message # line 44:
Exception has occurred: TypeError
init() takes at least 4 arguments (3 given)
I think I have 4
I'm not sure where to begin with these issues. From my research it appears that there is an issue running GUI programs on macs updated higher then 11.1.
Code is below
# Create a Tkinter GUI program that converts Celsius Temp to Fahrenheit
# F == Fahrenheit
# C == Celsius
# Tkinter imported
import Tkinter
# Global variable used
temp_val = 'Celsius'
#Set value for drop down menu
def store_temp (set_temp):
global temp_val
temp_Val = set_temp
class TemperatureConverter:
def __init__(self):
# create main window
self.main_window = Tkinter.Tk()
# create a title for window
self.main_window.title('Temperature Converter')
# create three frames
self.top_frame = Tkinter.Frame()
self.option_frame = Tkinter.Frame()
self.mid_frame = Tkinter.Frame()
self.bottom_frame = Tkinter.Frame()
#create widget for top frame
self.prompt_label = Tkinter.Label(self.top_frame, text= 'Enter a temperature in Celsius: ')
#pack top frame
self.prompt_label.pack(side='left')
# create str variable obj to hold empty string variable
self.inputNumber = Tkinter.StringVar()
self.var = Tkinter.StringVar()
# create widget for option drop down menu
self.entry = Tkinter.Entry(self.option_frame, textvariable=self.inputNumber )
self.dropDownList = ['Celsius','Fahrenheit']
self.drop_down = Tkinter.OptionMenu(self.option_frame, value=self.var , values=self.dropDownList, command=store_temp)
self.var.set(dropDownList[0])
# option widgets packed
self.entry.pack(side='right')
self.dropDownList.pack(side='left')
#create widget for middle frame
self.result_label = Tkinter.Label(self.mid_frame)
# create widgets for bottom frame
self.call_convert = (call_convert , result_label, inputNumber)
self.convert_button = Tkinter.Button(self.bottom_frame, text='Convert', command=self.call_convert)
self.quit_button= Tkinter.Button(self.bottom_frame, text= 'Quit', command= self.main_window.destroy)
#pack the buttons
self.convert_button.pack(side='left')
self.quit_button.pack(side='left')
#pack the frames
self.top_frame.pack()
self.option_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
#Enter the tkinter main loop
Tkinter.mainloop()
# convert method is callback fucntion for convert button
def call_convert(self):
if temp_Val == 'Celsius':
f = float((float(temp)* 9/5)+32)
self.result_label.config(text='The temperature in Fahrenhiet is:')
if temp_Val == 'Fahrenheit':
c = float((float(temp)-32) * 5 / 9)
self.result_label.config(text='The temperature in Celsius is:')
if __name__ == '__main__':
temp_converter = TemperatureConverter()

There were a lot of bugs in your code. I fixed all of them (I think). I had to guess where you wanted to put the label with the results. I also had to fix all of the indentations. This is the working code:
# Create a tkinter GUI program that converts Celsius Temp to Fahrenheit
# F == Fahrenheit
# C == Celsius
# tkinter imported
import tkinter
class TemperatureConverter:
def __init__(self):
# create main window
self.main_window = tkinter.Tk()
# create a title for window
self.main_window.title("Temperature Converter")
# create three frames
self.top_frame = tkinter.Frame(self.main_window)
self.option_frame = tkinter.Frame(self.main_window)
self.mid_frame = tkinter.Frame(self.main_window)
self.bottom_frame = tkinter.Frame(self.main_window)
# create widget for top frame
self.prompt_label = tkinter.Label(self.top_frame, text="Enter a temperature in Celsius:")
# pack top frame
self.prompt_label.pack(side="left")
# create str variable obj to hold empty string variable
self.inputNumber = tkinter.StringVar(self.main_window)
self.var = tkinter.StringVar()
# create widget for option drop down menu
self.entry = tkinter.Entry(self.option_frame, textvariable=self.inputNumber)
self.dropDownList = ["Celsius", "Fahrenheit"]
self.drop_down = tkinter.OptionMenu(self.option_frame, self.var, *self.dropDownList)
self.var.set(self.dropDownList[0])
# option widgets packed
self.entry.pack(side="right")
self.drop_down.pack(side="left")
# create widget for middle frame
self.result_label = tkinter.Label(self.mid_frame)
# create widgets for bottom frame
self.convert_button = tkinter.Button(self.bottom_frame, text="Convert", command=self.call_convert)
self.quit_button= tkinter.Button(self.bottom_frame, text= "Quit", command=self.main_window.destroy)
# pack the buttons
self.convert_button.pack(side="left")
self.quit_button.pack(side="left")
# pack the frames
self.top_frame.pack()
self.option_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
# It is better to call `<tkinter.Tk>.mainloop()`
self.main_window.mainloop()
# convert method is callback fucntion for convert button
def call_convert(self):
if self.var.get() == "Celsius":
f = float((float(self.entry.get())* 9/5)+32)
self.result_label.config(text="The temperature in Fahrenhiet is: "+str(f))
if self.var.get() == "Fahrenheit":
c = float((float(self.entry.get())-32) * 5 / 9)
self.result_label.config(text="The temperature in Celsius is: "+str(c))
self.result_label.pack(side="bottom")
if __name__ == "__main__":
temp_converter = TemperatureConverter()
Look at what I did for the OptionMenu and look at how I fixed you call_convert function. If you have any specific questions, tell me and I will try to answer them.
By the way I don't think any of the errors you were getting were caoused by your OS. Also I suggest that next time you use import tkinter as tk as it will make it way easier to write code.

Related

Python3, difficulty with classes and tkinter

First of all to kick it off,
I'm not great at programming,
I have difficulty with understanding most basics,
I always try doing my uterly best to solve things like this myself.
I'm trying to create a simple gui that makes json files. Everything works fine. Fine in the sense that I'm able to create the files.
Now I wanted to get my code cleaned up and to the next level. I have added tabs to the tkinter screen and that is where the troubles starts. Because when I'm, on a differend tab, the function doesn't get the current selected items, so I added buttons to save that list and then move to different tab.
I have a function(Save_List_t), which looks at the selected items from the listbox(a_lsb1) and saves them to a list(choice_list_t). This function runs when I press button(a_button).
After doing that I got a problem, I don't want to use "global" but I need the list in a other function(Mitre_Gen_Techs) to generate the files. This function runs when I press a button on the third tab.(c.button1)
To tackel this problem, I saw a post where someone uses a class to fix it. However even after reading to the documentation about classes I still don't truely get it.
Now I'm stuck and get the error. Which I don't find that strange, it makes sense to me why it gives the error but what am I doing wrong or how do I solve this issue.
The error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\thans\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
TypeError: Save_List_t() missing 1 required positional argument: 'self'
The code I wrote:
from tkinter import *
from attackcti import attack_client
from mitretemplategen import *
from tkinter import ttk
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mitre ATT&Ck
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ac = attack_client()
groups = ac.get_groups()
groups = ac.remove_revoked(groups)
techs = ac.get_enterprise_techniques()
techs = ac.remove_revoked(techs)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Tkinter screen setup
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
root = Tk()
root.title("Mitre Att&ck")
root.minsize(900, 800)
root.wm_iconbitmap('')
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Functions / classes
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Screen(object):
def __init__(self):
self.choice_list_t = []
self.choice_list_g = []
def Save_List_t(self):
for item in a_lsb2.curselection():
self.choice_list_t.append(a_lsb2.get(item))
print(self.choice_list_t)
def Save_List_g(self):
choice_list_g = []
for item in b_lsb1.curselection():
self.choice_list_g.append(b_lsb1.get(item))
print(self.choice_list_g)
def Mitre_Gen_Techs(self):
# Gen the json file
# mitre_gen_techs(self.choice_list_t, techs)
#testing class
print(self.choice_list_t)
def Mitre_Gen_Groups(self):
# Gen the json file
# mitre_gen_groups(self.choice_list_g, groups)
#testing class
print(self.choice_list_g)
def main():
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# First Tkinter tab
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
rows = 0
while rows < 50:
root.rowconfigure(rows, weight=1)
root.columnconfigure(rows, weight=1)
rows += 1
# Notebook creating tabs
nb = ttk.Notebook(root)
nb.grid(row=1, column=0, columnspan=50, rowspan=50, sticky='NESW')
# Create the differend tabs on the notebook
tab_one = Frame(nb)
nb.add(tab_one, text='APG')
tab_two = Frame(nb)
nb.add(tab_two, text='Actors')
tab_gen = Frame(nb)
nb.add(tab_gen, text='test')
# =-=- First Tab -=-=
# List box 1
a_lsb1 = Listbox(tab_one, height=30, width=30, selectmode=MULTIPLE)
# List with techs
a_lsb2 = Listbox(tab_one, height=30, width=30, selectmode=MULTIPLE)
for t in techs:
a_lsb2.insert(END, t['name'])
# Save list, to later use in Screen.Mitre_Gen_Techs
a_button = Button(tab_one, text="Save selected", command=Screen.Save_List_t)
# =-=- Second Tab -=-=
# List with TA's
b_lsb1 = Listbox(tab_two, height=30, width=30, selectmode=MULTIPLE)
for g in groups:
b_lsb1.insert(END, g['name'])
# Save list, to later use in Screen.Mitre_Gen_Groups
b_button = Button(tab_two, text="Save selected", command=Screen.Save_List_g)
# =-=- Third tab -=-=
c_button = Button(tab_gen, text="Print group json", command=Screen.Mitre_Gen_Groups)
c_button1 = Button(tab_gen, text="Print techs json", command=Screen.Mitre_Gen_Techs)
# Placing the items on the grid
a_lsb1.grid(row=1, column=1)
a_lsb2.grid(row=1, column=2)
b_lsb1.grid(row=1, column=1)
a_button.grid(row=2, column=1)
b_button.grid(row=2, column=1)
c_button.grid(row=2, column=1)
c_button1.grid(row=2, column=2)
root.mainloop()
# If main file then run: main()
if __name__ == "__main__":
main()
The application:
Image
I found someone who explained what was wrong.
Credits to Scriptman ( ^ , ^ ) /
simply adding:
sc = Screen()
And changing:
Button(tab_apg, text="Save selected", command=sc.Save_List_t)
Resolved the issue.

Returning variable from a function

I have a main function page and i linked the next page with a button
when i click the button it executes a function in the main page but.
i want to use the result from the function in another file as a variable
########main.py
class Main(object):
def __init__(self,master):
self.master = master
mainFrame = Frame(self.master)
mainFrame.pack()
topFrame= Frame(mainFrame,width=1050,height =50, bg="#f8f8f8",padx =20, relief =SUNKEN,
borderwidth=2)
topFrame.pack(side=TOP,fill = X)
self.btnselfolder= Button(topFrame, text="Select Folder", compound=LEFT,
font="arial 12 bold", command=self.selectFolder)
self.btnselfolder.pack(side=LEFT)
def selectFolder(self):
print("folder")
return folder
################# selectfolder page
class Page2(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry("450x300")
self.title("page2")
self.resizable(False,False)
self.topFrame = Frame(self, width=350,height=150, bg="white")
self.topFrame.pack(fill=X)
# call the function from main.py and it will give me the same output folder
y = selectFolder()
Since the selectFolder method is not static, you'll have to access it using instance. Like this:
main = Main()
folder = main.selectFolder()
folder should hold the value you returned.
I'm afraid when i just copied and pasted your code into my IDLE directly, it immediately presented me with naming errors. Not even a Tkinter window popped up. So to clear things up I would go about making a Tkinter window like the following. Also bare in mind I don't really use or know how to integrate classes with Tkinter itself, but I quickly learned how to by your mistakes :)
# import tkinter from the libraries
from tkinter import *
import os
# the Zen of Python
#import this
# declare the tkinter window variable
# scroll to bottom to see mainloop
root = Tk()
########main.py
# folder is not defined as a variable
# but i recommend looking into
# using system of which i imported
# for you. You can research how to
# use it yourself for good practice
# i am sorry if i am wrong about this
# but my impression is you're trying to
# use the OS to select a folder from a
# directory. So the 'selectFolder' function
# should not be declared as a method within the class
# therefore rendering the Page2 class useless.
def selectFolder():
print("folder")
# return will result in an error
# because it has not been declared as a variable
# return folder
class Main():
# all instances instantiated (created)
# within the __init__ method
# must be declared in the parentheses
def __init__(self, topFrame, btnselfolder):
# instantiate the topFrame object
self.topFrame = topFrame
# instantiate the btnselfolder
self.btnselfolder = btnselfolder
# pro tip - when having multiple
# arguments within an object, then
# to keep it clean and easy to read
# space out the code like i have for
# you. You should also read the "Zen of Python"
# which is at the top as 'import this'
# however i have commented it out for you
topFrame = Frame(root,
width=1050,
height = 50,
bg = "#f8f8f8",
padx = 20,
relief = SUNKEN,
borderwidth=2)
topFrame.pack(side=TOP, fill = X)
btnselfolder = Button(topFrame,
text = "Select Folder",
compound=LEFT,
font = "arial 12 bold",
command = selectFolder).pack()
# mainloop() method will keep the window open
# so that it doesn't appear for a millisecond
# and dissapear :)
root.mainloop()
# I hope this has been a big help
# Thanks for posting this question! :-)
# Enjoy your day, good luck and be safe in Lockdown!
Thanks again for the question, I thoroughly enjoyed solving it or at least giving you some guidence! :)

How do I get the value from an option in tkinter after it has passed through a function?

I'm using tkinter to create an option menu for a user to interact with, when the selection has been made it will pass through a function to return an integer based on the input. I want to be able to return the integer back to a variable outside of the function so it can be received by another file. My problem is that python will keep returning the button as the command has not yet been processed.
from tkinter import *
Final = [] ##List containing options
master = Tk()
variable = StringVar(master)
variable.set(Final[0]) # default value
w = OptionMenu(master, variable, *Final)
w.pack()
def ok():
global choice
choice = variable.get()
global x
x = 0
for i in Final:
if str(i) == str(choice):
break
x += 1
button = Button(master, text="Choose", command=ok)
button.pack()
values = x

Python3: GUI later cannot display output properly?

I'm designing a GUI application that converts between celsius and fahrenheit. For now, there're primarily two problems that I'm not able to tackle:
1) When I enter an integer that needs to be converted based on the given conversion formula, the Label from tkinter cannot display the output properly. In fact, it shows something like this:
<conversionModel.Conversion object at 0x1057b11d0>
which made it really difficult for debug to a beginner like me.
2) There's a quitButton, thought which we can destroy() the GUI application. The problem is that when I close the GUI by clicking the red cross of the window, the Shell says:
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed
I checked answers to other questions regarding the same problem, it turned out that it was because this GUI application was destroyed before closing. I had no idea how to address this particular problem.
Below are three pieces of code written in Model/View/Controller form:
The Model in conversionModel.py:
class Conversion:
"""
class Conversion is the Model for a celsius-fahrenheit conversion
application. It converts celsius into fahrenheit and fahrenheit into
celsius.
"""
def toCelsius(self, temp):
return (5 / 9) * (temp - 32)
def toFahrenheit(self, temp):
return ((9 / 5) * temp) + 32
The View in conversionView.py:
import tkinter
class MyFrame(tkinter.Frame):
def __init__(self, controller):
tkinter.Frame.__init__(self)
self.pack()
self.controller = controller
self.tempEntry = tkinter.Entry()
self.tempEntry.insert(0, "0")
self.tempEntry.pack({"side": "left"})
self.celsiusButton = tkinter.Button(self)
self.celsiusButton["text"] = "Celsius"
self.celsiusButton["command"] = self.controller.buttonToC
self.celsiusButton.pack({"side": "left"})
self.fahrenheitButton = tkinter.Button(self)
self.fahrenheitButton["text"] = "Fahrenheit"
self.fahrenheitButton["command"] = self.controller.buttonToF
self.fahrenheitButton.pack({"side": "left"})
self.labelForOutput = tkinter.Label(self)
self.labelForOutput["text"] = 0
self.labelForOutput.pack ({"side": "left"})
self.quitButton = tkinter.Button(self)
self.quitButton["text"] = "Quit"
self.quitButton["command"] = self.quit
self.quitButton.pack({"side": "left"})
The Controller in controller.py:
import tkinter
import conversionView
import conversionModel
class Controller:
def __init__(self):
root = tkinter.Tk()
self.model = conversionModel.Conversion()
self.view = conversionView.MyFrame(self)
self.value = float(self.view.tempEntry.get())
self.view.mainloop()
root.destroy()
def buttonToC(self):
self.model.toCelsius(self.value)
self.view.labelForOutput["text"] = str(self.model) + " °C"
def buttonToF(self):
self.model.toFahrenheit(self.value)
self.view.labelForOutput["text"] = str(self.model) + " °F"
if __name__ == "__main__":
c = Controller()
For #1, you need to read the tempEntry control each time you do a conversion, and capture the result of the conversion for printing. As it is now you only read the tempEntry control on __init__, and str(self.model) just prints out the name of the model object. This should work:
def buttonToC(self):
fahr = float(self.view.tempEntry.get())
temp = self.model.toCelsius(fahr)
self.view.labelForOutput["text"] = str(temp) + " °C"
def buttonToF(self):
celsius = float(self.view.tempEntry.get())
temp = self.model.toFahrenheit(celsius)
self.view.labelForOutput["text"] = str(temp) + " °F"
For #2, I'm not familiar enough with Tk yet to know why the Quit button works correctly but the red X destroys the windows before you get around to calling root.destroy, but this should work around it:
self.view.mainloop()
try:
root.destroy()
except tkinter.TclError:
pass
The Quit button needs the destroy, but the X button doesn't and throws an exception. This code just ignores the exception in the X button case.

Python Variable in tkinter window

I am building a device that counts how many parts are made off a machine and then turns the machine off at a specific number. I am using an Arduino for all the I/O work and then importing the serial data into Python as variable partCount. I would like to create a simple GUI in tkinter to show the number of parts that have been made and the total number needed. The problem is that I keep getting an error on the label lines that include a variable instead of just a text. I've done a lot of research on it, but I just can't get it for some reason. Any advice would be appreciated.
import serial
import csv
import datetime
import tkinter
#Part Variables
partNumber = "A-33693" #Part Number
stockupTotal = 10
arduinoSerialData = serial.Serial('com3',9600) #Serial Variable
now = datetime.datetime.now()
#GUI
window = tkinter.Tk()
window.title("Troy Screw Products")
titleLabel = tkinter.Label(window, text="Davenport Machine Control")
partNumberLabel = tkinter.Label(window, text="Part #:")
stockUpTotalLabel = tkinter.Label(window, text="Stockup Total:")
partCountLabel = tkinter.Label(window, text="Current Part Count:")
partNumberInfo = tkinter.Label(window, partNumber)
stockUpTotalInfo = tkinter.Label(window, stockupTotal)
partCountInfo = tkinter.Label(window, partCount)
titleLabel.pack()
partNumberLabel.pack()
partNumberInfo.pack()
stockUpTotalLabel.pack()
stockUpTotalInfo.pack()
partCountLabel.pack()
partCountInfo.pack()
window.mainloop()
#Write to CSV File
def writeCsv():
with open("machineRunData.csv", "a") as machineData:
machineDataWriter = csv.writer(machineData)
machineDataWriter.writerow([partNumber, "Stockup Complete",now.strftime("%Y-%m-%d %H:%M")])
machineData.close()
#Serial Import
while (1==1):
if (arduinoSerialData.inWaiting()>0):
partCount = arduinoSerialData.readline()
partCount = int(partCount)
if partCount == 999999:
writeCsv()
print(partCount)
There are several options you can give to a Label widget, as you can see here. You should specify all parameters after the first by name:
partNumberInfo = tkinter.Label(window, text=PartNumber)
To use Python variables in Tkinter you need to special Tk objects, of which there are four: BooleanVar, DoubleVar, IntVar, and StringVar. See this answer for a good example.
So after a ton of research over the last week, I found a solution. The problem was that the program would run the GUI, but not run the code for the serial import until the GUI window was closed. I needed a way to get both the GUI running and updated and get the serial data importing at the same time. I resolved this by creating a thread for both operations. There's probably an easier way to do this, but this what I came up with. The code is below for anyone having a similar problem.
import serial
import time
import threading
from tkinter import *
#Part Variables
partNumber = "A-33693" #Part Number
stockupTotal = 10
partCount = 0
def countingModule():
global partCount
while (1==1):
time.sleep(2)
partCount += 1
print(partCount)
def gui():
global partCount
root = Tk()
pc = IntVar()
pc.set(partCount)
titleLabel = Label(root, text="Machine Control")
partNumberLabel = Label(root, text="Part #:")
stockUpTotalLabel = Label(root, text="Stockup Total:")
partCountLabel = Label(root, text="Current Part Count:")
partNumberInfo = Label(root, text=partNumber)
stockUpTotalInfo = Label(root, text=stockupTotal)
partCountInfo = Label(root, textvariable=pc)
titleLabel.pack()
partNumberLabel.pack()
partNumberInfo.pack()
stockUpTotalLabel.pack()
stockUpTotalInfo.pack()
partCountLabel.pack()
partCountInfo.pack()
def updateCount():
pc.set(partCount)
root.after(1, updateCount)
root.after(1, updateCount)
root.mainloop()
op1 = threading.Thread(target = countingModule)
op2 = threading.Thread(target = gui)
op1.start()
op2.start()

Resources