Python append tkinter widget to list shows as int value - python

I'm trying to make an array/list of tkinter Labels for a wage calculator, but whenever I append the widget to my array, it appends as an int datatype. I was thinking appending it would allow me to call list[widgetIndex].grid() but since it wasn't a Label datatype getting appended to my list for some reason, it gives me an error saying that this 'int' object has no attribute called 'grid'. Why is it an int datatype as oppose to the Label datatype that the variable definitely was before I appended it?
def addJob(jc, cc, he, wl, hl, we):
jc = jc + 1
cc = cc + 2
wageLabel = Label(root, text="Wage")
hoursLabel = Label(root, text="Hours")
wageEntry = Entry(root)
hoursEntry = Entry(root)
wl.append(wageLabel)
hl.append(hoursLabel)
we.append(wageEntry)
he.append(hoursEntry)
wl[jc-1].grid(row =0, column =cc-1, sticky=E)
hl[jc-1].grid(row =1, column =cc-1, sticky=E)
we[jc-1].grid(row = 0, column=cc)
he[jc-1].grid(row = 1, column=cc)

You initialize w1 using w1 = [ 0 ].
Later on you just append to w1, so your list will be sth. like
[ 0, <class 'tkinter.Label'>, <class 'tkinter.Label'>, <class 'tkinter.Label'>, ... ] depending on how many labels you add.
Using w1 = [] will solve your problem.
The error occurs as the first element in your list ("0") cannot be gridded.
Using print w1 in your function would have shown you this in a fairly easy way.
Bryan is right, the code you posted is without a doubt okay for the purpose you intend using it, but the initialization for your passed parameters (not posted as code sample) defines a different behaviour.
Please read on How to create a Minimal, Complete and Verifiable Example. Breaking down code into the smallest piece still reproducing the error mostly guides you direct to the solution.

Related

How to export selections made in tkinter Combobox to Excel or text File?

I'm working on a program that will be used for internally ordering parts between stores. Currently I have the GUI working perfectly, and the code has no errors.
I want to have a button at the bottom of the GUI that will say send, which will read the selections made, and then export those into a file that can be read by the logistics department. I have scoured the internet but cant seem to find such thing for python.
I would prefer it to export the selections into a excel spreadsheet, but text file will do too, if it can be coded so that it would be easy to read.
As I received a bad comment on the last post, I'll post code for the two kinds of selection boxes, i have. The code pasted into a .py file will open a similar GUI.
from tkinter import *
from tkinter.ttk import *
master = Tk()
master.geometry("400x400")
def openiPhone5():
iPhone5 = Toplevel(master)
iPhone5.geometry("800x800")
Label(iPhone5,
text="iPhone 5").grid()
#Variabel til iPhone 5 Farver
iPhone5Colors =('Sort', 'Hvid')
#PARTS###
#OrginalSkærm
OGscreen = Combobox(iPhone5)
OGscreenColor = Combobox(iPhone5)
OGscreen['values'] = (1, 2, 3, 4, 5, "Text")
OGscreenColor['values'] = iPhone5Colors
OGscreen.current(0) # set the selected item
OGscreenColor.grid(column=3, row=7)
#CUSTOM
CustomAmount = Combobox(iPhone5)
CustomTEXT = Combobox(iPhone5)
CustomTEXT['text'] = (1, "Text")
CustomAmount['values'] = (1, 2, 3, 4, 5, "Text")
CustomAmount.current(0) # set the selected item
CustomAmount.grid(column=3, row=18)
CustomTEXT.grid(column=3, row=17)
Custom_lbl = Label(iPhone5,
text="Custom")
Custom_lbl.grid(column=1, row=17)
def openNewWindow1():
# Toplevel object which will
# be treated as a new window
newWindow1 = Toplevel(master)
# sets the title of the
# Toplevel widget
newWindow1.title("Apple")
# sets the geometry of toplevel
newWindow1.geometry("800x800")
# A Label widget to show in toplevel
Label(newWindow1,
text="iPhones").grid()
btn = Button(newWindow1,
text="iPhone 5",
command=openiPhone5)
The simplest way is to start by associating a variable with each combobox. You can then store those variables in a list or dictionary. To get the data out it's just a matter of iterating over the list or dictionary and calling get on each variable.
Here's an example of a very simplistic example that creates 10 comboboxes with associated variables:
combo_vars = []
for i in range(10):
var = tk.StringVar(root, value=0)
combo_vars.append(var)
label = tk.Label(form, text=f"Value {i+1}:")
combo = ttk.Combobox(form, textvariable=var, width=20, values=list(range(5)))
label.grid(row=i, column=0, sticky="e")
combo.grid(row=i, column=1, sticky="w")
For example, assuming you have a variable named combo_vars that contains instances of StringVar such as in the previous example, you can get all of the values and print them out in a simple loop. There's a more concise way of doing this using list comprehensions, but I'm using the more verbose method to make it easier to understand.
values = []
for var in combo_vars:
value = var.get()
values.append(value)
With that, values has a list of the values. You can then do whatever you want with that list: convert them to a comma-separated list, write them to a database, convert them to json, save them to an excel file, or whatever it is you need to do.
For example, to write a comma-separated list to a file you can use the csv module like so:
import csv
...
with open("data.csv", "w") as csvfile:
writer = csv.writer(csvfile, values)
writer.writerow(values)

How to use result in IF to spit out True/False?

The following code snippet
if pyautogui.locateOnScreen('test4x4.png', region = (200,200,4,4)) == "Box(left=200, top=200, width=4, height=4)":
print("Found")
else:
print("NotFound")
spits out NotFound,
but
print(pyautogui.locateOnScreen('test4x4.png', region = (200,200,4,4)))
prints out "Box(left=200, top=200, width=4, height=4)".
How to properly match the result so I can get the Found value back?
The locateOnScreen function returns a pyscreeze.Box object, not a string.
So you'll want to convert it to a tuple before doing any comparisons:
box = pyautogui.locateOnScreen('test4x4.png', region = (200,200,4,4))
if box is not None and tuple(box) == (200, 200, 4, 4):
print("Found")
else:
print("NotFound")
<Edit>
The reason you are getting TypeError: 'NoneType' object is not iterable, is because if the image is not found, locateOnScreen returns None. Trying to convert None to a tuple: tuple(None) throws an error.
You can protect against this by checking that box isn't None, as I've edited above.
I can't explain why you're still getting the error when the image is successfully found however, so you'll need to give some more info for us to solve that.
</Edit>
The reason your version doesn't work is because when you call print(box), behind the scenes the print function is actually calling str(box) and printing that instead.
Thats why even though
>>> print(box)
Box(left=200, top=200, width=4, height=4)
it doesn't mean that
box == "Box(left=200, top=200, width=4, height=4)"

Adding a scroll bar to Tkinter with a long list of data

I currently have an issue with my Output in Tinker. So whenever I try to output my data read from a file the output dialog is too small to fit the huge list of data. For example:
The window is too large to display all my data so I looked into adding a scrollbar for the window in order to better fit the data and easily see all of it.
The issue arises in my code:
from Tkinter import *
import tkSimpleDialog
import tkMessageBox
root = Tk()
w = Label(root, text ="my Program")
w.pack()
tkMessageBox.showinfo("Welcome", "Add your welcome message here")
with open("Logs.txt", "rb") as f_input:
start_token = tkSimpleDialog.askstring("Serial Number", "What is the device serial number?")
end_token = tkSimpleDialog.askstring("End Keyword", "What is the end keyword")
reText = re.search("%s(.*?)%s" % (re.escape(start_token + ",SHOWALL"), re.escape(end_token)), f_input.read(), re.S)
if reText:
output = reText.group(1)
scrollbar = Scrollbar(root)
scrollbar.pack( side = RIGHT, fill=Y, expand = False)
mylist = Listbox(root, yscrollcommand = scrollbar.set )
mylist.insert(END, output)
mylist.pack( side = LEFT, fill = Y, expand = False)
scrollbar.config( command = mylist.yview )
mainloop()
# tkMessageBox.showinfo("Output", output)
print output
else:
print "not found"
When I add the scrollbar I get his as an output:
So what I am trying to do is fit the data in a nice window dialog that has a scrollbar.
This is the output:
APN=""
APU=""
APP=""
DDC=
FMT=""
HDR=
AKR=
AKT=
AKP=
PMR=
PKA=
PHN=""
PHS=""
HBI=
PMF=
PMN=
PRN=
PRF=
RET=
SFR=
SFT=
SXD=
SXV=
SXW=
SXX=
SPM=
VIT=
VID=
VPT=
ATN=
ATF=
AMT=
AMD=
LGF=
GPA=
GFN=
GFO=
GDN=
GDF=
GPN=
SCN=
SCO=
SCP=
AEE=
AET=
AES=
OUA=
BWP=
MSO=
UPE=
UPC=
UPT=
VAA=
VAR=
VAI=
IVJ=
IGM=
IGA
IGB=
IGC=
IGD=
IGE=
IGF=
VOW=
VWD=
I think you need to change how the output is inserted into the Listbox, assuming its contents is newline separated (as shown in your updated question).
The documentation presents the insert() method's calling sequence as:
insert(index, *elements)
So one way to do things would be to call it multiple times, once for each element, in a loop:
for element in output.splitlines():
mylist.insert(END, element)
However, the * prefix on the second argument in the documentation means that the method accepts a variable number of arguments starting at the point (each of which will be put on a separate line).
This means the same thing could accomplished in a single call:
mylist.insert(END, *output.splitlines())
What that does is first split the output data up into a list based upon the newlines in it, and then passes them as a bunch of separate arguments in the method call by prefixing the result returned from output.splitlines() with an *, which causes it to be "unpacked" into multiple calling arguments.
This is a fairly common Python idiom, so is worth learning and remembering. See Unpacking Argument Lists in the documentation.

tkinter table:adding data into new row and specify index

Has anyone worked with tkinter table?
I have problem with adding data into new row. I have an entry field in which user enter object. Then in def onButtonSents() I check if this word is contained in objects array and enter this object into the first column of the table naming Object, in the second column I add positive sentiments of the object and so on. I want by clicking the button add new row and put in different columns of one row values of dictionary. Here what I have for now:
file sentiment_analysis.py:
objects [] #an array of objects which user enters into entry field
positiveSentiments = dict() #dictionary with key - object and positive words which correspond to it
negativeSentiments = dict() #dictionary with key - object and negative words which correspond to it
array_cols = ['Object', 'Positive sentiments', 'Negative sentiments']
var = tktable.ArrayVar()
#below is initialization of my tkinter table
import tktable
array_cols = ['Object', 'Positive sentiments', 'Negative sentiments']
var = tktable.ArrayVar()
table = tktable.Table(frame,
rows = 1,
cols = 3,
roworigin=0,
colorigin=0,
variable=var,)
table.pack(side='bottom', fill='both')
var.set(index='0,0', value = 'Object')
table.tag_configure('active', )
var.set(index='0,1', value = 'Positive Sentiments')
var.set(index='0,2', value = 'Negative Sentiments')
#The method which puts data into the rows of table
def onButtonSents():
table.insert_rows(1)
for i in range (1, 5):
index1 = '%i,%i' % (i, 0)
table.activate(index1)
for key in sentiment_analysis.positiveSentiments.keys():
if key == entry.get():
var.set(index='active', value=entry.get())
for i in range (1, 5):
index2 = '%i,%i' % (i, 1)
table.activate(index2)
for key in sentiment_analysis.positiveSentiments.keys():
if key == entry.get():
var.set(index='active', value=sentiment_analysis.positiveSentiments[key])
for i in range (1, 5):
index3 = '%i,%i' % (i, 2)
table.activate(index3)
for key in sentiment_analysis.negativeSentiments.keys():
if key == entry.get():
var.set(index='active', value=sentiment_analysis.negativeSentiments[key])
But the cells of table aren't correctly filled. First object if filled correctly but then all objects become the same like first I enter 'ubs' and when I enter the second 'wheat' the first also becomes 'wheat' and their sentiments are also changed.
Unfortunately I haven't found any suggestions for this problem in the Internet. I would be very grateful for any advice!
Below I have included my rewrite of your code to work the way you intended.
This runs properly for me.
Let me know if it works for you.
Please Note: I am using Python 3.6, tkinter and tktable.
Before I show the code, I will give a brief explanation of the tktable syntax for setting data into a row.
Your code was on the right track, but there were some errors preventing it from working.
Your first error was to use "var.set" to set data to the tktable array variable.
Instead, you should have been using "table.set" to set data to the tktable "table" pathname variable.
Secondly, because you are setting data to a row, you can use the set 'row' option.
Thirdly, you do Not need to specify "index=" and "value=".
You can simply enter the index and value, separated by commas.
If you have created variables for the index and value, you can use the variable names.
To set multiple values to multiple indexes on the same row,
you simply add more values, separated by commas.
You only need to specify the index of the first cell in this row.
For example:
You wrote:
var.set(index='active', value=entry.get())
This should be:
table.set('row', '1,0', entry.get(), 'Positive Sentiments', 'Negative Sentiments')
The above line presumes you want this 'row' to begin at cell index '1,0'.
The code you provided did not run for me.
Therefore, to get the code to run as intended, I have added a class structure.
If you have questions about classes and how to use them, most of these questions have already been answered in detail on Stack Overflow by Bryan Oakley and others.
The class structure I have added to your code is based on what I have learnt from Bryan Oakley via Stack Overflow.
Also, I made other changes as follows:
Amended your onButtonSents method, by changing the conditional statements, and correcting the "insert_rows" syntax,
so that the correct data is added to the next new line as you specified.
If you have any questions about this, please let me know.
Added some test sample data to the "objects" list and for the dictionaries: "positiveSentiments" and "negativeSentiments',
to demonstrate how the code works.
Since you have Not specified how you would like the code to behave when the user inputs an object that is Not on the list,
I have left this for you to do yourself.
Here is the revised code:
import tkinter as tk
import tktable
from tkinter import Tk, Label, Button
from tkinter import *
class Control(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.controller = self
self.shared_data = {
"objects": tk.Variable(),
"positiveSentiments": tk.Variable(),
"negativeSentiments": tk.Variable(),
"x": tk.IntVar(),
}
self.title('Title of Control Class')
self.entry_frame = tk.Frame()
self.entry_frame.pack(side=TOP)
self.user_input = tk.StringVar()
self.test_entry = tk.Entry(self.entry_frame, textvariable=self.user_input)
self.test_entry.pack(side=LEFT)
self.test_button = tk.Button(self.entry_frame, text='Button', command=self.onButtonSents)
self.test_button.pack(side=RIGHT)
self.table_frame = tk.Frame()
self.table_frame.pack(side=BOTTOM)
self.array_cols = ['Object', 'Positive sentiments', 'Negative sentiments']
self.var = tktable.ArrayVar()
self.table = tktable.Table(self.table_frame,
rows = 1,
cols = 3,
roworigin=0,
colorigin=0,
colwidth=20,
variable=self.var,)
self.table.pack(side='bottom', fill='both', expand=True)
self.table.set('row', '0,0', 'Object','Positive Sentiments', 'Negative Sentiments')
self.table.tag_configure('active', )
self.shared_data["objects"] = ['object1', 'object2', 'object3', 'object4', 'object5', 'object6', 'object7'] #an array of objects which user enters into entry field
self.shared_data["positiveSentiments"] = {'object1' : 'positive1', 'object2' : 'positive2', 'object3' : 'positive3', 'object4' : 'positive4', 'object5' : 'positive5', 'object6' : 'positive6', 'object7' : 'positive7' } #dictionary with key - object and positive words which correspond to it
self.shared_data["negativeSentiments"] = {'object1' : 'negative1', 'object2' : 'negative2', 'object3' : 'negative3', 'object4' : 'negative4', 'object5' : 'negative5', 'object6' : 'negative6', 'object7' : 'negative7'} #dictionary with key - object and negative words which correspond to it
#The method which puts data into the rows of table
def onButtonSents(self, _event=None):
#self.table.insert_rows(1)
for o in self.shared_data["objects"]:
if o == self.user_input.get():
if o in self.shared_data["positiveSentiments"]:
if o in self.shared_data["negativeSentiments"]:
if self.shared_data["x"].get() is not None:
self.shared_data["x"].set(self.shared_data["x"].get() + 1)
self.index1 = '%i,%i' % (self.shared_data["x"].get(), 0)
self.table.insert_rows(self.shared_data["x"].get() - 1, 1)
self.table.set('row', self.index1, self.user_input.get(), self.shared_data["positiveSentiments"].get(o), self.shared_data["negativeSentiments"].get(o))
else:
self.shared_data["x"].set(1)
self.index1 = '%i,%i' % (self.shared_data["x"].get(), 0)
self.table.set('row', self.index1, self.user_input.get(), self.shared_data["positiveSentiments"].get(o), self.shared_data["negativeSentiments"].get(o))
Control1 = Control()
Control1.mainloop()

For loop inside a function

I'm still learning Python, and I'd love to know a way to make the following work:
a_function(
for n,item in enumerate(list):
inside_function(code code code,
code code code,
code code code))
So there's a function nested inside another function, and I need to play out the inside function a number of times, but not the outside one. The code I'm working with is not mine so I can't change the way these functions work.
I can edit with the actual code if anyone needs it, it's something from PyChart.
Edit: actual code:
ar = area.T(y_coord = category_coord.T(data, 0),
x_grid_style=line_style.gray50_dash1,
x_grid_interval=chartlength/5, x_range = (0,chartlength),
x_axis=axis.X(label="X label"),
y_axis=axis.Y(label="Y label"))
chart_object.set_defaults(interval_bar_plot.T, direction="horizontal",
width=5, cluster_sep = 0, data=data)
ar.add_plot(
for n,item in enumerate(compactlist):
interval_bar_plot.T(line_styles = [None, None],
fill_styles = [fill_style.red, None],
label=compactlist[n], cluster=(n,len(compactlist)))
)
can = canvas.default_canvas()
can.set_title("Chromosome comparison")
can.set_author("Foo")
ar.draw()
The ar.add_plot function creates a working area in the canvas (as I understand it), while the interval_bar_plot function creates the bars, one by one. So I need multiple interval_bar_plot functions but only the one add_plot, or it simply repeats the first bar n times.
Edit: and the error:
File "intvlbar.py", line 105
for n,item in enumerate(compactlist):
^
SyntaxError: invalid syntax
What you are trying to do is pass several bar plot objects to the add_plot method (documented in here). One way you can do this is to pass them each explicitly. For example:
ar.add_plot(bar1, bar2, bar3)
Examples of this are in the sample code sections of the PyChart documentation for bar plots and interval bar plots, for example.
You do not want to do this because your compactlist might be inconveniently long or of varying length between runs. Another option is to use argument unpacking. Create a list containing your bar plot objects:
bars = [interval_bar_plot.T(line_styles = [None, None],
fill_styles = [fill_style.red, None],
label=compactlist[n], cluster=(n,len(compactlist)))
for n,item in enumerate(compactlist)]
Now call add_plot with your bars:
ar.add_plot(*bars)
The error you are getting is because the for loop does not return anything in itself. But the for loop is placed inside the function call ar.add_plot() where the parameters should go. So python is telling you "ar.add_plot() needs parameters, but this for loop isn't going to give them to me"
What parameters does ar.add_plot() need?
You need something closer to this (though this probably isn't correct):
ar.add_plot()
for n,item in enumerate(compactlist):
interval_bar_plot.T(line_styles = [None, None],
fill_styles = [fill_style.red, None],
label=compactlist[n], cluster=(n,len(compactlist)

Categories