I am creating GUI using Python. It was working correctly until I included a button in for loop at __init__
. I am not getting any error when I compile in command prompt. GUI is not opening. What's causing this error?
New.py:
class myapp:
def callfunc(title = "", author = "", body = ""):
L1 = Label(top, text="Title")
L1.pack( side = TOP)
E1 = Entry(top, bd =5)
E1.pack(side = TOP)
E1.insert(0,title)
L2 = Label(top, text="Author")
L2.pack( side = TOP)
E2 = Entry(top, bd =5)
E2.pack(side = TOP)
E2.insert(0,author)
L3 = Label(top, text="Body")
L3.pack( side = TOP)
E3 = Entry(top, bd =5)
E3.pack(side = TOP)
E3.insert(0,body)
data = {"author": E2.get(),
"body" : E3.get(),
"title" : E1.get()}
data_json = json.dumps(data)
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
url = 'http://localhost/spritle/api.php?action=insert_list&data_json='
check = connected_to_internet(url)
if(check):
r = requests.post(url+data_json ,headers=headers )
if (r.status_code == 200):
tkMessageBox.showinfo("Result","success")
else:
if(os.path.isfile("offline_post.json")):
with open('offline_post.json','a') as f:
f.write(data_json+"\n")
else:
open('offline_post.json', 'a')
with open('offline_post.json','a') as f:
f.write(data_json+"\n")
SubmitButton = Button(top,text="Submit", fg="White", bg="#0094FF",
font=("Grobold", 10), command = callfunc)
SubmitButton.pack()
# homeButton = Button(text="Home", fg="White", bg="#0094FF",
# font=("Grobold", 10), command = view)
# homeButton.pack()
def connected_to_internet(url, timeout=5):
try:
_ = requests.get(url, timeout=timeout)
threading.Timer(10, connected_to_internet(url)).start()
print "asd"
return True
except requests.ConnectionError:
print("No internet connection available.")
return False
def __init__(self, parent):
self.row=0
url = "http://localhost/spritle/api.php?action=get_users";
r = requests.get(url)
j = r.json()
E1 = Label(top, text="Title")
E1.grid(row=self.row, column=0)
E1 = Label(top, text="Author")
E1.grid(row=self.row, column=1)
E1 = Label(top, text="Body")
E1.grid(row=self.row, column=2)
for val in j:
self.row += 1
T1 = Label(top, text=val['title'])
T1.grid(row=self.row, column=0)
A1 = Label(top, text=val['author'])
A1.grid(row=self.row, column=1)
B1 = Label(top, text=val['body'])
B1.grid(row=self.row, column=2)
editButton = Button(top, text="Edit", fg="White", bg="#0094FF",
font=("Grobold", 5), command = lambda: callfunc(val['title'],val['author'],val['body']))
editButton.pack()
newButton = Button(top, text="New Post", fg="White", bg="#0094FF",
font=("Grobold", 5), command = lambda: callfunc)
newButton.pack()
top = Tk()
top.title("App")
app = myapp(top)
top.mainloop()
Answer
The issue with your code is likely related to how the buttons are being created inside the for
loop and how you are managing the GUI's main event loop. Here are a few potential causes and fixes:
1. Indentation and Class Structure:
You should define your methods and the __init__
constructor properly within the class. The callfunc
method and __init__
method are not properly indented inside the class. They should be part of the myapp
class.
2. Button Packing in the Loop:
You are calling Button.pack()
inside the for
loop in the callfunc
method, which may be causing the issue. You should avoid mixing the grid
and pack
layout managers for the same widget. In your code, you are using grid()
for labels, but using pack()
for buttons inside the loop. It's recommended to use one layout manager consistently within the same container.
3. Callback Functions:
You are passing the callfunc
method as a command for buttons. Since it's called with parameters, you should ensure that the lambda function is correctly passing arguments to callfunc
.
4. Global Variables and Tkinter Initialization:
The top-level Tk
widget (top
) should be created once and passed correctly to the class.
Updated Code:
import tkinter as tk
from tkinter import messagebox
import requests
import json
import os
import threading
class myapp:
def __init__(self, parent):
self.top = parent
self.row = 0
url = "http://localhost/spritle/api.php?action=get_users"
r = requests.get(url)
j = r.json()
# Create labels
self.E1 = tk.Label(self.top, text="Title")
self.E1.grid(row=self.row, column=0)
self.E2 = tk.Label(self.top, text="Author")
self.E2.grid(row=self.row, column=1)
self.E3 = tk.Label(self.top, text="Body")
self.E3.grid(row=self.row, column=2)
for val in j:
self.row += 1
T1 = tk.Label(self.top, text=val['title'])
T1.grid(row=self.row, column=0)
A1 = tk.Label(self.top, text=val['author'])
A1.grid(row=self.row, column=1)
B1 = tk.Label(self.top, text=val['body'])
B1.grid(row=self.row, column=2)
editButton = tk.Button(self.top, text="Edit", fg="White", bg="#0094FF",
font=("Grobold", 5), command=lambda title=val['title'], author=val['author'], body=val['body']: self.callfunc(title, author, body))
editButton.grid(row=self.row, column=3)
newButton = tk.Button(self.top, text="New Post", fg="White", bg="#0094FF",
font=("Grobold", 5), command=self.callfunc)
newButton.grid(row=self.row + 1, column=3)
def callfunc(self, title="", author="", body=""):
# Create entry fields to add a new post or edit an existing post
L1 = tk.Label(self.top, text="Title")
L1.pack(side=tk.TOP)
E1 = tk.Entry(self.top, bd=5)
E1.pack(side=tk.TOP)
E1.insert(0, title)
L2 = tk.Label(self.top, text="Author")
L2.pack(side=tk.TOP)
E2 = tk.Entry(self.top, bd=5)
E2.pack(side=tk.TOP)
E2.insert(0, author)
L3 = tk.Label(self.top, text="Body")
L3.pack(side=tk.TOP)
E3 = tk.Entry(self.top, bd=5)
E3.pack(side=tk.TOP)
E3.insert(0, body)
data = {
"author": E2.get(),
"body": E3.get(),
"title": E1.get()
}
data_json = json.dumps(data)
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
url = 'http://localhost/spritle/api.php?action=insert_list&data_json='
check = self.connected_to_internet(url)
if check:
r = requests.post(url + data_json, headers=headers)
if r.status_code == 200:
messagebox.showinfo("Result", "Success")
else:
if os.path.isfile("offline_post.json"):
with open('offline_post.json', 'a') as f:
f.write(data_json + "\n")
else:
open('offline_post.json', 'a')
with open('offline_post.json', 'a') as f:
f.write(data_json + "\n")
else:
messagebox.showerror("Error", "No internet connection available.")
def connected_to_internet(self, url, timeout=5):
try:
_ = requests.get(url, timeout=timeout)
threading.Timer(10, self.connected_to_internet, args=[url]).start()
return True
except requests.ConnectionError:
print("No internet connection available.")
return False
# Main Tkinter setup
top = tk.Tk()
top.title("App")
app = myapp(top)
top.mainloop()
Key Changes:
- Indentation and Class Structure: The methods (
callfunc
,connected_to_internet
, etc.) are now properly indented as part of themyapp
class. - Lambda for Button Commands: The
lambda
function now correctly passes the parameters (title
,author
,body
) tocallfunc
. - Consistent Layout Manager: I used
grid()
for both the labels and buttons inside the__init__
method, keeping it consistent. - Message Box:
tkMessageBox.showinfo
is replaced withmessagebox.showinfo
, which is the correct usage.
Additional Recommendations:
- Network Operations: Since you're checking for network connectivity, ensure that the
connected_to_internet
function doesn't overwhelm the network with continuous requests. You might want to limit the retries and add more graceful error handling. - Button Placement: If you have many buttons or dynamic content, consider using
pack()
orgrid()
more efficiently to prevent overlap or UI misalignment.
Let me know if you encounter any further issues!