python tkinter how to organize the rows and columns - python-2.7

Hi I'm trying to build a user interface and having problem with column and row positions. What I expect to see is some distance between buttons and entry widgets since I left two empty column between them. So why are they standing just next to the entry widgets and changing the distances between entry areas? Could anyone give me some help about this?
Here is the code...
from Tkinter import*
HMCC=Tk()
HMCC.title(" GUI v1.0 ")
HMCC.geometry("500x300")
entry_1 = Entry(HMCC)
entry_2 = Entry(HMCC)
entry_3 = Entry(HMCC)
entry_4 = Entry(HMCC)
entry_5 = Entry(HMCC)
entry_6 = Entry(HMCC)
entry_7 = Entry(HMCC)
entry_8 = Entry(HMCC)
entry_1.grid(row=2,column=1)
entry_2.grid(row=3,column=1)
entry_3.grid(row=4,column=1)
entry_4.grid(row=5,column=1)
entry_5.grid(row=6,column=1)
entry_6.grid(row=7,column=1)
entry_7.grid(row=8,column=1)
entry_8.grid(row=9,column=1)
Channel_1 = Label(HMCC, text = "Channel 1 : ")
Channel_2 = Label(HMCC, text = "Channel 2 : ")
Channel_3 = Label(HMCC, text = "Channel 3 : ")
Channel_4 = Label(HMCC, text = "Channel 4 : ")
Channel_5 = Label(HMCC, text = "Channel 5 : ")
Channel_6 = Label(HMCC, text = "Channel 6 : ")
Channel_7 = Label(HMCC, text = "Channel 7 : ")
Channel_8 = Label(HMCC, text = "Channel 8 : ")
Channel_1.grid( row = 2, column = 0, sticky = E)
Channel_2.grid( row = 3, column = 0, sticky = E)
Channel_3.grid( row = 4, column = 0, sticky = E)
Channel_4.grid( row = 5, column = 0, sticky = E)
Channel_5.grid( row = 6, column = 0, sticky = E)
Channel_6.grid( row = 7, column = 0, sticky = E)
Channel_7.grid( row = 8, column = 0, sticky = E)
Channel_8.grid( row = 9, column = 0, sticky = E)
#button1 = Button(text=" START " , fg="red" )
#button2 = Button(text=" PAUSE " , fg="blue" )
#button3 = Button(text=" STOP ", fg="green")
#button4 = Button(text="QUIT" , fg="black",command=HMCC.quit)
#button1.grid( row = 1, column = 3)
#button2.grid( row = 2, column = 3)
#button3.grid( row = 3, column = 3)
#button4.grid( row = 4, column = 3)
HMCC.mainloop()
Current view
Thanks in advance

If there is nothing in column 2, then tkinter will ignore it.
In addition to the comment posted above which contains the answer to your question, you can clean up your code significantly by just using a loop:
num_rows = 8
entries = [None]*num_rows
channels = [None]*num_rows
for i in range(num_rows):
channels[i] = Label(HMCC, text = "Channel {0} : ".format(i+1))
channels[i].grid(row=i+2,column=0,sticky=E)
entries[i] = Entry(HMCC)
entries[i].grid(row=i+2, column=1)
Better yet, use list comprehension:
num_rows = 8
entries = [Entry(HMCC).grid(row=i+2, column=1) for i in range(num_rows)]
channels = [Label(HMCC, text = "Channel {0} : ".format(i)).grid(row=i+2,column=0,sticky=E) for i in range(num_rows)]

Related

change label values when an entry value is changed

My problem at the moment is I am trying to change a label(label 16) to the first value of entry_values[0] which isn't working I have tried passing it in as a variable and many other things, after about an hour of research I couldn't find a solution.I think the main problem is that it sets the label before the code with the entry is run so that it wont change. when I set it to a textvariable it produces an empty string (I think) but when I use just text it puts in a 0 where I expect my number.
def sub_menu(root):
global subpage
subpage = Frame(root)
button5 = Button(subpage, text="Save Generation Data",
command = lambda: save_entries())
button5.grid(row = 1, column = 6, sticky = E)
button6 = Button(subpage, text="Return To Main Page",
command = lambda: switch_page("main"))
button6.grid(row = 0, column = 6, sticky = W)
juveniles_label0 = Label(subpage,text="Juveniles")
adults_label1 = Label(subpage,text="Adults")
seniles_label2 = Label(subpage,text="Seniles")
population_label3 = Label(subpage,text="Population (Thousands)")
survival_rate_label4 = Label(subpage,text="Survival Rate (Between 0 and 1)")
birth_rate_label5 = Label(subpage,text="Birth Rate")
number_of_gens_label6 = Label(subpage,text="Number of Generations")
disease_trigger_label7 = Label(subpage,text="Disease Trigger Point")
global entry0
entry0 = Entry(subpage)
global entry1
entry1 = Entry(subpage)
global entry2
entry2 = Entry(subpage)
global entry3
entry3 = Entry(subpage)
global entry4
entry4 = Entry(subpage)
global entry5
entry5 = Entry(subpage)
global entry6
entry6 = Entry(subpage)
global entry7
entry7 = Entry(subpage)
global entry8
entry8 = Entry(subpage)
juveniles_label0.grid(row = 0, column = 1)
adults_label1.grid(row = 0, column = 2)
seniles_label2.grid(row = 0, column = 3)
population_label3.grid(row = 1, column = 0)
survival_rate_label4.grid(row = 2, column = 0)
birth_rate_label5.grid(row = 3, column = 0)
number_of_gens_label6.grid(row = 3, column = 2)
disease_trigger_label7.grid(row = 4, column = 0)
entry0.grid(row = 1, column = 1)
entry1.grid(row = 1, column = 2)
entry2.grid(row = 1, column = 3)
entry3.grid(row = 2, column = 1)
entry4.grid(row = 2, column = 2)
entry5.grid(row = 2, column = 3)
entry6.grid(row = 3, column = 1)
entry7.grid(row = 3, column = 3)
entry8.grid(row = 4, column = 1)
return subpage
def save_entries(): #entry recieve point
save_page = Frame(root)
""" if e0 < 0:
make a check to check if value is < 0 dont accept and if a value is inputed or not using if type(string_name) == str """
e0 = entry0.get()
if e0 >= 0:
entry_values[0] = (e0)
e1 = entry1.get()
if e0 >= 0:
entry_values[1] = (e1)
e2 = entry2.get()
if e0 >= 0:
entry_values[2] = (e2)
e3 = entry3.get()
if e0 >= 0:
entry_values[3] = (e3)
e4 = entry4.get()
if e0 >= 0:
entry_values[4] = (e4)
e5 = entry5.get()
if e0 >= 0:
entry_values[5] = (e5)
e6 = entry6.get()
if e0 >= 0:
entry_values[6] = (e6)
e7 = entry7.get()
if e0 >= 0:
entry_values[7] = (e7)
e8 = entry8.get()
if e0 >= 0:
entry_values[8] = (e8)
print entry_values
return save_page
def display_values(root):
sub2 = Frame(root)
global entry_values
label8 = Label(sub2, text = "Juveniles")
label9 = Label(sub2, text = "Adults")
label10 = Label(sub2, text = "Seniles")
label11 = Label(sub2, text = "Population(Thousands)")
label12 = Label(sub2, text = "Survival Rate(Between 1 and 0)")
label13 = Label(sub2, text = "Birth Rate")
label14 = Label(sub2, text = "Number of Generations")
label15 = Label(sub2, text = "Disase Trigger Point")
label16 = Label(sub2, text = entry_values[0])
label17 = Label(sub2, textvariable = entry_values[1])
label18 = Label(sub2, textvariable = "")
label19 = Label(sub2, textvariable = "")
label20 = Label(sub2, textvariable = "")
label21 = Label(sub2, textvariable = "")
label22 = Label(sub2, textvariable = "")
label23 = Label(sub2, textvariable = "")
label24 = Label(sub2, textvariable = "")
button7 = Button(sub2, text="Return To Main Page",
command = lambda: switch_page("main"))
label8.grid(row = 0, column = 1)
label9.grid(row = 0, column = 2)
label10.grid(row = 0, column = 3)
label11.grid(row = 1, column = 0)
label12.grid(row = 2, column = 0)
label13.grid(row = 3, column = 0)
label14.grid(row = 3, column = 3)
label15.grid(row = 4, column = 0)
label16.grid(row = 1, column = 1)
label17.grid(row = 1, column = 2)
label18.grid(row = 1, column = 3)
label19.grid(row = 2, column = 1)
label20.grid(row = 2, column = 2)
label21.grid(row = 2, column = 3)
label22.grid(row = 3, column = 1)
label23.grid(row = 3, column = 3)
label24.grid(row = 4, column = 1)
button7.grid(row = 0, column = 0)
return sub2
In order to change the text of a label you can do:
label["text"] = textVar
or
label.config(text=textVar)
So in your above code, when the entry changes, reconfigure the label using one of the above options.

Tkinter strange behavior on destroy()

I am developing an application on Tkinter, Python 2.7.
In one of my processes I build a portion of my window (root) with 26 widgets:
23 Labels, 2 Buttons and 1 Entry.
While building them I keep adding their names to a list for further destroying
when their use is finished. For that I use the press of one of the buttons ("Done")
to read the list created and destroy() them inside a "for" loop.
The widgets get destroyed erratically, not in the order on the list. And I need
several presses of the button to finish it.
I found out of frustration, not insight, that if the list is reversed() in
the "for" loop they all get "destroyed" in the first attempt.
Is this an expected behaviour? Very puzzling!
I am ready to post the portion of my application with the strange behavior
expunged of unnecessary code unless somebody already knows the reason for it.
I am still busting my chops with Python and not ready to use Classes...
Thank you!
I am including the relevant portion of my program. I edited the original to reduce it size. Tested the edited version and it has the same behavior. I commented some of my code to show where to correct.
Happy that Mr. Oakley has taken interest. I am not sure if on transcribing my code the proper indentation was not affected.
My code:
# testdestroy.py
from Tkinter import *
root = Tk()
root.title('Information container')
root.geometry('1160x900+650+50')
global todestroy, screen, font1, lbl1txt, lbl2txt
global col, row, colincr, rowincr, bxincr, entries
todestroy = []
screen = ''
col = 10
row = 10
font1 = 'verdana 12 bold '
colincr = 370
rowincr = 40
bxincr = 145
entries = {' Last updated: ' : '11/08/2016 at 11:55',
' Login id: ' : 'calfucura',
' Password: ': 'munafuca',
'card number' : '1234567890',
'check number': '445',
'expiry' : '12/06/2018',
'PIN' : '9890',
'Tel:' : '1-800-234-5678',
'emergency' : 'entry nine',
'use for' : 'gas, groceries'}
def position(col, row, what): # returns the position for the place command
colincr = 370
rowincr = 40
bxincr = 145
if what == 'down':
row += rowincr
col -= colincr
if what == 'side':
col += colincr
if what == 'button1':
row += rowincr
col += colincr - bxincr
if what == 'button':
col -= bxincr
if what == 'reset':
col = col
row = row
return col, row
def done(event): # Button "Done"
print 'Done pressed'
for name in todestroy: # DOES NOT WORK!!!!
# THIS WORKS in the previous line:
# for name in reversed(todestroy):
name.destroy()
todestroy.remove(name)
def accept(event): # Button "Accept"
print 'Name to show: ', entry1.get()
scr2d1(entries)
def scr2d(): # Name to show
lbl1txt = 'Enter name to show: '
screen = 'scr2d'
scr2(lbl1txt)
# scr2d1(entries)
def scr2(lbl1txt):
global todestroy, col, row, entry1
lbl1 = Label(root, text = lbl1txt, anchor = E, width = 25, font = font1)
entry1 = Entry(root, width = 25, show = '*', font = font1)
Accept = Button(root, text = 'Accept', font = font1, bg = 'green', width = 9)
cmd = eval('Accept'.lower())
Accept.bind('<ButtonRelease-1>', cmd)
col, row = position(200, 200, 'reset')
lbl1.place(x = col, y = row)
col, row = position(col, row, 'side')
entry1.place(x = col , y = row )
col, row = position(col, row, 'button1')
Accept.place(x = col, y = row)
todestroy = []
todestroy.extend([lbl1, entry1, Accept])
def scr2d1(entries): # show entries
global todestroy, col, row
lblup = 1
lbl = 'lbl' + str(lblup)
lbl = Label(root, text = 'Entry', font = font1, width = 20 )
row = rowincr * 7
col = 600
col, row = position(col, row, 'down')
lbl.place(x = col, y = row)
todestroy.append(lbl)
lblup += 1
lbl = 'lbl' + str(lblup)
lbl = Label(root, text = 'Contents', font = font1, width = 20)
col, row = position(col, row, 'side')
lbl.place (x = col, y = row)
todestroy.append(lbl)
for name in sorted(entries):
lblup += 1
lbl = 'lbl' + str(lblup)
lbl = Label(root, text = name, bg = 'yellow', font = font1, width = 25, anchor = E)
col, row = position(col, row, 'down')
lbl.place(x = col, y = row)
todestroy.append(lbl)
lblup += 1
lbl = 'lbl' + str(lblup)
lbl = Label(root, text = entries[name], bg = 'yellow', font = font1, width = 25, anchor = W)
col, row = position(col, row, 'side')
lbl.place(x = col , y = row)
todestroy.append(lbl)
cmd = eval('done')
Done = Button(root, text = 'Done', font = font1, bg = 'green', width = 9)
Done.bind('<ButtonRelease-1>', cmd)
col, row = position(col, row, 'button1')
Done.place(x = col, y = row)
todestroy.append(Done)
scr2d()
root.mainloop()
The problem is that you are altering the list as you iterate over it, which is not something you should do. The reason that it works with reversed is because you are iterating over a copy of the original list. You get the same result if you use for name in todestroy[:], which also iterates over a copy of the list.
The quickest solution is to not remove anything from the list,and simply reset the list after you've deleted everything:
def done(event):
global todestroy
for name in todestroy:
name.destroy()
todestroy = []
A better solution would be to put all of the widgets you plan to destroy into a Frame. You can then destroy just the frame, and it will destroy all of its child widgets.

Python 2.7; How to clear an entry in the GUI

I have an example code, here just for BMI index. I would like to clear the input and output fields in the GUI. It seems that i can clear the entries, but the BMI calculation is not being removed (row 79 does not seem to have an effect) (# self.text.delete(0, 'end'))
Thanks
Emin
import math
from Tkinter import *
class Application(Frame):
"""A GUI application with three buttons"""
def __init__(self, master):
"""Initialize the Frame"""
Frame.__init__(self,master)
self.grid()
self.create_widgets()
self.title = Label(self, text = "BMI index calculation")
self.title.grid(row = 0, column = 0, columnspan = 2 , sticky =W)
def create_widgets(self):
"""Create button, text and entry widgets"""
self.name = Label(self, text = "What is your name?")
self.name.grid(row = 1, column = 0, columnspan = 2 , sticky =W)
self.name_io = Entry(self)
self.name_io.grid(row = 1, column =2, sticky = W)
self.age = Label(self, text = "How old are you?")
self.age.grid(row = 2, column = 0, columnspan = 2 , sticky =W)
self.age_io = Entry(self)
self.age_io.grid(row = 2, column =2, sticky = W)
self.height = Label(self, text = "How tall are you?")
self.height.grid(row = 3, column = 0, columnspan = 2 , sticky =W)
self.height_io = Entry(self)
self.height_io.grid(row = 3, column =2, sticky = W)
self.weight = Label(self, text = "How much do you weigh in kg?")
self.weight.grid(row = 4, column = 0, columnspan = 2 , sticky =W)
self.weight_io = Entry(self)
self.weight_io.grid(row = 4, column =2, sticky = W)
self.submit_button = Button(self, text = "Calculate", command = self.reveal)
self.submit_button.grid(row = 5, column = 0, sticky = W)
self.text = Text(self, width = 40, height = 5, wrap = WORD)
self.text.grid(row = 6, column = 0, columnspan = 3, sticky = W)
self.clear_button = Button(self, text = "Clear", command = self.clear_text)
self.clear_button.grid(row = 7, column = 0, sticky = W)
def reveal(self):
"""Display message based on the password typed in"""
content_name = self.name_io.get()
content_age = float(self.age_io.get())
content_height = float(self.height_io.get())
content_weight = float(self.weight_io.get())
BMI = round((content_weight/(content_height/100)**2.),1)
underBMI = 18.5
NormalBMI = 24.9
OverweightBMI = 29.9
ObesityBMI = 30
if BMI <= underBMI:
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "you are underweight, so you need to eat!"
elif (BMI > underBMI) and (BMI <= NormalBMI):
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "your BMI is Normal"
elif (BMI > NormalBMI) and (BMI <= OverweightBMI):
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "you are Overweight - need to exercise!"
elif (BMI > OverweightBMI):
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "you are in Obesity"
self.text.insert(0.0, message)
def clear_text(self):
self.name_io.delete(0, 'end')
self.age_io.delete(0, 'end')
self.height_io.delete(0, 'end')
self.weight_io.delete(0, 'end')
# self.text.delete(0, 'end')
root = Tk()
root.title("BMI Index")
root.geometry("600x350")
app = Application(root)
root.mainloop ()
The problem is that you're giving an index that is 0.0. Text widget indexes are a string of the form line.column but you're giving it a floating point number.
The proper index for the first character is the string "1.0".
self.text.delete("1.0", 'end')
Simply
your_entry.delete(0,END)

Getting selected items from a Tkinter listbox without using a listbox bind

I have 2 listboxes (which are connected so that items can move from one to the other) and at the end I would like to get all the entries in the second listbox by using a 'Ok' button (or simply closing the frame). I could add/remove values to a list every time an item is selected (as shown in the commented section of the code below) but I would rather have a single line of code along the lines of [master.selected.get(idx) for idx in master.selected.curselection()] in the close function but I am unable to get it working.
Code:
def measurementPopup(self,master):
self.chargeCarrier = StringVar()
self.massModifiers = StringVar()
self.chargeCarrier.set("[M+xH]")
def onselect1(evt):
w = evt.widget
index = int(w.curselection()[0])
value = w.get(index)
# My Dirty fix -> Here I could enter the selected value to a buffer list (to be returned in the ok function).
master.selected.insert(END,value)
master.avail.delete(index)
def onselect2(evt):
w = evt.widget
index = int(w.curselection()[0])
value = w.get(index)
# My Dirty fix -> Here I could remove the selected value from a buffer list (to be returned in the ok function).
master.selected.delete(index)
master.avail.insert(END,value)
def close(self):
# Here I would return the buffer list and close the window
master.measurementWindow = 0
top.destroy()
if master.measurementWindow == 1:
return
master.measurementWindow = 1
top = self.top = Toplevel()
top.protocol( "WM_DELETE_WINDOW", lambda: close(self))
self.charge = Label(top, text = "Charge", width = 10)
self.charge.grid(row = 0, column = 0, sticky = W)
self.min = Label(top, text = "Min", width = 5)
self.min.grid(row=0, column = 1, sticky = W)
self.minCharge = Spinbox(top, from_= 1, to = 3, width = 5)
self.minCharge.grid(row = 0, column = 2, sticky = W)
self.max = Label(top, text = "Max", width = 5)
self.max.grid(row = 0, column = 3, sticky = W)
self.maxCharge = Spinbox(top, from_ = 1, to=3, width=5)
self.maxCharge.grid(row = 0, column = 4, sticky = W)
self.chargeCarrier = OptionMenu(top, self.chargeCarrier, "[M+xH]", "[M+xNa]")
self.chargeCarrier.grid(row = 0, column = 5, sticky = W)
self.availMass = Label(top, text = "Available")
self.availMass.grid(row = 1, column = 1, sticky = W)
self.selectMass = Label(top, text = "Selected")
self.selectMass.grid(row = 1, column = 3, sticky = W)
self.massMod = Label(top, text = "Mass Mods")
self.massMod.grid(row = 2, column = 0, sticky = W)
self.avail = Listbox(top)
for i in UNITS:
if BLOCKS[i]['available'] == 1:
self.avail.insert(END,BLOCKS[i]['human_readable_name'])
self.avail.grid(row = 2, column = 1, columnspan = 2, sticky = W)
self.avail.bind('<<ListboxSelect>>',onselect1)
self.selected = Listbox(top)
self.selected.grid(row = 2, column = 3, columnspan = 2, sticky = W)
self.selected.bind('<<ListboxSelect>>',onselect2)
self.ok = Button(top,text = 'Ok',command = lambda: close(self))
self.ok.grid(row = 3, column = 0, sticky = W)
I have tried to use the following small snippet in the close function:
values = [master.selected.get(idx) for idx in master.selected.curselection()]
print ', '.join(values)
However, the for segment doesn't return anything. I would expect that this is due to the fact that nothing is actually selected but that I would need something opposite, along the lines of master.selected.allitems() (if it exists and if I understand it correctly).
Summary
How would one get all the items in 1 specific listbox?
The .get() function for the Listbox widget allows you to specify a range of items, which can be specified as 0 to END to return a tuple of all the items.
Example:
from Tkinter import *
root = Tk()
l = Listbox(root, width = 15)
l.pack()
l.insert(END, "Hello")
l.insert(END, "world")
l.insert(END, "here")
l.insert(END, "is")
l.insert(END, "an")
l.insert(END, "example")
def close():
global l, root
items = l.get(0, END)
print(items)
root.destroy()
b = Button(root, text = "OK", command = close).pack()
root.mainloop()
I hope this helps, if it's not what you were looking for let me know in a comment and I can try expand my answer.

TclError: wrong # args error

I have no idea what is wrong but I keep getting this
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/lib-tk/Tkinter.py", line 1410, in call
return self.func(*args)
File "/Users/Zane/Desktop/Factorial GUI.py", line 72, in reveal2
self.text2.insert(0.0, message)
File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/lib-tk/Tkinter.py", line 2986, in insert
self.tk.call((self._w, 'insert', index, chars) + args)
TclError: wrong # args: should be ".22186144.22187184 insert index chars ?tagList chars tagList ...?"
here is my code:`
from Tkinter import*
class App(Frame):
def fac(self, n):
if n >= 0:
if n == 1 or n == 0:
return 1
else:
return n*self.fac(n-1)
else:
print('Error')
def per(self, n, r):
y = (self.fac(n)) / self.fac(n - r)
print (y)
def __init__(self, master):
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
self.instruction1 = Label(self, text = "Factorial:")
self.instruction1.grid(row = 0, column = 0, columnspan = 1, sticky = W)
self.password1 = Entry(self)
self.password1.grid(row = 0, column = 1, sticky = W)
self.submit_button1 = Button(self, text ="Enter", command = self.reveal1)
self.submit_button1.grid(row = 2, column = 0, sticky = W)
self.text1 = Text(self, width = 30, height = 1, wrap = WORD)
self.text1.grid(row = 3, column = 0, columnspan = 2, sticky = W)
self.instruction2 = Label(self, text = "Permutation:")
self.instruction2.grid(row = 4, column = 0, columnspan = 1, sticky = W)
self.password2 = Entry(self)
self.password2.grid(row = 4, column = 1, sticky = W)
self.password3 = Entry(self)
self.password3.grid(row = 6, column = 1, sticky = W)
self.submit_button2 = Button(self, text ="Enter", command = self.reveal2)
self.submit_button2.grid(row = 7, column = 0, sticky = W)
self.text2 = Text(self, width = 30, height = 1, wrap = WORD)
self.text2.grid(row = 8, column = 0, columnspan = 2, sticky = W)
def reveal1(self):
y = int(self.password1.get())
message = self.fac(y)
self.text1.delete(0.0, END)
self.text1.insert(0.0, message)
def reveal2(self):
y = int(self.password2.get())
z = int(self.password3.get())
message = self.per(y, z)
self.text2.delete(0.0, END)
self.text2.insert(0.0, message)
root = Tk()
root.title('Factorial')
root.geometry("340x300")
app = App(root)
root.mainloop()
`
Almost the only way to get the error you say you get with the code you posted, is if the insert method is called when the data to insert is None. message comes from the result of per, but per returns None because you don't explicitly return anything else.
One of the first things to try when trying to debug is to check that the data you're sending to the failing function is what you think it is. You can do this in a very low-tech way by simply printing out the values being passed to the insert message. This instantly told me that message was None. Once I learned that, it's pretty simple to answer the question "why was it None?".