I've been pushing this error about all day trying to solve it and decided i need help, i'm guessing its telling me that 'countrynames' is either not defined, not populated or has any values, yet it is used previously in the class 'mysqlbusiness' ok, i am trying to display a simple line of text that shows what item has been selected from a drop down list, the list is populated by a tuple, and a command to apply is selected by a radio button, the message is a polite note !
The TKinter error is this:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "S:\python\jon\wrt_toolkit_v5\toolbox_v7.py", line 141, in sendGift
name = mysqlbusiness.mysqlConnect.countrynames[idx]
AttributeError: 'function' object has no attribute 'countrynames'
The code snippet is this,
class mysqlbusiness:
def mysqlConnect():
import pymysql
sqlUsr = MysqlUsr.get()
sqlpwd = Mysqlpwd.get()
conn = pymysql.connect(host='192.168.0.27', user= sqlUsr, passwd=sqlpwd, db='information_schema')
cursor = conn.cursor()
cursor.execute("SELECT SCHEMA_NAME FROM SCHEMATA")
schema_names = cursor.fetchall()
schema_tuple = tuple(schema_names)
countrynames = (schema_tuple)
cnames = (countrynames)
lbox.insert(END, *schema_names)
# Colorize alternating lines of the listbox
for i in range(0,len(cnames),2):
lbox.itemconfigure(i, background='#CEECF5')
# create a vertical scrollbar to the right of the listbox
yscroll = tk.Scrollbar(command=lbox.yview, orient=tk.VERTICAL)
yscroll.grid(row=15, column=6, sticky='ns')
lbox.configure(yscrollcommand=yscroll.set)
lbox.selection_set(0)
conn.close()
# Called when the user double clicks an item in the listbox, presses
# the "apply" button, or presses the Return key. In case the selected
# item is scrolled out of view, make sure it is visible.
#
# Figure out which schema is selected, which command (connect or delete) is selected with the radiobuttons, and provide feedback.
def listConnection(*args):
idxs = lbox.curselection()
if len(idxs)==1:
idx = int(idxs[0])
lbox.see(idx)
name = mysqlbusiness.mysqlConnect.countrynames[idx]
# Gift sending left as an exercise to the reader
sentmsg.set("%s %s" % (gifts[gift.get()], name))
lbox.bind('<Double-1>', listConnection)
mainframe.bind('<Return>', listConnection)
You are making several mistakes:
You are using a class but defined only functions. Don't use classes to create namespaces, Python is not Java and does not require classes to be used.
Your mysqlConnect() function doesn't return anything. You cannot refer to local names in functions.
You didn't actually call the mysqlConnect() function.
You are mixing data retrieval with updating the UI; better to split that out to separate functions.
You are not extracting the column from the rows; each row result from the database is a tuple with columns (one in this case).
You are using several variable names for the same result set; there is no need really to make the results a tuple, for example.
There should be no need to re-bind the event handlers every time listConnection is called.
Corrected code:
import pymysql
def countrynames():
sqlUsr = MysqlUsr.get()
sqlpwd = Mysqlpwd.get()
conn = pymysql.connect(host='192.168.0.27', user=sqlUsr,
passwd=sqlpwd, db='information_schema')
cursor = conn.cursor()
cursor.execute("SELECT SCHEMA_NAME FROM SCHEMATA")
countrynames = [row[0] for row in cursor]
conn.close()
return countrynames
def set_countrynames_lbox(countrynames)
lbox.insert(END, *countrynames)
# Colorize alternating lines of the listbox
for i in range(0, len(countrynames), 2):
lbox.itemconfigure(i, background='#CEECF5')
# create a vertical scrollbar to the right of the listbox
yscroll = tk.Scrollbar(command=lbox.yview, orient=tk.VERTICAL)
yscroll.grid(row=15, column=6, sticky='ns')
lbox.configure(yscrollcommand=yscroll.set)
lbox.selection_set(0)
# Called when the user double clicks an item in the listbox, presses
# the "apply" button, or presses the Return key. In case the selected
# item is scrolled out of view, make sure it is visible.
#
# Figure out which schema is selected, which command (connect or delete) is selected with the radiobuttons, and provide feedback.
def listConnection(*args):
idxs = lbox.curselection()
if len(idxs)==1:
idx = int(idxs[0])
lbox.see(idx)
cnames = countrynames()
# optional, set the lbox again with
# set_countrynames_lbox(cnames)
name = cnames[idx]
# Gift sending left as an exercise to the reader
sentmsg.set("%s %s" % (gifts[gift.get()], name))
# moved out of the functions
lbox.bind('<Double-1>', listConnection)
mainframe.bind('<Return>', listConnection)
Related
I am building a user interface with several (as many as the user wants) tabular (spreadsheet-like) forms of user-specified size (but the size won't change once initialized). The user populates these tables either by copy-pasting data (usually from excel) or directly typing data to the cells. I am using the Tksheet Tkinter add-on.
It seems that there are several options in Tksheet to achieve the goal of opening an empty table of i rows and j columns:
a) set_sheet_data_and_display_dimensions(total_rows = None, total_columns = None).
This routine throws a TypeError. The error is raised in:
GetLinesHeight(self, n, old_method = False)
The subroutine expects the parameter n to be an integer, but receives a tuple.
The calling routine is sheet_display_dimensions, and the relevant line is:
height = self.MT.GetLinesHeight(self.MT.default_rh).
MT.default_rh is apparently a complex object, it can be an integer, but also a string or a tuple. Other routines that use it in Tksheet perform elaborate manipulation to make sure it is handed to the subroutine in integer form, but not so sheet_display_dimensions.
b) sheet_data_dimensions(total_rows = None, total_columns = None)
This seems to work programmatically, but does not display the table to the user.
One may add the line sheet_display_dimensions(i,j) but--you guessed it--this raises an error...
Sample code:
from tksheet import Sheet
import tkinter as tk
from tkinter import ttk
# This class builds and displays a test table. It is not part of the question but merely used to illustrate it
class SeriesTable(tk.Frame):
def __init__(self, master):
super().__init__(master) # call super class init to build frame
self.grid_columnconfigure(0, weight=1) # This configures the window's escalators
self.grid_rowconfigure(0, weight=1)
self.grid(row=0, column=0, sticky="nswe")
self.sheet = Sheet(self, data=[[]]) # set up empty table inside the frame
self.sheet.grid(row=0, column=0, sticky="nswe")
self.sheet.enable_bindings(bindings= # enable table behavior
("single_select",
"select_all",
"column_select",
"row_select",
"drag_select",
"arrowkeys",
"column_width_resize",
"double_click_column_resize",
"row_height_resize",
"double_click_row_resize",
"right_click_popup_menu",
"rc_select", # rc = right click
"copy",
"cut",
"paste",
"delete",
"undo",
"edit_cell"
))
# Note that options that change the structure/size of the table (e.g. insert/delete col/row) are disabled
# make sure that pasting data won't change table size
self.sheet.set_options(expand_sheet_if_paste_too_big=False)
# bind specific events to my own functions
self.sheet.extra_bindings("end_edit_cell", func=self.cell_edited)
self.sheet.extra_bindings("end_paste", func=self.cells_pasted)
label = "Change column name" # Add option to the right-click menu for column headers
self.sheet.popup_menu_add_command(label, self.column_header_change, table_menu=False, index_menu=False, header_menu=True)
# Event functions
def cell_edited(self, info_tuple):
r, c, key_pressed, updated_value = info_tuple # break the info about the event to individual variables
'''
updated_value checked here
'''
# passed tests
pass # go do stuff
def cells_pasted(self, info_tuple):
key_pressed, rc_tuple, updated_array = info_tuple # break the info about the event to individual variables
r, c = rc_tuple # row & column where paste begins
err_flag = False # will be switched if errors are encountered
'''
updated_array is checked here
'''
# passed tests
if err_flag: # error during checks is indicated
self.sheet.undo() # undo change
else:
pass # go do stuff
def column_header_change(self):
r, c = self.sheet.get_currently_selected()
col_name = sd.askstring("User Input", "Enter column name:")
if col_name is not None and col_name != "": # if user cancelled (or didn't enter anything), do nothing
self.sheet.headers([col_name], index=c) # This does not work - it always changes the 1st col
self.sheet.redraw()
# from here down is test code
tk_win = tk.Tk() # establish the root tkinter window
tk_win.title("Master Sequence")
tk_win.geometry("600x400")
tk_win.config(bg='red')
nb = ttk.Notebook(tk_win) # a notebook in ttk is a [horizontal] list of tabs, each associated with a page
nb.pack(expand=True, fill='both') # widget packing strategy
settings_page = tk.Frame(nb) # initiate 1st tab object in the notebook
nb.add(settings_page, text = "Settings") # add it as top page in the notebook
test = SeriesTable(nb) # creates a 1 row X 0 column table
nb.add(test, text = "Table Test") # add it as second page in the notebook
i = 4
j = 3
#test.sheet.set_sheet_data_and_display_dimensions(total_rows=i, total_columns=j) # raises TypeError
#test.sheet.sheet_data_dimensions(total_rows=i, total_columns=j) # extends the table to 4 X 3, but the display is still 1 X 0
#test.sheet.sheet_display_dimensions(total_rows=i, total_columns=j) # raises TypeError
test.sheet.insert_columns(j) # this works
test.sheet.insert_rows(i - 1) # note that we insert i-1 because table already has one row
test.mainloop()
I figured out a work-around with:
c)
insert_columns(j)
insert_rows(i - 1)
Note that you have to insert i-1 rows. This is because the sheet object is initiated with 1 row and 0 columns. (But does it say so in the documentation? No it does not...)
I've got some code in python using tkinter which retrieves the name of a room and uses that to to insert into an SQL database the room name and which site it belongs to. However when I run the code its not retrieving the room name from the entry box.
Can anyone help?
def addroom():
global screen14
global roomsinsite
roomsinsite = StringVar()
screen14 = Tk()
screen14.geometry("300x250")
screen14.title("Insert rooms")
Label(screen14, text = "Insert room name:", bg = "LightSkyBlue1", width = "300", height = "2").pack()
Label(screen14, text = "").pack()
roomsinsite_entry = Entry(screen14, textvariable = roomsinsite)
roomsinsite_entry.pack()
Button(screen14, text = "Register room", width = "12", height = "1", command = insertroom).pack()
def insertroom():
sitename4_info = sitename2.get()
print(sitename4_info)
roomname1_info = roomsinsite.get()
print(roomname1_info)
cursor = cnn.cursor()
# SQL to select the siteID and insert rooms for that paticular site.
siteID_fetch3 = "SELECT siteID FROM Sites WHERE siteName = %s"
cursor.execute(siteID_fetch3, [sitename4_info])
siteID_fetch3 = cursor.fetchall()
# print out the values retrieved
print(siteID_fetch3[0][0])
insertRooms = "INSERT INTO `rooms`(`siteID_fk2`, `roomname`) VALUES (%s,%s)"
insertRooms_val = (siteID_fetch3[0][0], roomname1_info)
cursor.execute(insertRooms, insertRooms_val)
# print out the rows inserted.
print(cursor.rowcount)
cnn.commit()
You are probably having more than one Tk in your code, which means your StringVar does not know which Tk to belong to. So here there are three possible solutions:
Avoid using more than one Tk and replace all child windows with Toplevel, so:
screen14 = Toplevel()
roomsinsite = StringVar()
If you are adamant that you want to use more than one instance of Tk then you can specify master for each StringVar, like:
screen14 = Tk()
roomsinsite = StringVar(master=screen14)
To be honest, I wouldn't use StringVar with entry widgets except when I want to use trace, here if the only purpose of using StringVar is for getting the value of the entry widget, then remove it and use get() method of the entry widget, like:
roomname1_info = roomsinsite_entry.get()
The combination of first and third method seems like best practice, if you ask me. Also here, even if you are not using more than one Tk, one of the above methods would certainly solve the problem(as far as something is inputted inside the entry and then the insertroom() is called).
I created 12 Entry boxes using a for loop with a default value of N/A. Any change in the text of entry is detected through .trace method.
I want to use reset button to make all the text on the Entry Boxes back to N/A
from tkinter import *
root = Tk()
t_diesel_price_EnF_variable = ["JanVar", "FebVar", "MarVar", "AprVar","MayVar","JuneVar","JulyVar","AugVar","SeptVar", "OctVar", "NovVar", "DecVar"]
t_diesel_price_EnF_values = ["N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A"]
def EnFChanging(*events):
for EnF in range(0,len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_values[EnF]=t_diesel_price_EnF_variable[EnF].get()
try:
t_diesel_price_EnF_values[EnF] = float(t_diesel_price_EnF_values[EnF])
except ValueError:
pass
print(t_diesel_price_EnF_values)
for EnF in range(0,len(t_diesel_price_EnF_values)):
t_diesel_price_EnF_variable[EnF] = StringVar(root , value = "N/A")
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10).pack()
t_diesel_price_EnF_variable[EnF].trace("w",EnFChanging)
def ChangeText():
for EnF in range(0, len(t_diesel_price_EnF_values)):
t_diesel_price[EnF].delete(0,END)
t_diesel_price[EnF].insert(0,"N/A")
return
b1 = Button(root, text = "Reset" , command = ChangeText).pack()
root.mainloop()
When I press the button it gives an error t_diesel_price[EnF].delete(0,END)
TypeError: 'NoneType' object is not subscriptable
What should I do now, Please ignore the basic errors of programming as I am a Mechanical Engineer with not a programming back ground. And I have to make a lot of other boxes too for my energy calculator.
You trying treat t_diesel_price as an Entry (and as a list) when your variable is None.
First of all I suggest you to install some IDE (e.g. PyCharm) and place break points to see whats wrong with variable!
Your problem occures because you create and pack your widget in one line! So t_diesel_price is None because pack() always returns None (link).
Just split your declaration and packing to:
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10)
t_diesel_price.pack()
After that it's works for me, except this fact that t_diesel_price is last created entry and the value changes only in it. So I assume that you need another list to iterate over entries:
...
# another fresh list
t_diesel_price_EnF_entries = list()
...
# declare entry
t_diesel_price = Entry(root, textvariable=t_diesel_price_EnF_variable[EnF], width=10)
# pack entry
t_diesel_price.pack()
# append entry to list
t_diesel_price_EnF_entries.append(t_diesel_price)
...
def ChangeText():
# iterate over entries
for diesel_price in t_diesel_price_EnF_entries:
diesel_price.delete(0,END)
diesel_price.insert(0,"N/A")
...
Alternatively you can iterate over StringVar's if you don't wanna store your entries at all:
def ChangeText():
# iterate over stringvars
for EnF in range(len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_variable[EnF].set('N/A')
And you can make it more readable as in example with entry iterating:
def ChangeText():
# iterate over stringvars
for string_var in t_diesel_price_EnF_variable:
string_var.set('N/A')
Cheers!
In this simple program I'm drawing a window, adding a search box, getting a search term for the user, looking that up in a postgreSQL DB and displaying the reults. It works up to a point - when the user enters a second query.
I have tried in two different ways but each way gives a different problem. In the first way (lines 32-35 http://pastebin.com/q5bnLxaB) I'm creating the output window in the main and passing that to the search function. It crashes with gtk_scrolled_window_add: assertion 'child_widget == NULL' failed. I think because I'm adding the tree to the window when it has already been done. To fix this, I would need to reset the window somehow.
In the second way (lines 56-58) I have added the output window only in the search function so it no longer has this crashing issue. However, the window itself does not update with the results of a second or subsequent search.
Which, if either, of these methods seems the most sensible? I also need to consider that the next step is adding a clickable button beside each search term which will display extended data for each returned result.
BTW, in both cases the output window does not appear until a search is entered. It doesn't hinder functionality but it strange to me.
#!/usr/bin/python
from gi.repository import Gtk
from gi.repository.GdkPixbuf import PixbufLoader
import urllib2
import psycopg2
import sys
class NotebookWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Rugby Database")
#Create Application Window
self.set_border_width(10)
self.set_default_size(800, 600)
self.set_position(Gtk.WindowPosition.CENTER)
#Add external container (box)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(vbox)
#Add tabbed window
nbook = Gtk.Notebook()
vbox.pack_start(nbook, True, True, 0)
nbook.show()
#Add Main tab
label = Gtk.Label()
label.set_markup("<b><big>Main</big></b>")
table = Gtk.Table(rows=40, columns=10)
#Add Results field
#results_box = Gtk.ScrolledWindow()
#results_box.set_vexpand(True)
#table.attach(results_box, 0, 1, 1, 39, xpadding=5, ypadding=5)
#Add Search box
entry = Gtk.Entry()
entry.set_property("secondary-icon-stock", Gtk.STOCK_FIND)
entry.connect("icon-press", self.on_search_button)
#entry.connect("activate", self.on_search_enter, results_box)
entry.connect("activate", self.on_search_enter, table)
table.attach(entry, 0, 1, 0, 1, xpadding=5)
nbook.append_page(table, label)
def on_search_button(self, entry, icon, event):
search = entry.get_text()
print("Search for " + search)
def on_search_enter(self, entry, table): #changed from results_box
store = Gtk.ListStore(str, str, str)
tree = Gtk.TreeView(store)
##
results_box = Gtk.ScrolledWindow()
results_box.set_vexpand(True)
table.attach(results_box, 0, 1, 1, 39, xpadding=5, ypadding=5)
##
search = entry.get_text()
search = search.replace("'","''") #deal with apostrophes
entry.set_text("")
print("Search for " + search)
result = self.lookup_player(search)
print result
if len(result) > 0:
for i in range(0, len(result)):
store.append([result[i][0],result[i][1],str(result[i][4])])
print result[i][0],result[i][1],result[i][2],result[i][3],result[i][4],result[i][5],result[i][6],result[i][7],result[i][8]
else:
print "No players found"
#Add column for last name
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Last Name")
column.pack_start(renderer, True)
column.add_attribute(renderer, "text", 0)
tree.append_column(column)
#Add column for first name
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("First Name")
column.pack_start(renderer, True)
column.add_attribute(renderer, "text", 1)
tree.append_column(column)
#Add column for icon
renderer = Gtk.CellRendererPixbuf()
column = Gtk.TreeViewColumn("Show")
column.pack_start(renderer, True)
column.add_attribute(renderer, "stock-id", 2)
tree.append_column(column)
results_box.add(tree)
table.show_all()
tree.show_all()
def on_click_edit(self, button):
print("Editing Player")
def lookup_player(self, pstring):
try:
con = psycopg2.connect(database='Rugby', user='postgres', password = '1234')
cur = con.cursor()
search = "select pl.lastname, pl.firstname, pl.height, pl.weight, to_char(pl.dob, 'DD/MM/YYYY'), cl.name, pl.injury_id, to_char(pl.suspendeduntil, 'DD/MM/YYYY'), pl.photo from player pl inner join club cl on cl.id = pl.currentclub_id where firstname ilike '%s' or lastname ilike '%s'" % (pstring, pstring)
cur.execute(search)
result = cur.fetchall()
return result
except psycopg2.DatabaseError, e:
print 'Error %s' % e
sys.exit(1)
finally:
if con:
con.close()
win = NotebookWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Note This is a follow-up question to
Python (GTK) : Can someone explain the difference between TreeStore, Listmodel and all the rest?
as the original query has been answered and the scope has thus changed (BTW massive thanks to everyone who helped with that!)
You should do everything you can in __init__(). This includes creating the store (as an instance variable so you can use it in the search handler), creating all widgets (ScrolledWindow and TreeView) and also adding the columns to the treeview.
In the search handler you should only call clear() on the ListStore (to remove any old results) and then append new results to the store.
I have a problem with the following code. Now I'm very new to programming in general, and most of the code is copied off the internet, and I adjusted it so it would work the way I want it to. So if there is no easy way of solving it, that's ok. Maybe you can just point out some topics of programming or python, that I should read about.
I try to explain it anyways. I have defined the function query(), that makes some changes to sqlite databases. The input is a list. That function works just fine if I use it just by itself.
Now I'm trying to have an Interface, where I can define, what should be inside that list, depending on what checkboxes are checked. Then I want to execute the function with that specific list, when I press a button. The checkboxes are generated just fine, the button, too. Also, when I check or uncheck the buttons, it updates the list just fine, and it shows the new updated list in the interpreter.
The problem is, that the button does not work:
1. It doesn't use the new updated list, instead it uses an empty list ()
2. When I input a predefined list, that is not empty, it automatically runs query() without me even clicking the button.
I may not have explained this well, but I hope you understand what my problem is.
Thanks for the help
`
def chkbox_checked():
for ix, item in enumerate(cb):
opt[ix]=(cb_v[ix].get())
print opt
def query(opt):
import sqlite3
connection = sqlite3.connect("gather.sqlite")
cursor1 = connection.cursor()
cursor1.execute('Drop table IF EXISTS matches')
cursor1.execute('CREATE TABLE matches(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matches"')
for i in range(0, len(opt)):
a=opt[i]
cursor1.execute('INSERT INTO matches (date, team1, team2, league) SELECT * FROM gather WHERE team1=? or team2=? or league=?', (a,a,a,))
cursor1.execute('Drop table IF EXISTS matchessorted')
cursor1.execute('CREATE TABLE matchessorted(date TEXT, team1 TEXT, team2 TEXT, league TEXT)')
cursor1.execute('DELETE FROM "main"."matchessorted"')
cursor1.execute('INSERT INTO matchessorted (date, team1, team2, league) SELECT * FROM matches ORDER BY date')
connection.commit()
import Tkinter as tk
from Tkinter import *
opt = []
root = tk.Tk()
mylist = [
'name1',
'name2',
'name3'
]
cb = []
cb_v = []
for ix, text in enumerate(mylist):
cb_v.append(tk.StringVar())
off_value=0
cb.append(tk.Checkbutton(root, text=text, onvalue=text,offvalue=off_value,
variable=cb_v[ix],
command=chkbox_checked))
cb[ix].grid(row=ix, column=0, sticky='w')
opt.append(off_value)
cb[-1].deselect()
label = tk.Label(root, width=20)
label.grid(row=ix+1, column=0, sticky='w')
button1 = Button(root, text = "Calculate", command = query(opt))
button1.grid(column=1, row=0, sticky=W)
root.mainloop()
`
A couple of points about how to structure your code: You need to write a function that populates the list based on your selection. It can return a list call 'options' and when you want to execute the code inside the query, you call the function that constructs the options list. The query function will have a statement like this:
options = get_options() #assuming the function that populates the options is called get_options
and then you execute the query function's code.
button1 = Button(root, text = "Calculate", command = query(opt))
This calls query(opt) immediately, before you create your Button, and passes the result of that call (None) to the Button constructor as the command argument. What you really want is a function that, when called, executes query(opt). Something like this:
def calculate_clicked():
query(opt)
button1 = Button(root, text = "Calculate", command = calculate_clicked)
or this:
button1 = Button(root, text = "Calculate", command = lambda : query(opt))