I'd like to know how I can add or delete widgets from within an imported module. I fail to access them correctly. I know, using OOP would make it easier, but I tried to grasp OOP and while the principles are easy I can't get my head around the details, so since I lack a proper teacher, I need a procedural solution.
This is the main script:
#!/usr/bin/python
try:
# Python2
import Tkinter as tk
except ImportError:
# Python3
import tkinter as tk
import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
import target
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"),width=300,height=200)
def test():
target.secondWindow()
root = tk.Tk()
root.geometry("600x350+30+50")
myframe = tk.Frame(root,relief="groove",bd=1)
myframe.place(x=20, y=30, width=560, height=200 )
canvas = tk.Canvas(myframe)
frame = tk.Frame(canvas)
myscrollbar=tk.Scrollbar(myframe, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="right", fill="y")
canvas.pack(side="left")
canvas.create_window((0,0), window=frame, anchor='nw')
allMissions = {
"1":{"name":"go"},
"2":{"name":"see"},
"3":{"name":"win"},
"4":{"name":"party"}} # this would be a text file
for a in allMissions.keys():
mn = allMissions[a]["name"]
tk.Label(frame, text=mn, justify="left").grid(row=int(a), column=0)
# what's bind really doing?
frame.bind("<Configure>", myfunction)
test = tk.Button(root, command=test, text="TEST")
test.place(x = 20, y = 250, width=580, height=40)
tk.mainloop()
and this is the imported module: target.py
try:
# Python2
import Tkinter as tk
except ImportError:
# Python3
import tkinter as tk
def changeMainWindow():
# here's where I'm stuck
print("What do I have to do to add a new")
print("label in the main window from here?")
print("Or to delete it?")
def secondWindow():
amWin = tk.Toplevel()
amWin.geometry("300x200+720+50")
button = tk.Button(amWin, text="OK", command=changeMainWindow)
button.place(x = 20, y = 80, width=260, height=30)
#amWin.mainloop() comment noticed (:
You do it by passing the memory address of whatever widget to the second program. There is no reason to import Tkinter again as you can just pass a pointer to the existing instance. If you are going to be doing anything more than simple experimenting with Tkinter, then it is well worth the time to learn classes first at one of the online sites like this one http://www.greenteapress.com/thinkpython/html/thinkpython016.html More here https://wiki.python.org/moin/BeginnersGuide/NonProgrammers
You aren't going to get many answers with the way the program is structured because most programmers use the class structure AFAIK, so do not know how to pound the code into a non-class environment, so will not have any answers. If the first program below used classes then the second program's class could be inherited, and the functions would become part of the first program's class and could be accessed in the same way as the existing classes, so no passing of pointers, or any other hack, would be necessary.
## I deleted some code for simplicity
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"),width=300,height=200)
def test():
TG.secondWindow()
root = tk.Tk()
root.geometry("600x350+30+50")
myframe = tk.Frame(root,relief="groove",bd=1)
myframe.place(x=20, y=30, width=560, height=200 )
canvas = tk.Canvas(myframe)
frame = tk.Frame(canvas)
myscrollbar=tk.Scrollbar(myframe, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="right", fill="y")
canvas.pack(side="left")
canvas.create_window((0,0), window=frame, anchor='nw')
# what's bind really doing?
frame.bind("<Configure>", myfunction)
test = tk.Button(root, command=test, text="TEST", bg="lightblue")
test.place(x = 20, y = 250, width=580, height=40)
tk.Button(root, text="Quit All", command=root.quit,
bg="orange").place(x=20, y=300)
""" instance of the class in the imported program
a pointer to the root window and the Tk instance are passed
"""
TG=target.Target(tk, root)
tk.mainloop()
And target.py. Notice there are no imports.
class Target():
def __init__(self, tk, root):
self.tk=tk
self.root=root
def changeMainWindow(self):
# here's where I'm stuck
self.tk.Label(self.amWin, bg="yellow", text =""""What do I have to do to add a new
label in the main window from here?
Or to delete it?""").place(x=50,y=20)
def secondWindow(self):
self.amWin = self.tk.Toplevel(self.root)
self.amWin.geometry("300x200+720+50")
button = self.tk.Button(self.amWin, text="Add Label",
command=self.changeMainWindow)
button.place(x = 20, y = 90, width=260, height=30).
Related
I have a readymade game(2048), where the game starts without a welcome window, so, I have just made a welcome window with a bunch of buttons like New Game and AI mode, now when I click the New Game button, I expect to get a new window where the game can be played. However, the game displays at the bottom of the main window, and another window that is supposed to display the actual game displays nothing.
# mainwindow.py file:
from tkinter import *
from PIL import ImageTk, Image
import game_2048
root = Tk()
root.iconbitmap('unnamed.ico')
root.title('2048')
bg = ImageTk.PhotoImage(Image.open("welcome.png"))
my_canvas = Canvas(root, width=780, height=550)
my_canvas.grid()
my_canvas.create_image(0, 0, image=bg, anchor=NW)
button1 = Button(root, text="New Game", fg="black", bg="#ddf0d0", padx=3,
pady=3, font=('Helvetica', '12', 'bold'), activebackground="#94d3c3", command=lambda: game_2048.mains(root)
)
button2 = Button(root, text="AI Mode", fg="black", bg="#ddf0d0",
padx=3, pady=3, font=('Helveica', '12', 'bold'), activebackground="#94d3c3")
button1_window = my_canvas.create_window(10, 10, anchor=NW, window=button1)
button2_window = my_canvas.create_window(120, 10, anchor=NW, window=button2)
root.mainloop()
And I have tried to modify the game_2048 file i.e. 2048 game like this:
def mains(root):
Top = Toplevel(root)
l1 = Button(Top, command=lambda:Game())
l1.pack()
class Game(tkinter.Frame):
def __init__(self):
tkinter.Frame.__init__(self)
self.grid()
self.master.title('2048')
self.main_grid = tkinter.Frame(
self, bg=c.GRID_COLOR, bd=3, width=400, height=400)
self.main_grid.grid(pady=(80, 0))
self.make_GUI()
self.start_game()
self.master.bind("<Left>", self.left)
self.master.bind("<Right>", self.right)
self.master.bind("<Up>", self.up)
self.master.bind("<Down>", self.down)
self.mainloop()
if __name__ == "__main__":
mains()
I am pretty sure that I have made some mistakes in the mains() function as a result of which I am not getting the desired output. So my question is what should I do to rectify these mistakes?
Here are the changes I made to your code:
Removed import mainwindow from game_2048.py, as it will lead to circular import calling the functions twice.
You created a class inheriting from a Frame, this frame is just like any other tkinter widget, requires to be placed in the window using geometric manager, like pack()/grid()/place(), you did not do that
I also destroyed the root window before creating another new root window. Also note that instead of using Toplevel, I used Tk. This is done, so that closing the game window will close the app. But if using Toplevel, you will have to close the menu window too.
from tkinter import *
import tkinter
import random
import colors as c
def mains(root):
root.destroy()
root = Tk()
def init(root):
l1.destroy()
game = Game(root)
game.pack()
l1 = Button(root, text='Start Game',command=lambda: init(root))
l1.pack()
class Game(tkinter.Frame):
def __init__(self, parent):
tkinter.Frame.__init__(self, parent)
.......
But if you were to ask me, I would not go for such structure, but this will work here too. Understand this, work on it, implement your own structure that does not call more than one Tk().
I am currently working on a little just for fun project, which pretends it´s generating something and then shows a specific message, but I have a question: As soon as I press the button on the screen it is showing a progressbar, that is what I want it to do, but if I press the button again it just shows the same thing again and again, is there any way to prevent the program from printing the Starting the generate text and the progressbar multiple times?
Here´s the code:
# my little import area
import tkinter as tk
from tkinter import ttk
# Initialization
win = tk.Tk()
win.title("StackOverflow")
# Window Size
win.resizable(False, False)
win.minsize(750,500)
# Button clicked command
def buttonclicked():
tk.Label(win, text="Starting to generate...").pack()
pb.pack()
pb.start(500)
#Widgets
headerlabel = tk.Label(win, text="StackOverFlow Question")
generatebutton = tk.Button(win, text="Generate", command=buttonclicked)
pb = ttk.Progressbar(win, orient="horizontal", length=250, mode="determinate")
#Positioning
headerlabel.pack()
generatebutton.pack()
win.mainloop()
You can put
global generatebutton
generatebutton.config(state='disabled')
in your buttonclicked function (which is stupid because the global keyword is usually SO BAD to use, it turns your code into nightmare), or use OOP to your advantage. You can also use win.destroy() or generatebutton.destroy().
Here is that more OOP-intensive code example:
import tkinter as tk
from tkinter import ttk
class Joke:
def __init__(self):
self.win = tk.Tk()
self.win.title("StackOverflow")
self.win.resizable(False, False)
self.win.minsize(750,500)
headerlabel = tk.Label(self.win, text="StackOverFlow Question")
self.generatebutton = tk.Button(self.win, text="Generate", command=self.buttonclicked)
self.pb = ttk.Progressbar(self.win, orient="horizontal", length=250, mode="determinate")
headerlabel.pack()
self.generatebutton.pack()
self.win.mainloop()
def buttonclicked(self):
tk.Label(self.win, text="Starting to generate...").pack()
self.pb.pack()
self.pb.start(500)
self.generatebutton.config(state='disabled')
Joke()
Hope that's helpful!
THis is my code for TKinter GUI:
from tkinter import *;
from tkinter import ttk
# import tix as tk
class pryprotclass:
def onFrameConfigure(self, event,canvas1):
canvas1.configure(scrollregion=canvas1.bbox("all"))
def __init__(self,master):
frame2=Frame(master);
frame2.configure(background='yellow')
frame2.pack_propagate(False) ;
frame2.pack(fill="both", expand=True,side=RIGHT);
canvas1 = Canvas(frame2, borderwidth=0, background="#ffffff")
canvas1.configure(scrollregion=canvas1.bbox("all"))
frame3=Frame(canvas1);
vsb = Scrollbar(frame3, orient="vertical", command=canvas1.yview)
canvas1.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas1.pack(side="left", fill="both", expand=True)
canvas1.create_window((4,4), window=frame3, anchor="nw")
canvas1.configure(scrollregion=canvas1.bbox("all"))
frame3.bind("<Configure>"),
canvas1.configure(scrollregion=canvas1.bbox("all"))
frame3.config( width=240 );
frame3.configure(background='purple')
frame3.pack_propagate(False) ;
now there are around 40 labels now to they take up the entire y direction
See image--https://imgur.com/Epq6vJJ
What is the error in the code?
Neither is the scrollbar working nor am I able to see the bar in the scrollbar to move up or down?
here is the requested code (unneccessary)
label=Label(frame3,text='ALL TASKS')
label.configure(background="WHITE",font="Courier 14 bold")
label.pack(side=TOP,anchor="nw",padx=10,pady=10)
chromelogo=PhotoImage(file="./Images/chrome.gif")
chromelogo=chromelogo.subsample(16,16)
labelchrome=Label(frame3,text="Chrome")
labelchrome.config(image=chromelogo)
labelchrome.config(background='WHITE')
labelchrome.config(compound='left')
labelchrome.image=chromelogo
labelchrome.pack(side=TOP,anchor="nw")
c1=Checkbutton(frame3,text="History")
c2=Checkbutton(frame3,text="Cookies")
c3=Checkbutton(frame3,text="Saved Passwords")
c4=Checkbutton(frame3,text="Download History")
c5=Checkbutton(frame3,text="Last Download Location")
c1.config(background='WHITE')
c2.config(background='WHITE')
c3.config(background='WHITE')
c4.config(background='WHITE')
c5.config(background='WHITE')
c1.pack(anchor="nw",padx=30)
c2.pack(anchor="nw",padx=30)
c3.pack(anchor="nw",padx=30)
c4.pack(anchor="nw",padx=30)
c5.pack(anchor="nw",padx=30)
firefoxlogo=PhotoImage(file="./Images/firefox.gif")
firefoxlogo=firefoxlogo.subsample(18,18)
labelfirefox=Label(frame3,text="Firefox")
labelfirefox.config(background='WHITE')
labelfirefox.config(image=firefoxlogo)
labelfirefox.config(compound='left')
labelfirefox.image=firefoxlogo
labelfirefox.pack(side=TOP,anchor="nw")
c6=Checkbutton(frame3,text="History")
c7=Checkbutton(frame3,text="Cookies")
c8=Checkbutton(frame3,text="Saved Passwords")
c9=Checkbutton(frame3,text="Internet Cache")
c10=Checkbutton(frame3,text="Saved Form Information")
c6.config(background='WHITE')
c7.config(background='WHITE')
c8.config(background='WHITE')
c9.config(background='WHITE')
c10.config(background='WHITE')
c6.pack(anchor="nw",padx=30)
c7.pack(anchor="nw",padx=30)
c8.pack(anchor="nw",padx=30)
c9.pack(anchor="nw",padx=30)
c10.pack(anchor="nw",padx=30)
iexplorelogo=PhotoImage(file="./Images/iexplore.gif")
iexplorelogo=iexplorelogo.subsample(12,12)
labeliexplore=Label(frame3,text="Internet Explorer")
labeliexplore.config(image=iexplorelogo)
labeliexplore.config(background='WHITE')
labeliexplore.config(compound='left')
labeliexplore.image=iexplorelogo
labeliexplore.pack(side=TOP,anchor="nw")
c11=Checkbutton(frame3,text="History")
c12=Checkbutton(frame3,text="Cookies")
c13=Checkbutton(frame3,text="Last Download Location")
c14=Checkbutton(frame3,text="Temporary Internet Files")
c15=Checkbutton(frame3,text="Autocomplete Form History")
c11.config(background='WHITE')
c12.config(background='WHITE')
c13.config(background='WHITE')
c14.config(background='WHITE')
c15.config(background='WHITE')
c11.pack(anchor="nw",padx=30)
c12.pack(anchor="nw",padx=30)
c13.pack(anchor="nw",padx=30)
c14.pack(anchor="nw",padx=30)
c15.pack(anchor="nw",padx=30)
explorerlogo=PhotoImage(file="./Images/explorer.gif")
explorerlogo=explorerlogo.subsample(16,16)
explorerlabel=Label(frame3,text="Windows Explorer")
explorerlabel.config(image=explorerlogo)
explorerlabel.config(background='WHITE')
explorerlabel.config(compound='left')
explorerlabel.image=explorerlogo
explorerlabel.pack(side=TOP,anchor="nw")
c16=Checkbutton(frame3,text="Recent Documents")
c17=Checkbutton(frame3,text="Run(in Start Menu)")
c18=Checkbutton(frame3,text="Network Passwords")
c16.config(background='WHITE')
c17.config(background='WHITE')
c18.config(background='WHITE')
c16.pack(anchor="nw",padx=30)
c17.pack(anchor="nw",padx=30)
c18.pack(anchor="nw",padx=30)
systemlogo=PhotoImage(file="./Images/system.gif")
systemlogo=systemlogo.subsample(16,16)
systemlabel=Label(frame3,text="System")
systemlabel.config(image=systemlogo)
systemlabel.config(background='WHITE')
systemlabel.config(compound='left')
systemlabel.image=systemlogo
systemlabel.pack(side=TOP,anchor="nw")
c19=Checkbutton(frame3,text="Recycle Bin")
c20=Checkbutton(frame3,text="Temporary Files")
c21=Checkbutton(frame3,text="Clipboard")
c22=Checkbutton(frame3,text="DNS Cache")
#c23=Checkbutton(frame3,text="add")
c19.config(background='WHITE')
c20.config(background='WHITE')
c21.config(background='WHITE')
c22.config(background='WHITE')
c19.pack(anchor="nw",padx=30)
c20.pack(anchor="nw",padx=30)
c21.pack(anchor="nw",padx=30)
c22.pack(anchor="nw",padx=30)
frame3.pack(fill="y", expand=False,side="left");
frame4=Frame(frame2);
frame4.configure(background='PINK')
frame4.pack_propagate(False) ;
frame5=Frame(frame4)
frame5.configure(background='green')
progressbar=ttk.Progressbar(frame4,orient=HORIZONTAL)
progressbar.pack(fill=BOTH,padx=20,pady=15)
run_PryPro=Button(frame4,text="RUN")
frame5.configure(height=300)
frame5.pack(fill=BOTH,expand=True,padx=20)
run_PryPro.pack(padx=60,pady=25,ipadx=20,ipady=10)
frame4.packfill="both", expand=True,side="right");
main Class's Code as requested ==
from tkinter import * ;
from lefttoolbar import *;
from pyprot import *;
from logotop import *;
root=Tk()
obj_pryprot=pryprotclass(root);
=root.minsize(800, 500);
root.mainloop();
You need to be more methodical in writing your code. I count at least nine bugs in your code. You need to start by creating as few widgets as possible, and then add features only when you know the existing features are working correctly. It's very difficult to debug eight things at once.
Here are the things that need fixing:
PEP8 recommends that class names begin with an uppercase character
You need to remove frame3.pack_propagate(False). Turning off geometry propagation is an advanced feature that you almost never should use.
You need to remove frame2.pack_propagate(False)
You need to make the scrollbar a child of frame2, not frame3, since frame3 is what you want to scroll. As a general rules, scrollbars and the things they scroll should have the same parent/master
You need to give the canvas a size
you need to make canvas1 be an instance variable
Your bind needs to associate a function with the event
The bound function needs to take a single argument
The bound function needs to use self.canvas1 rather than canvas1
Here's a version of your code with all of those issues fixed:
from tkinter import *;
class Pryprotclass:
def onFrameConfigure(self, event):
self.canvas1.configure(scrollregion=self.canvas1.bbox("all"))
def __init__(self,master):
frame2=Frame(master);
frame2.pack(fill="both", expand=True, side=RIGHT);
self.canvas1 = Canvas(frame2, borderwidth=0, background="#ffffff")
vsb = Scrollbar(frame2, orient="vertical", command=self.canvas1.yview)
self.canvas1.configure(yscrollcommand=vsb.set)
self.canvas1.pack(side="left", fill="both", expand=True)
vsb.pack(side="right", fill="y")
frame3=Frame(self.canvas1);
self.canvas1.create_window((4,4), window=frame3, anchor="nw")
frame3.bind("<Configure>", self.onFrameConfigure),
# dummy widgets for testing
for i in range(100):
label = Label(frame3, text="Item #%s" % i)
label.pack(side="top", fill="x", padx=10, pady=1)
root = Tk()
p = Pryprotclass(root)
root.mainloop()
The following script creates a tkinter window with a text label, exit button and change-text button:
from tkinter import *
from tkinter import ttk
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
test_label = Label(root, text="none").grid(row=0, column=0, sticky=W)
change_text_btn = Button(root, text="change_text", command=self.set_label_text).grid(row=2, column=0, sticky=W)
exit_btn = Button(root, text="Exit", command=self.client_exit).grid(row=2, column=1, sticky=W)
def set_label_text(self):
test_label.config(text='changed the value')
def client_exit(self):
exit()
if __name__ == '__main__':
root = Tk()
app = Window(root)
root.mainloop()
After click on change_text_btn I get a NameError: name 'test_label' is not defined error. So the problem is that test_label created in init_window() is not avaliable from set_label_text() beacuse of the scope. How do I fix it?
To overcome the issue, you can make test_label an instance variable by prefixing it with self. Besides that, when you chain methods like that, what happens is you assign None to your variable, since grid() returns None - instead, place each method in a separate line (this stands for all of your buttons):
self.test_label = Label(root, text="none")
self.test_label.grid(row=0, column=0, sticky=W)
Of course, you'd need to refer to it with self.test_label later on in your set_label_text function.
Other than that, I suggest you get rid of from tkinter import *, since you don't know what names that imports. It can replace names you imported earlier, and it makes it very difficult to see where names in your program are supposed to come from. Use import tkinter as tk instead.
I am attempting to create a GUI using Python classes. Because I'm new to Python, I'm still learning to troubleshoot my errors. Below, I wish to create a class called Plot_Seismo, and create a GUI that has a listbox, a quit button, and a "Plot" button. I am having trouble with the "Plot_button" within the class. What I want this button to do is read in the seismogram and then plot the selected seismogram from the listbox. I have a feeling my syntax is incorrect (due to my naivete). I can get this to work when this function isn't in a class, but when I put it in a class as a method, I get slightly confused. The error message is shown below:
#!/usr/bin/env python
from Tkinter import *
from obspy.core import read
import math
class Plot_Seismo:
def __init__(self, parent):
self.master = parent
top = Frame(parent, width=500, height=300)
top.pack(side='top')
# create frame to hold the first widget row:
hwframe = Frame(top)
# this frame (row) is packed from top to bottom (in the top frame):
hwframe.pack(side='top')
# create label in the frame:
font = 'times 18 bold'
hwtext = Label(hwframe, text='Seismogram Reader GUI', font=('Arial',18,'bold'), fg="red")
hwtext.pack(side='top')
### ListBox
List1 = Listbox(root, width=50, height= 10)
List1.insert(1,"trace1.BHZ")
List1.insert(2,"trace2.BHZ")
List1.pack(padx=20, pady=20)
plot_button = Button(top, text='Plot Seismogram', command=self.plot_seis)
plot_button.pack(side='top', anchor='w', padx=45, pady=20)
self.event = read(List1.get(List1.curselection()[0]))
# finally, make a quit button and a binding of q to quit:
quit_button = Button(top, text='Quit Seismo-Reader GUI', command=self.quit)
quick_button.pack(side='top', anchor='w', padx=20, pady=20)
self.master.bind('<q>', self.quit)
def quit(self, event=None):
self.master.quit()
def plot_seis(self, event=None):
self.event.plot()
root = Tk()
Plot_Seismo = Plot_Seismo(root)
root.mainloop()
Error Message:
Traceback (most recent call last):
File "plot_seismogram.py", line 46, in <module>
Plot_Seismo = Plot_Seismo(root)
File "plot_seismogram.py", line 31, in __init__
self.event = read(List1.get(List1.curselection()[0]))
IndexError: tuple index out of range
Since I do not have obspy module installed, I had to shrink down your code a bit, but you should get the point.
Since I only have Python3 running on my machine, I rewrote your code to Python3 which is not a big deal. The only differences should be (tkinter instead of Tkinter and print() instead of print).
I changed some parts of your code: The listbox is no populated using a list, which make this a bit easier and it became a class attribute to access it in plot_seis.
Since .curselection() returns a tuple with the listboxes entry index, we have to get the according text entry as described in the docs and in this answer.
Buttons and maybe the listbox provide some event handling features which are making the listbox an class attribute by using self. kind of ugly somehow, but it does the job:
#!/usr/bin/env python3
# coding: utf-8
from tkinter import *
# from obspy.core import read
import math
class Plot_Seismo:
def __init__(self, parent):
self.master = parent
top = Frame(parent, width=500, height=300)
top.pack(side='top')
# create frame to hold the first widget row:
hwframe = Frame(top)
# this frame (row) is packed from top to bottom (in the top frame):
hwframe.pack(side='top')
# create label in the frame:
font = 'times 18 bold'
hwtext = Label(hwframe, text='Seismogram Reader GUI', font=('Arial',18,'bold'), fg="red")
hwtext.pack(side='top')
### ListBox
self.List1 = Listbox(root, width=50, height= 10)
# populate listbox using a list with desired entries
self.list_entries = ["trace1.BHZ", "trace2.BHZ"]
for i, element in enumerate(self.list_entries):
self.List1.insert(i, element)
self.List1.pack(padx=20, pady=20)
plot_button = Button(top, text='Plot Seismogram', command=self.plot_seis)
plot_button.pack(side='top', anchor='w', padx=45, pady=20)
# finally, make a quit button and a binding of q to quit:
quit_button = Button(top, text='Quit Seismo-Reader GUI', command=self.quit)
quit_button.pack(side='top', anchor='w', padx=20, pady=20)
self.master.bind('<q>', self.quit)
def quit(self, event=None):
self.master.quit()
def plot_seis(self, event=None):
selection_index = self.List1.curselection()[0]
selection_text = self.List1.get(selection_index)
print(selection_text)
# do something with `read` from the `obspy.core` module
# read(selection_text)
root = Tk()
Plot_Seismo = Plot_Seismo(root)
root.mainloop()