Related
Long ago i watched a tutorial on how to encrypt files(of any kind) with a key/password
The original code just makes the process in the terminal, but i wanted to make it into an application using tkinter as my GUI, i've come to a problem my small brain can't solve
The original video: https://www.youtube.com/watch?v=HHlInKhVz3s
This is the error i get: TypeError: Encrypt() missing 2 required positional arguments: 'WhichFile' and 'KeyInput'
This is my code:
from tkinter.filedialog import askopenfilename
import time
root = Tk()
root.title=("Tkinter Calculator")
root.geometry("500x500")
#title
WindowTitle = Label(root, text="Choose Action", font=("Arial", 15))
WindowTitle.place(x=250, y=10,anchor="center")
### The functions
#Encrypt
def Encrypt(WhichFile, KeyInput):
file = open(WhichFile, "rb")
data = file.read()
file.close()
data = bytearray(data)
for index, value in enumerate(data):
data[index] = value ^ KeyInput
file = open("CC-" + WhichFile, "wb")
file.write(data)
file.close()
#Decrypt
def Decrypt(WhichFile, KeyInput):
file = open(WhichFile, "rb")
data = file.read()
file.close()
data = bytearray(data)
for index, value in enumerate(data):
data[index] = value ^ KeyInput
file = open(WhichFile, "wb")
file.write(data)
file.close()
#Step1 - Write the name of the file(Needs to be in the same folder(Also include ext.))
WhichFile = Entry(root, width = 20)
WhichFile.place(x=100, y=150)
WhichFile.insert(0, "Enter File name with extension")
#Step2 - Ask for a key/password
KeyInput = Entry(root, width = 20)
KeyInput.place(x=100, y=250)
KeyInput.insert(0, "Enter a key: ")
#Button for encrypt
Encryptbtn = Button(root, text="Encrypt", highlightbackground='#3E4149', command=Encrypt)
Encryptbtn.place(x=100, y=350)
#Button for decrypt
Decryptbtn = Button(root, text="Decrypt", highlightbackground='#3E4149', command=Decrypt)
Decryptbtn.place(x=200, y=350)
root.mainloop()
So the error occurs in this line:
Encryptbtn = Button(root, text="Encrypt", highlightbackground='#3E4149', command=Encrypt)
Passing a function with arguments to Button's "command"
You have to pass the arguments to the function Encrypt() which demands args "WhichFile" and "KeyInput".
You can pass arguments in Button declaration by using lambda keyword:
Button(root, text="Encrypt", highlightbackground='#3E4149', command=lambda:Encrypt(file,input))
It will take the values of "file" and "input" just when you click on the button, remember it cause sometime it is not that what you actually want (e.g. Python Tkinter button callback).
If you want the arguments to be "remembered" as they was at moment of creating button, use currying
(What is 'Currying'? ,
More about "lambda").
So, for first you have to pass arguments to that function. As I can see, you slightly don't understand how it works, cause you're trying to use the arguments in declaration of a funtion like they were a global variables of something (def Encrypt(WhichFile, KeyInput) and then as an "assignment" WhichFile = Entry(...)) but it doesn't work like that, arguments that are passed to function are specified in function's call, e.g. foo(argument1,argument2), not at the moment of defining it.
Getting text from Entry :
You have to know, that WhichFile = Entry(root, width = 20) is not assigning the value of Entry to WhichFile variable, but the Entry itself (check it by using print(WhichFile)). I propose changing the name of that variable for e.g. "user_input_file" or sth.
If you want to get the text typed in the Entry use user_input_file.get().
Excatly the same thing with KeyInput Entry.
Next, you have to create a variable that will point to that value (python varaibles are pointers), and assign it in that function in a way I mentioned before (with that lambda:).
Just write it as a global variable, for example:
WhichFile = user_input_file.get()
KeyInput = user_input_key.get()
Of course after declaring user_input_file and user_input_key.
I think that'll solve ur problem, feel free to ask 'bout more.
I've been tying to set text to a label but i get this error despite doing the same thing above it with no error, "AttributeError: 'Label' object has no attribute 'set'"
Here is my code and all help is of course appreciated :)
:
#creating the labels
text = StringVar()
text.set("-------")
plate1 = Label(DDFrame1,textvariable = text)
plate1.grid(row=0,column=1)
text1 = StringVar()
text1.set("-------")
owner1 = Label(DDFrame1,textvariable = text1)
owner1.grid(row=1,column=1)
text2 = StringVar()
text2.set("-------")
flags1 = Label(DDFrame1,textvariable = text2)
flags1.grid(row=2,column=1)
def changeText2():
import random
name = ["Vickie Vanfleet","Marcellus Amaker","Cyndi Beale","Roni Foti","Carolyn Sealey",
"Lynda Ansell","Tomiko Kimbrell","Elfreda Bontrager","Melynda Mayberry",
"Precious Nolan","Carl Harm","Trevor Olsen","Anamaria Christianson","Jonna Wagnon",
"Alvina Flock","Sima Lablan","Talisha Fripp","Janey Smedley","Kelly Delpozo",
"Shanice Folse","Sharice Wissing","Darlena Steele","Darlena Steele","Chana Tews",
"Agueda Struble","Harriette Pacifico","Brandon Ellisor","Garry Foushee",
"Telma Kellett","Randa Wojciechowski","Claire Snow","Willa Bankes","Arnold Fall",
"Salome Ridings","Venus Tuner","Willetta Hendriks","Leana Straus"]
name_outcome = random.choice(name)
text1.set(name_outcome)
def changeText3():
import random
reports = ["rep1","rep2","rep3"]
report_outcome = random.choice(reports)
text2.set(report_outcome) #this is the line the error is referencing
Replace text1.set(name_outcome) with text1.configure(text=name_outcome). Same for text2 with report_outcome.
The Label method has no .set() method, you may be confusing it with the StringVar class which does. To work with something like that you would instantiate the label using the textvariable= attribute instead:
text = tkinter.StringVar()
tkinter.Label(root, textvariable=text).pack()
# henceforth usages of text.set("some string") will update the label
How to justify the values listed in drop-down part of a ttk.Combobox? I have tried justify='center' but that seems to only configure the selected item. Could use a resource link too if there is, I couldn't find it.
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
import tkinter.ttk as ttk
except ImportError:
import Tkinter as tk
import ttk
if __name__ == '__main__':
root = tk.Tk()
cbb = ttk.Combobox(root, justify='center', values=(0, 1, 2))
cbb.pack()
root.mainloop()
I have a one-liner solution. Use the .option_add() method after declaring ttk.Combobox. Example:
cbb = ttk.Combobox(root, justify='center', values=(0, 1, 2)) # original
cbb.option_add('*TCombobox*Listbox.Justify', 'center') # new line added
Here's one pure Python way that gets close to what you want. The items in the dropdown list all get justified to fit within the Combobox's width (or a default value will be used).
Update
Part of the reason the initial version of my answer wasn't quite right was because the code assumed that a fixed-width font was being used. That's not the case on my test platform at least, so I've modified the code to actually measure the width of values in pixels instead of whole characters, and do essentially what it did originally, but in those units of string-length measure.
import tkinter as tk
import tkinter.font as tkFont
from tkinter import ttk
class CenteredCombobox(ttk.Combobox):
DEFAULT_WIDTH = 20 # Have read that 20 is the default width of an Entry.
def __init__(self, master=None, **kwargs):
values = kwargs.get('values')
if values:
entry = ttk.Entry(None) # Throwaway for getting the default font.
font = tkFont.Font(font=entry['font'])
space_width = font.measure(' ')
entry_width = space_width * kwargs.get('width', self.DEFAULT_WIDTH)
widths = [font.measure(str(value)) for value in values]
longest = max(entry_width, *widths)
justified_values = []
for value, value_width in zip(values, widths):
space_needed = (longest-value_width) / 2
spaces_needed = int(space_needed / space_width)
padding = ' ' * spaces_needed
justified_values.append(padding + str(value))
kwargs['values'] = tuple(justified_values)
super().__init__(master, **kwargs)
root = tk.Tk()
ccb = CenteredCombobox(root, justify='center', width=10, values=('I', 'XLII', 'MMXVIII'))
ccb.pack()
root.mainloop()
(Edit: Note that this solution works for Tcl/Tk versions 8.6.5 and above. #CommonSense notes that some tkinter installations may not be patched yet,
and this solution will not work).
In Tcl ( I don't know python, so one of the python people can edit the question).
A combobox is an amalgamation of an 'entry' widget and a 'listbox' widget. Sometimes to make the configuration changes you want, you need to access the internal widgets directly.
Tcl:
% ttk::combobox .cb -values [list a abc def14 kjsdf]
.cb
% pack .cb
% set pd [ttk::combobox::PopdownWindow .cb]
.cb.popdown
% set lb $pd.f.l
.cb.popdown.f.l
% $lb configure -justify center
Python:
cb = ttk.Combobox(value=['a', 'abc', 'def14', 'kjsdf'])
cb.pack()
pd = cb.tk.call('ttk::combobox::PopdownWindow', cb)
lb = cb.tk.eval('return {}.f.l'.format(pd))
cb.tk.eval('{} configure -justify center'.format(lb))
Some caveats. The internals of ttk::combobox are subject to change.
Not likely, not anytime soon, but in the future, the hard-coded .f.l
could change.
ttk::combobox::PopdownWindow will force the creation of the listbox when it is called. A better method is to put the centering adjustment into
a procedure and call that procedure when the combobox/listbox is mapped.
This will run for all comboboxes, you will need to check the argument
in the proc to make sure that this is the combobox you want to adjust.
proc cblbhandler { w } {
if { $w eq ".cb" } {
set pd [ttk::combobox::PopdownWindow $w]
set lb $pd.f.l
$lb configure -justify center
}
}
bind ComboboxListbox <Map> +[list ::cblbhandler %W]
After digging through combobox.tcl source code I've come up with the following subclass of ttk.Combobox. JustifiedCombobox justifies the pop-down list's items almost precisely after1 pop-down list's been first created & customized and then displayed. After the pop-down list's been created, setting self.justify value to a valid one will again, customize the justification almost right after the pop-down list's first been displayed. Enjoy:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
from tkinter import ttk
except:
import Tkinter as tk
import ttk
class JustifiedCombobox(ttk.Combobox):
"""
Creates a ttk.Combobox widget with its drop-down list items
justified with self.justify as late as possible.
"""
def __init__(self, master, *args, **kwargs):
ttk.Combobox.__init__(self, master, *args, **kwargs)
self.justify = 'center'
def _justify_popdown_list_text(self):
self._initial_bindtags = self.bindtags()
_bindtags = list(self._initial_bindtags)
_index_of_class_tag = _bindtags.index(self.winfo_class())
# This dummy tag needs to be unique per object, and also needs
# to be not equal to str(object)
self._dummy_tag = '_' + str(self)
_bindtags.insert(_index_of_class_tag + 1, self._dummy_tag)
self.bindtags(tuple(_bindtags))
_events_that_produce_popdown = tuple([ '<KeyPress-Down>',
'<ButtonPress-1>',
'<Shift-ButtonPress-1>',
'<Double-ButtonPress-1>',
'<Triple-ButtonPress-1>',
])
for _event_name in _events_that_produce_popdown:
self.bind_class(self._dummy_tag, _event_name,
self._initial_event_handle)
def _initial_event_handle(self, event):
_instate = str(self['state'])
if _instate != 'disabled':
if event.keysym == 'Down':
self._justify()
else:
_ = self.tk.eval('{} identify element {} {}'.format(self,
event.x, event.y))
__ = self.tk.eval('string match *textarea {}'.format(_))
_is_click_in_entry = bool(int(__))
if (_instate == 'readonly') or (not _is_click_in_entry):
self._justify()
def _justify(self):
self.tk.eval('{}.popdown.f.l configure -justify {}'.format(self,
self.justify))
self.bindtags(self._initial_bindtags)
def __setattr__(self, name, value):
self.__dict__[name] = value
if name == 'justify':
self._justify_popdown_list_text()
def select_handle():
global a
_selected = a['values'][a.current()]
if _selected in ("left", "center", "right"):
a.justify = _selected
if __name__ == '__main__':
root = tk.Tk()
for s in ('normal', 'readonly', 'disabled'):
JustifiedCombobox(root, state=s, values=[1, 2, 3]).grid()
a = JustifiedCombobox(root, values=["Justify me!", "left", "center", "right"])
a.current(0)
a.grid()
a.bind("<<ComboboxSelected>>", lambda event: select_handle())
root.mainloop()
1 It basically makes use of bindtag event queue. This was mostly possible thanks to being able to creating a custom bindtag.
I'm trying to print the value xf_in which is entered in the GUI.
However, I get the following error message when i press the run button:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\My_Name\Anaconda3\lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:/Users/My_Name/Python Scripts/test/gui.py", line 6, in EP
xf_In = tk.get(e_xf)
AttributeError: module 'tkinter' has no attribute 'get'
I've tried to find the source of the error online but to no avail.
Thanks in advance for any help
My code is as follows:
import tkinter as tk
from PIL import ImageTk as imtk
from PIL import Image as im
def EP(): # Enter inputs from values typed in
xf_In = tk.get(e_xf)
print(xf_In)
root = tk.Tk()
l_xf = tk.Label(root, text="xA of Feed").grid(row=0)
e_xf = tk.Entry(root).grid(row=0, column=1)
run = tk.Button(root, text="Run", command=EP).grid(row=8, column=0, columnspan = 2)
img = imtk.PhotoImage(im.open("x.png"))
panel = tk.Label(root, image = img).grid(row = 0, column = 2, rowspan = 7)
root.mainloop()
As the error message indicates, the tk module does not have a function named get. It might have plenty of classes whose instances have a get method, but you can't access them the way you're doing.
If you're trying to get the contents of the Entry, you should assign it to a name, and call get on that instead:
def EP(): # Enter inputs from values typed in
xf_In = e_xf.get(e_xf)
print(xf_In)
#...
e_xf = tk.Entry(root)
e_xf.grid(row=0, column=1)
Note that this assignment is different from doing e_xf = tk.Entry(root).grid(row=0, column=1). If you do that, then e_xf will be bound to the return value of grid, rather than the Entry instance. grid returns None, so trying to call get on that would only give you an AttributeError. Related reading: Why do my Tkinter widgets get stored as None?
I am a python and arcpy user and I have a problem about dependent combobox. Actually I have asked the same topic on here, but no one answer yet. I've got the answer from here and here. But I think I am too newbie on python programming and I don't get the answer clearly.
I try my code like this below, base on the answer that I've got before:
import Tkinter
from Tkinter import *
root = Tkinter.Tk()
bu = StringVar()
bu.set("")
businessunit = ["DUM", "IND", "KAM", "RAP"]
bu_menu = OptionMenu(root, bu, *businessunit, command=Combobox_1)
bu_menu.config(bg="white", fg="dark blue", width=3, relief=GROOVE)
bu_menu.place(x=95, y=110)
sec = StringVar()
sec.set("")
sector = {"DUM":['GRG', 'KBU', 'LBO', 'PLS', 'PLU', 'PPR', 'RPT', 'SBI', 'SKB'],
"IND":['BYS','MER','NGD','PER','SJG','SLJ'],
"KAM":['RSG','SRG','SRY','TSK'],
"RAP":['BAS','CER','LGB','LON','LOS','MDU','MRE','MRW','PEN','PES','PPD','TEE','TEW','TSB','UKU']}
sec_menu = OptionMenu(root, sec, *sector, command=Combobox_2)
sec_menu.config(bg="white", fg="dark blue", width=3, relief=GROOVE)
sec_menu.place(x=155, y=110)
def __init__(self):
def Combobox_1(businessunit):
print bu.get()
def Combobox_2(sector):
print sec.get()
self.Combobox_1.activated[str].connect(self.on_combo_activated)
def on_combo_activated(self, text):
self.Combobox_2.clear()
self.Combobox_2.addItems(self.sector[text])
root.pack()
root.mainloop()
root.destroy()
Please anyone help me on this. Thank you so much for your answer.
edited:
in this case, dependent combobox means:
If DUM is selected in Combobox_1, Combobox_2 will only show GRG,KBU,LBO, etc.
If IND is selected in Combobox_1, Combobox_2 will only show BYS,MER,PER, etc.
If KAM is selected in Combobox_1, Combobox_2 will only show RSG,SRG,SRY, etc.
If RAP is selected in Combobox_1, Combobox_2 will only show BAS,CER,LGB, etc.
I would advise looking at: Change OptionMenu based on what is selected in another OptionMenu
Just change the values as you need them in your menu.
(Also, you don't need to import Tkinter twice, just use "import Tkinter" or "import Tkinter as tk")
Thank you for your contribution. Actually I've got the answer of this question in my question before. Please see this link Graded combobox Menu Python Tkinter
This is called a cascade combo box. Basically in your command (action) you then create the second box with the data based on the selected value of the first. This is easy with a database i dont know of other ways but some sort of array or dictionary should also work fine.
I do it in a class that I call from my app so this is a little over complicated but can be easily simplified if you dont want to bother with the class vals is a dictionary that looks like {'value1':number,'value2':number2} to keep the index needed fro a database call to get more values or whatever. I am using .grid you can use pack or whatever suits you.
def ebox(self, index=None, row=None, column=None, vals=None):
self.value[index] = tk.StringVar()
lis = []
for item in vals:
lis.append(item)
self.combos[index] = ttk.Combobox(self.win, values=lis, textvariable=self.value[index])
self.combos[index].bind("<<ComboboxSelected>>", lambda event, y=vals: self.eboxclk(event, y))
self.combos[index].grid(row=row, column=column)
return
def eboxclk(self, event=None, index=None):
print(event.widget.get(), " widget get ")
print(index[event.widget.get()], " this should be your index")
return
I initialize a dictionary for my widgets, but you could just skip it and just
mycombo = ttk.Combobox(master,values=alist, textvariable=tk.StringVar)
mycombo.bind('<<ComboboxSelected>>', lambda event, dict: dosomething(event, dict)