Hi i've a code that look like this:
from tkinter import *
from tkinter import ttk
import socket, time, datetime
class UI:
def __init__(self):
self.root = Tk()
self.root.title("Chating App")
self.root.geometry("500x500")
def widgets(self):
self.m1 = PanedWindow(self.root, sashrelief=RAISED, width=10)
self.m1.pack(fill=BOTH, expand=1)
self.main_frame_1 = Frame(self.m1)
self.m1.add(self.main_frame_1)
Label(self.main_frame_1, text="hello worlds").pack()
self.m2 = PanedWindow(self.m1, orient=VERTICAL)
self.m1.add(self.m2)
self.label2 = Label(self.m2, text="top")
self.m2.add(self.label2)
def run(self):
self.widgets()
mainloop()
UI().run()
and my question is how can i set the maximum distances of the sash to be dragged?, so the user can drag the sash without fully shrinking the other pane. sorry if i misspelling :)
When you add item to PanedWindow then you can set its minimal size
self.m1.add(..., minsize=100)
If you use horizontal orientation then it will be used as minimal width.
If you use vertical orientation then it will be used as minimal height.
Documentation: PanedWindow
Working code:
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import ttk
class UI:
def __init__(self):
self.root = tk.Tk()
self.root.title("Chating App")
self.root.geometry("500x500")
def widgets(self):
self.m1 = tk.PanedWindow(self.root, width=10, sashrelief='raised')
self.m1.pack(fill='both', expand=True)
self.main_frame_1 = tk.Frame(self.m1)
self.m1.add(self.main_frame_1, minsize=100)
self.label_hello = tk.Label(self.main_frame_1, text="hello worlds")
self.label_hello.pack()
self.m2 = tk.PanedWindow(self.m1, orient='vertical', sashrelief='raised')
self.m1.add(self.m2, minsize=100)
self.label_top = tk.Label(self.m2, text="top")
self.m2.add(self.label_top, minsize=100)
self.label_bottom = tk.Label(self.m2, text="bottom")
self.m2.add(self.label_bottom, minsize=100)
def run(self):
self.widgets()
self.root.mainloop()
UI().run()
PEP 8 -- Style Guide for Python Code
Related
I am working on a game called 'Flag Quiz' using tkinter. I have a script called mainmenu where I can choose between an easy mode and a hard mode. If I click on one of the buttons the recent mainmenu window disappears and a new tkinter window opens.
Here is my mainmenu script:
from tkinter import *
import tkinter as tk
from hardmode import HardApp
from easymode import EasyApp
class TitleScreen(tk.Tk):
def __init__(self):
super().__init__()
self.title('Flag Quiz')
self.geometry('600x600')
self.resizable(0,0)
self.make_widgets()
def make_widgets(self):
self.background = PhotoImage(file = './background.png')
self.label = Label(self, image=self.background)
self.label.place(x=0, y=0, relwidth=1, relheight=1)
self.easy = Button(self, text="Easy Mode", height=2, width=6, font=('default', 20), command=self.play_easy)
self.hard = Button(self, text="Hard Mode", height=2, width=6, font=('default', 20), command=self.play_hard)
self.easy.place(relx=0.5, rely=0.45, anchor=CENTER)
self.hard.place(relx=0.5, rely=0.55, anchor=CENTER)
def play_easy(self):
self.withdraw()
self.app = EasyApp()
#self.app.start()
def play_hard(self):
self.withdraw()
self.app = HardApp()
#self.app.start()
def start(self):
self.mainloop()
TitleScreen().start()
And here is my easy mode script:
import tkinter as tk
from tkinter import *
import random
import os
import json
class EasyApp(tk.Toplevel):
def __init__(self):
super().__init__()
self.title('Flag Quiz')
self.geometry('')
self.resizable(0,0)
self.score = 0
self.create_widgets()
def create_widgets(self):
# variables
self.user_guess = StringVar(self)
self.text = StringVar(self)
self.text.set(" ")
# initial image
self.scoretext = Label(self, text="Score: ").pack(side='top', fill='x')
self.scorevalue = Label(self, text=self.score).pack(side='top', fill='x')
self.file = random.choice(os.listdir('pngs'))
self.randimg = PhotoImage(file='pngs/{}'.format(self.file))
self.randimg = self.randimg.subsample(2, 2)
self.panel = Label(self, image=self.randimg)
self.panel.pack()
self.country, self.ext = self.file.split('.')
self.countries = self.load_lookup()
self.countryname = [country for country in self.countries if country['alpha2'] == self.country]
self.s = []
for i in range(0,3):
country = random.choice(self.countries)
self.s.append(country['de'])
self.s.append(self.countryname[0]['de'])
random.shuffle(self.s)
self.btndict = {}
for i in range(4):
self.btndict[self.s[i]] = Button(self, text=self.s[i], height=2, width=35, font=('default', 20), command=lambda j=self.s[i]: self.check_input(j))
self.btndict[self.s[i]].pack()
def check_input(self, d):
if d != self.countryname[0]['de']:
print("Falsch")
else:
self.score += 5
for widget in self.winfo_children():
widget.destroy()
self.create_widgets()
def load_lookup(self):
with open('lookup.json') as file:
self.obj = file.read()
self.countryobj = json.loads(self.obj)
return self.countryobj
# def start(self):
# self.mainloop()
After clicking the close button (the default button on windows/osx to close a window) the window from my easy mode app disappears but PyCharm says that my program is still running.
I made some investigations and removed the self.withdraw() function in the function play_easy(self) in my mainmenu script. So now the mainmenu is still open after I click on the easy mode button. If I'm closing both windows now, the program fully ends.
Replacing self.withdraw() with self.destroy() is not working. The main menu is closed, but a new empty window opens instead.
Any suggestions on how to handle this problem so that my program fully ends if I click the close button within the easy/hard mode window?
You have two windows - main window created with Tk and subwindow created with Toplevel. When you use close button to close main window then it should also close all subwindows but when you close subwindow then it doesn't close main window (parent window) but only own subwindows - because usually it can be useful to display again main window to select other options and open again subwindow.
One of the methods is to destroy first window and use Tk to create new window.
But in this method you can't use some button in second window to go back to first window - and sometimes it can be problem. Even if you create again first window then it will not remeber previous values (if you have some Entry or other widgets to set values)
# from tkinter import * # PEP8: `import *` is not preferred`
import tkinter as tk
class EasyApp(tk.Tk): # use `Tk` instead of `Toplevel`
def __init__(self):
super().__init__()
self.scoretext = tk.Label(self, text="EasyApp")
self.scoretext.pack()
#def start(self):
# self.mainloop()
class TitleScreen(tk.Tk):
def __init__(self):
super().__init__()
self.button = tk.Button(self, text="Easy Mode", command=self.play_easy)
self.button.pack()
def play_easy(self):
self.destroy() # destroy current window
self.app = EasyApp()
def start(self):
self.mainloop()
TitleScreen().start()
Other method is to use self.wm_protocol("WM_DELETE_WINDOW", self.on_close) to execute function on_close when you use close button and in this function destroy main window (master).
This way you can still use Button to go back to main window which will remember previous content.
# from tkinter import * # PEP8: `import *` is not preferred`
import tkinter as tk
class EasyApp(tk.Toplevel): # still use `Toplevel`
def __init__(self, master): # send main window as master/parent
super().__init__(master) # it will also set `self.master = master`
self.scoretext = tk.Label(self, text="EasyApp")
self.scoretext.pack()
self.button = tk.Button(self, text="Go Back", command=self.go_back)
self.button.pack()
# run `on_close` when used `close button`
#self.protocol("WM_DELETE_WINDOW", self.on_close)
self.wm_protocol("WM_DELETE_WINDOW", self.on_close)
def go_back(self):
self.destroy() # destroy only current window
self.master.deiconify() # show again main window
def on_close(self):
self.destroy() # destroy current window
self.master.destroy() # destroy main window
class TitleScreen(tk.Tk):
def __init__(self):
super().__init__()
self.entry = tk.Entry(self)
self.entry.pack()
self.entry.insert('end', "You can change text")
self.button = tk.Button(self, text="Easy Mode", command=self.play_easy)
self.button.pack()
def play_easy(self):
self.withdraw()
self.app = EasyApp(self) # send main window as argument
def start(self):
self.mainloop()
TitleScreen().start()
Here's a fairly simple architecture for doing what you want. An application class is derived from Tk() which hides the default "root" window it normally displays and all the windows it does display are subclasses of a custom Toplevel subclass I've named BaseWin.
This class is just a Toplevel with its protocol for being delete (closed) set to call an a method named on_close(). This additional method simply destroys the current window before quits the application's mainloop() causing it to terminate.
The first window—an instance of the TitleScreen class—is displayed automatically when an instance of the application class is created. This window has two Buttons one labelled Easy Mode and the other Hard Mode. When one of them is clicked, an instance of the appropriate Toplevel subclass is created after the current window is removed by it call its destroy() method.
mainmenu.py
import tkinter as tk
from tkinter.constants import *
from tkinter import font as tkfont
from basewin import BaseWin
from easymode import EasyApp
from hardmode import HardApp
class SampleApp(tk.Tk):
def __init__(self):
super().__init__()
self.title('Flag Quiz')
self.geometry('600x600')
self.resizable(FALSE, FALSE)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold")
self.withdraw() # Hide default root Tk window.
startpage = TitleScreen(self.master)
self.mainloop()
class TitleScreen(BaseWin):
def __init__(self, master):
super().__init__(master)
self.make_widgets()
def make_widgets(self):
label = tk.Label(self, text="This is the Start Page", font=self.master.title_font)
label.pack(side="top", fill="x", pady=10)
self.easy = tk.Button(self, text="Easy Mode", font=('default', 20),
command=self.play_easy)
self.hard = tk.Button(self, text="Easy Mode", font=('default', 20),
command=self.play_hard)
self.easy.pack()
self.hard.pack()
def play_easy(self):
self.destroy()
self.app = EasyApp(self.master)
def play_hard(self):
self.destroy()
self.app = HardApp(self.master)
if __name__ == '__main__':
SampleApp()
basewin.py
import tkinter as tk
from tkinter.constants import *
class BaseWin(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.protocol("WM_DELETE_WINDOW", self.on_close)
def on_close(self):
self.destroy() # Destroy current window
self.master.quit() # Quit app.
easymode.py
import tkinter as tk
from tkinter.constants import *
from basewin import BaseWin
class EasyApp(BaseWin):
def __init__(self, master):
super().__init__(master)
self.title('Flag Quiz')
self.resizable(FALSE, FALSE)
self.make_widgets()
def make_widgets(self):
label = tk.Label(self, text="This is the Easy App", font=self.master.title_font)
label.pack(side="top", fill="x", pady=10)
hardmode.py
import tkinter as tk
from tkinter.constants import *
from basewin import BaseWin
class HardApp(BaseWin):
def __init__(self, master):
super().__init__(master)
self.title('Flag Quiz')
self.resizable(FALSE, FALSE)
self.make_widgets()
def make_widgets(self):
label = tk.Label(self, text="This is the Hard App", font=self.master.title_font)
label.pack(side="top", fill="x", pady=10)
I am using the following code, I am having 2 issues,
1. When I try to maximize the window, the Entry widget is not aligned/maximized along with main window and see a gap between scrolled text and Entry widget.
2. Second I am trying to set my cursor in Entry widget when I try to open or whenever my app is active but it's not working for some reason. Any Idea what am I making mistakes?
import tkinter as tk
from tkinter import scrolledtext
class Main:
def __init__(self, master):
self.master = master
master.title("Main")
width = master.winfo_screenwidth()
height = master.winfo_screenheight()
master.minsize(width=1066, height=766)
master.maxsize(width=width, height=height)
self.frame = tk.Frame(self.master)
text_area = scrolledtext.ScrolledText(self.master,width=75,height=35)
text_area.pack(side="top",fill='both',expand=True)
text_entry = tk.Entry(self.master,width=65)
text_entry.pack(side="top",fill=X, expand=True,ipady=3, ipadx=3)
text_entry.configure(foreground="blue",font=('Arial', 10, 'bold', 'italic'))
text_entry.focus()
self.frame.pack()
def initial(self):
print ("initializing")
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == '__main__':
main()
I can address the issue of your entry field not expanding properly.
That is because you have fill=X and this is not a valid input for fill. Instead use fill="x". I believe your 2nd issue with the entry field having a large gap is because you have set expand = True instead change that to expand = False.
That said I prefer to use the grid() geometry manager instead. Take a look at my below example of how to do this with grid and weights.
When using the grid() manager you can tell each widget exactly where you want it along a grid. The use of weights is for telling a row or column how much if any it should expand with the window. This combined with sticky="nsew" will help us control stuff expands within the window.
import tkinter as tk
from tkinter import scrolledtext
class Main(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Main")
width = self.winfo_screenwidth()
height = self.winfo_screenheight()
self.minsize(width=1066, height=766)
self.maxsize(width=width, height=height)
self.rowconfigure(0, weight=1)
self.rowconfigure(1, weight=0)
self.columnconfigure(0, weight=1)
text_area = scrolledtext.ScrolledText(self,width=75,height=35)
text_area.grid(row=0, column=0, ipady=3, ipadx=3, sticky="nsew")
text_entry = tk.Entry(self,width=65)
text_entry.grid(row=1, column=0, ipady=3, ipadx=3, sticky="ew")
text_entry.configure(foreground="blue",font=('Arial', 10, 'bold', 'italic'))
text_entry.focus()
def initial(self):
print ("initializing")
if __name__ == '__main__':
root = Main()
root.mainloop()
Update:
To clarify on your issue with fill and expand I have updated your code with the correction so you can see it working.
import tkinter as tk
from tkinter import scrolledtext
class Main:
def __init__(self, master):
self.master = master
master.title("Main")
width = master.winfo_screenwidth()
height = master.winfo_screenheight()
master.minsize(width=1066, height=766)
master.maxsize(width=width, height=height)
self.frame = tk.Frame(self.master)
text_area = scrolledtext.ScrolledText(self.master,width=75,height=35)
text_area.pack(side="top",fill='both',expand=True)
text_entry = tk.Entry(self.master,width=65)
text_entry.pack(side="top",fill="x", expand=False, ipady=3, ipadx=3)
text_entry.configure(foreground="blue",font=('Arial', 10, 'bold', 'italic'))
text_entry.focus()
self.frame.pack()
def initial(self):
print ("initializing")
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == '__main__':
main()
I want to be able to adjust the color of a ttk.Frame() widget. I know this question has been asked numerous times here. A very useful answer was given here: Change color of "tab header" in ttk.Notebook. One can do this using style.theme_create().
I want to make a similar adjustment using ttk.Style() but cannot figure out how. Could someone explain to me how I can do this?
The reason I am not using the method proposed in the above link is because there are other widgets in my code that I want to (and can) customize using ttk.Style().
import tkinter as tk
from tkinter import ttk
class Window():
def __init__(self, parent):
self.parent = parent
self.parent.minsize(width=600, height=400)
self.widgets()
def widgets(self):
s1 = ttk.Style()
s1.configure('My.TNotebook.Tab', padding=5, background='red')
s1.map('My.TNotebook.Tab', background=[('selected', 'green')])
self.nb1 = ttk.Notebook(self.parent, style='My.TNotebook.Tab')
self.tab1 = ttk.Frame(self.nb1)
self.tab2 = ttk.Frame(self.nb1)
self.tab3 = ttk.Frame(self.nb1)
self.nb1.add(self.tab1, text='Tab1')
self.nb1.add(self.tab2, text='Tab2')
self.nb1.add(self.tab3, text='Tab3')
self.nb1.place(relx=0.1, rely=0.1, width=500, height=200)
self.b1 = ttk.Button(self.parent, text='Quit', command=self.quit)
self.b1.place(relx=0.4, rely=0.7, height=70, width=150)
def quit(self):
self.parent.destroy()
root = tk.Tk()
app = Window(root)
root.mainloop()
I searched more and found an interesting and simple solution here: http://page.sourceforge.net/html/themes.html. The key to solving the problem seems to be the need to tell Python which theme to use (I am running Python on Windows). In my answer, I use classic but winnative, clam, alt, and default will do too. Using vista or xpnative makes no effect on the tabs' colors.
import tkinter as tk
from tkinter import ttk
class Window():
def __init__(self, parent):
self.parent = parent
self.parent.minsize(width=600, height=400)
self.widgets()
def widgets(self):
s1 = ttk.Style()
s1.theme_use('classic')
s1.configure('TNotebook.Tab', background='navajo white')
s1.map('TNotebook.Tab', background=[('selected', 'goldenrod'), ('active', 'goldenrod')])
self.nb1 = ttk.Notebook(self.parent)
self.nb1.place(relx=0.1, rely=0.1, width=500, height=200)
self.tab1 = ttk.Frame(self.nb1)
self.tab2 = ttk.Frame(self.nb1)
self.tab3 = ttk.Frame(self.nb1)
self.nb1.add(self.tab1, text='Tab1')
self.nb1.add(self.tab2, text='Tab2')
self.nb1.add(self.tab3, text='Tab3')
self.b1 = ttk.Button(self.parent, text='Quit', command=self.quit)
self.b1.place(relx=0.4, rely=0.7, height=70, width=150)
def quit(self):
self.parent.destroy()
root = tk.Tk()
app = Window(root)
root.mainloop()
I have the following code. This creates a dialog box just fine.
import tkinter as tk
import tkinter.ttk as ttk
class Window(ttk.Frame):
def __init__(self, master=None):
super().__init__(master, padding=2) # Creates self.master
helloLabel = ttk.Label(self, text="Hello Tkinter!")
quitButton = ttk.Button(self, text="Quit", command=self.quit)
helloLabel.pack()
quitButton.pack()
self.pack()
#self.create_widgets()
#self.create_layout()
# def create_widgets(self):
# pass
# def create_layout(self):
# pass
application = tk.Tk()
application.title("Window")
Window(application)
application.mainloop()
How come when I uncomment the following two lines in the above code the code breaks?
def create_widgets(self):
pass
I'm using python 3.4 32 bit on Windows 7 64-bit.
I want to create a GUI program base on tkinter. One of the widgets is Text. I want to add a horizontal scrollbar in it, but it didn't work.
Where did I make a mistake?
from Tkinter import *
import tkFont
class DpWin(object):
def run(self):
root=Tk()
root.geometry('768x612')
title='dp'
root.title(title)
xscrollbar = Scrollbar(root, orient=HORIZONTAL)
xscrollbar.pack(side=BOTTOM, fill=X)
yscrollbar = Scrollbar(root)
yscrollbar.pack(side=RIGHT, fill=Y)
text = Text(root,xscrollcommand=xscrollbar.set,yscrollcommand=yscrollbar.set)
text.pack()
xscrollbar.config(command=text.xview)
yscrollbar.config(command=text.yview)
text.insert(END,'a'*999)
mainloop()
def start(self):
self.b_start.config(state=DISABLED)
self.b_stop.config(state=ACTIVE)
def stop(self):
self.b_stop.config(state=DISABLED)
self.b_start.config(state=ACTIVE)
if __name__=='__main__':
win=DpWin()
win.run()
I've modified your code according to here. There are 2 main differences.
I made it so the textbox doesn't wrap. If you wrap text, there is nothing for the horizontal scrollbar to scroll to.
I used the grid geometry manager on a frame to keep the scrollbars and text widgets together. The advantage to using .grid is that you actually get scrollbars which are the correct width/height (something you can't achieve with pack).
...
from Tkinter import *
import tkFont
class DpWin(object):
def run(self):
root=Tk()
root.geometry('768x612')
title='dp'
root.title(title)
f = Frame(root)
f.pack()
xscrollbar = Scrollbar(f, orient=HORIZONTAL)
xscrollbar.grid(row=1, column=0, sticky=N+S+E+W)
yscrollbar = Scrollbar(f)
yscrollbar.grid(row=0, column=1, sticky=N+S+E+W)
text = Text(f, wrap=NONE,
xscrollcommand=xscrollbar.set,
yscrollcommand=yscrollbar.set)
text.grid(row=0, column=0)
xscrollbar.config(command=text.xview)
yscrollbar.config(command=text.yview)
text.insert(END, 'a'*999)
mainloop()
def start(self):
self.b_start.config(state=DISABLED)
self.b_stop.config(state=ACTIVE)
def stop(self):
self.b_stop.config(state=DISABLED)
self.b_start.config(state=ACTIVE)
if __name__=='__main__':
win=DpWin()
win.run()
There is one comment regarding making both x and y scrollbars work within the pack framework. Here is a minimal example:
import tkinter as tk
from tkinter import X, Y, BOTTOM, RIGHT, LEFT, Y, HORIZONTAL
class TextExample(tk.Frame):
def __init__(self, master=None):
super().__init__()
sy = tk.Scrollbar(self)
sx = tk.Scrollbar(self, orient=HORIZONTAL)
editor = tk.Text(self, height=500, width=300, wrap='none')
sx.pack(side=BOTTOM, fill=X)
sy.pack(side=RIGHT, fill=Y)
editor.pack(side=LEFT, fill=Y)
sy.config(command=editor.yview)
sx.config(command=editor.xview)
self.pack()
def main():
root = tk.Tk()
root.geometry("800x500+0+0")
app = TextExample(master=root)
root.mainloop()
if __name__ == '__main__':
main()