how to disable a second window? - python

So I'm just trying to create a window with a shadow. I want to move both windows at the same time. so far it works. however, when i click on the edge, the "shadow" window comes to the fore. is there a possibility to leave the second window permanently in the background?
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
class CFrame(Tk):
def __init__(self, parent=None, bg="#1b1e21", bd=1, bdcolor="lime"):
super().__init__(parent)
self.bg = bg
self.bd = bd
self.bdcolor = bdcolor
self.title("CFrame")
self.geometry("740x740+200+200")
self.overrideredirect(1)
self.bind("<B1-Motion>", self.dragWindow)
self.bind("<Button-1>", self.clickWindow)
self.shadow = Shadow()
self.headingFrame = Frame(self, bg="lime")
self.headingFrame.pack(fill="x")
self.mainFrame = Frame(self, bg=self.bg, highlightthickness=bd,
highlightbackground=self.bdcolor, highlightcolor=self.bdcolor)
self.mainFrame.pack(fill="both", expand="yes")
self.exitButton = ttk.Button(self.headingFrame, text="\u2613", command=self.programQuit)
self.exitButton.pack(side="right")
self.offSetX = 0
self.offSetY = 0
def dragWindow(self, event):
x = self.winfo_pointerx() - self.offSetX
y = self.winfo_pointery() - self.offSetY
x2 = self.winfo_pointerx() - self.offSetX - 10
y2 = self.winfo_pointery() - self.offSetY - 10
self.geometry("+%d+%d" % (x,y))
self.shadow.geometry("+%d+%d" % (x2,y2))
def clickWindow(self, event):
self.offSetX = event.widget.winfo_rootx() - self.winfo_rootx() + event.x
self.offSetY = event.widget.winfo_rooty() - self.winfo_rooty() + event.y
def programQuit(self):
ex = messagebox.askyesno("Quit?", "Quit?")
if ex == 1:
self.destroy()
class Shadow(Tk):
def __init__(self):
super().__init__()
self.title("Shadow")
self.geometry("760x760+190+190")
self.overrideredirect(1)
self.attributes("-alpha", 0.3)
self.bg = Frame(self, bg="black")
self.bg.pack(fill="both", expand="yes")
if __name__ == '__main__':
cf = CFrame()
cf.mainloop()
maybe a little bit too much code, but i think you can it imagn better with the whole example

I already did something like this here. It works by binding to "<FocusIn>" on your dummy shadow window and calling .focus_force() on the main window. Like this:
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class CFrame(tk.Tk):
def __init__(self, bg="#1b1e21", bd=1, bdcolor="lime"):
super().__init__()
self.bg = bg
self.bd = bd
self.bdcolor = bdcolor
super().geometry("740x740+200+200")
super().overrideredirect(True)
super().bind("<B1-Motion>", self.drag_window)
super().bind("<Button-1>", self.click_window)
self.shadow = Shadow()
self.heading_frame = tk.Frame(self, bg="lime")
self.heading_frame.pack(fill="x")
self.main_frame = tk.Frame(self, bg=self.bg, highlightthickness=bd,
highlightbackground=self.bdcolor,
highlightcolor=self.bdcolor)
self.main_frame.pack(fill="both", expand="yes")
self.exit_button = ttk.Button(self.heading_frame, text="\u2613",
command=self.program_quit)
self.exit_button.pack(side="right")
self.offset_x = 0
self.offset_y = 0
self.shadow.bind("<FocusIn>", self.focus_main)
def focus_main(self, event):
super().focus_force()
def drag_window(self, event):
x = self.winfo_pointerx() - self.offset_x
y = self.winfo_pointery() - self.offset_y
x2 = self.winfo_pointerx() - self.offset_x - 10
y2 = self.winfo_pointery() - self.offset_y - 10
super().geometry("+%d+%d" % (x,y))
self.shadow.geometry("+%d+%d" % (x2,y2))
def click_window(self, event):
self.offset_x = event.widget.winfo_rootx() - self.winfo_rootx() + event.x
self.offset_y = event.widget.winfo_rooty() - self.winfo_rooty() + event.y
def program_quit(self):
result = messagebox.askyesno("Quit?", "Are you sure you want to quit?")
if result:
super().destroy()
self.shadow.destroy()
class Shadow(tk.Tk):
def __init__(self):
super().__init__()
super().geometry("760x760+190+190")
super().overrideredirect(True)
super().attributes("-alpha", 0.3)
super().config(bg="black")
if __name__ == "__main__":
cf = CFrame()
cf.mainloop()
Also I removed the frame from the shadow window and replaced it with <tkinter.Tk>.config(bg="black").
Another thing: Is there a point to calling .title(...) when you are going to use .overrideredirect(True)?

Related

How to save drawing on canvas as png file (linux)?

I am creating a painting application and I want to save my drawing on canvas widget as png file on my computer. This is my code:
from tkinter import *
from tkinter.filedialog import *
from functools import partial
from tkinter import Menu
from tkinter import filedialog,messagebox
from PIL import Image
from tkinter.colorchooser import askcolor
import pyscreenshot as ImageGrab
import pyautogui
class PaintingApp:
x=y=None
def __init__(self,window):
self.window = window
self.upper_frame = Frame(window)
self.upper_frame.grid(row=0,column=0, padx=10, pady=5,sticky="ew")
self.lower_frame = Frame(window)
self.lower_frame.grid(row=2, column=0, padx=10, pady=5,sticky="ew")
self.canvas= Canvas(self.lower_frame,width=500,height=530,bg="white")
self.canvas.grid()
self.objects = [] #objects on canvas
self.pen_size = 2
self.pcolor = "black"
self.pen = Button(self.upper_frame,text="Pen",command=partial(self.pen_draw,thickness=self.pen_size))
self.pen.grid(row=0,column=3,padx=(10,160))
self.bg = Button(self.upper_frame,text="Background",command= self.bgcolor) #change bg color
self.bg.grid(row=2,column=1,padx=(100,10))
self.upper_menu()
self.canvas.bind("<Button-1>", self.get_x_and_y)
self.canvas.bind("<B1-Motion>", lambda event, b=self.pen_size: self.pen_draw(b,event))
self.im = None
def save_pic(self,event=None):
file = asksaveasfilename(defaultextension=".png")
x = self.canvas.winfo_rootx() + self.canvas.winfo_x()
y = self.canvas.winfo_rooty() + self.canvas.winfo_y()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
self.im=ImageGrab.grab(bbox=(x,y,x1,y1))
self.im.save(file[19:])
def pen_color(self,color):
self.pcolor= color
def get_x_and_y(self,event):
global x,y
x, y = event.x, event.y
def pen_draw(self,thickness,event=None):
global x,y
self.canvas.bind("<Button-1>", self.get_x_and_y) # Bind to pen_draw function
self.canvas.bind("<B1-Motion>", lambda event, b=self.pen_size: self.pen_draw(b,event))
if event != None:
self.objects.append(self.canvas.create_line((x, y, event.x, event.y), fill=self.pcolor,width=self.pen_size,capstyle=ROUND,smooth=True))
x, y = event.x, event.y
def upper_menu(self):
self.menubar = Menu(window)
self.menu1 = Menu(self.menubar, tearoff=0)
self.menu1.add_command(label="Save pic", command=self.save_pic)
self.menu1.add_separator()
self.menu1.add_command(label="Exit", command=window.destroy)
self.menubar.add_cascade(label="Settings", menu=self.menu1)
self.menu2 = Menu(self.menubar, tearoff=0)
self.menu2.add_command(label="Open pic")
self.menubar.add_cascade(label="Image", menu=self.menu2)
self.window.config(menu=self.menubar)
def bgcolor(self):
chosen_color = askcolor(color=self.canvas["bg"])[1]
self.canvas.configure(bg=chosen_color)
window = Tk()
window.geometry("500x450")
p = PaintingApp(window)
window.mainloop()
Now I have tried many many codes but it won't work. The code I provided above saves an all black picture which does not make any sense. I have tried using the module pyautogui as well but I still get the same result.
def save_pic(self,event=None):
file = asksaveasfilename(defaultextension=".png")
x = self.canvas.winfo_rootx() + self.canvas.winfo_x()
y = self.canvas.winfo_rooty() + self.canvas.winfo_y()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
self.im=pyautogui.screenshot(region=(x,y,x1,y1))
self.im.save(file[19:])
If you use a screen shot library, you should wait until the backend UI framework(X11 in your case) finishes the drawing. Also you can use the PIL.ImageGrab.grab() in Pillow instead of pyscreenshot.
So do like this.(I fixed several errors in the original example, such as an incorrect path.)
...
from PIL import ImageGrab
...
class PaintingApp:
...
def save_pic(self,event=None):
file = asksaveasfilename(defaultextension=".png")
def grab_and_save():
x = self.canvas.winfo_rootx()
y = self.canvas.winfo_rooty()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
self.im = ImageGrab.grab(bbox=(x,y,x1,y1))
self.im.save(file)
self.window.update()
self.window.after(1000, grab_and_save) # This waits 1000ms.
...
...
A better method will be to install Tkimg and export the bitmap on the canvas directly, but it will need some hard work. You can start with an unmaintained project, python-tkimg.

Tk can't be master of Toplevel

Classes.py
from tkinter import *
BGCL = '#000000'
CANCELCL = '#800000'
CANCELHOVCL = '#400000'
INFOCL = '#000080'
INFOHOVCL = '#000040'
BARCL = '#004000'
STROKECL = '#00c000'
class FantomForm(Tk):
def __init__(self):
Tk.__init__(self)
self.withdraw()
class MainForm(Tk):
def __init__(self):
Tk.__init__(self)
self['bg'] = BGCL
self.overrideredirect(True)
self.bar = Frame(self,bg = BARCL)
self.bar.place(x = 0 ,y = 0,relwidth = 1,height = 24)
self.closebtn = Button(self,bg = CANCELCL,fg = BGCL,relief = FLAT,command = self.Exit,bd=0,activebackground = BGCL)
#print(self.winfo_reqwidth())
self.closebtn.place(width =24,height = 24,x = self.winfo_reqwidth()-24)
self.bar.bind("<ButtonPress-1>", self.StartMove)
self.bar.bind("<ButtonRelease-1>", self.StopMove)
self.bar.bind("<B1-Motion>", self.OnMotion)
self.closebtn.bind("<Enter>",self.__closebtne)
self.closebtn.bind("<Leave>",self.__closebtnl)
def __closebtne(self,event = None):
self.closebtn['bg'] = CANCELHOVCL
def __closebtnl(self,event = None):
self.closebtn['bg'] = CANCELCL
def StartMove(self, event = None):
self.dragx = event.x
self.dragy = event.y
def StopMove(self, event = None):
self.dragx = None
self.dragy = None
def OnMotion(self, event = None):
deltax = event.x - self.dragx
deltay = event.y - self.dragy
x = self.winfo_x() + deltax
y = self.winfo_y() + deltay
self.geometry("+%s+%s" % (x, y))
def Exit(self,event = None):
self.destroy()
class Form(Toplevel):
def __init__(self,master = None):
Toplevel.__init__(self,master)
self['bg'] = BGCL
self.overrideredirect(True)
self.bar = Frame(self,bg = BARCL)
self.bar.place(x = 0 ,y = 0,relwidth = 1,height = 24)
self.closebtn = Button(self,bg = CANCELCL,fg = BGCL,relief = FLAT,command = self.Exit,bd=0,activebackground = BGCL)
#print(self.winfo_reqwidth())
self.closebtn.place(width =24,height = 24,x = self.winfo_reqwidth()-24)
self.bar.bind("<ButtonPress-1>", self.StartMove)
self.bar.bind("<ButtonRelease-1>", self.StopMove)
self.bar.bind("<B1-Motion>", self.OnMotion)
self.closebtn.bind("<Enter>",self.__closebtne)
self.closebtn.bind("<Leave>",self.__closebtnl)
def __closebtne(self,event = None):
self.closebtn['bg'] = CANCELHOVCL
def __closebtnl(self,event = None):
self.closebtn['bg'] = CANCELCL
def StartMove(self, event = None):
self.dragx = event.x
self.dragy = event.y
def StopMove(self, event = None):
self.dragx = None
self.dragy = None
def OnMotion(self, event = None):
deltax = event.x - self.dragx
deltay = event.y - self.dragy
x = self.winfo_x() + deltax
y = self.winfo_y() + deltay
self.geometry("+%s+%s" % (x, y))
def Exit(self,event = None):
self.destroy()
Main.py
from Classes import *
Form0 = MainForm()
Form1 = Form(MainForm)
Form0.mainloop()
Form1.mainloop()
MainForm is class based on Tk
Form is class based on Toplevel
The master for a widget must be another widget. You're passing a class, and a class isn't a widget. You can use an instance of the class, however.
Instead of this:
Form0 = MainForm()
Form1 = Form(MainForm)
do this:
Form0 = MainForm()
Form1 = Form(Form0)
Also, you should only call mainloop once. You shouldn't call it for every window.

Python How to clear window in tkinter

I need to clear window before I load a new map. But menu buttons have to stay. I was trying self.frame.destroy(), but it kill my menu buttons too. Basically I need to clear class Sokoban, but this class is not in class Window. Problem is in def map1(self): and def map2(self):
import tkinter
from tkinter import *
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title('Sokoban')
self.pack(fill = BOTH, expand = 1)
menu = Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
file.add_command(label='Mapa1', command = self.map1)
menu.add_cascade(label='Mapy', menu=file)
edit = Menu(menu)
edit.add_command(label='Mapa2', command = self.map2)
menu.add_cascade(label='Edit', menu=edit)
def map1(self):
self.frame.forget()
Sokoban('map1.txt')
def map2(self):
self.frame.forget()
Sokoban('map2.txt')
class Sokoban:
def __init__(self, subor):
t = open(subor, 'r')
self.pole = [None] * 8
self.box = []
for a in range(8):
self.pole[a] = list(t.readline().strip())
for b in range(len(self.pole[a])):
if self.pole[a][b] == '$':
self.player = Player(a,b)
self.pole[a][b] = '.'
if self.pole[a][b] == '#':
self.box.append(Box(a,b))
self.pole[a][b] = '.'
t.close()
self.game = self.g = tkinter.Canvas(bg = 'white', width = 650, height = 240,
cursor = 'cross')
self.g.pack()
self.farba = [['gray'],['khaki'],['#FF4500'],['yellow'],['brown']]
stena = '*'
policko = '.'
player = '$'
box = '#'
ciel = '+'
## self.newButt = self.g.create_rectangle(511, 0, 650, 45, fill = 'red')
## self.newButtText = self.g.create_text(580.5, 22.5, text = 'New Game',
## font = 'arial 19 italic bold',
## fill = 'yellow')
## self.map1Butt = self.g.create_rectangle(511, 46, 650, 86, fill = 'red')
## self.map1ButtText = self.g.create_text(580.5, 66, text = 'Map - 1',
## font = 'arial 19 italic bold',
## fill = 'yellow')
## self.map2Butt = self.g.create_rectangle(511, 87, 650, 127, fill = 'red')
## self.map2ButtText = self.g.create_text(580.5, 109, text = 'Map - 2',
## font = 'arial 19 italic bold',
## fill = 'yellow')
## self.map3Butt = self.g.create_rectangle(511, 128, 650, 168, fill = 'red')
## self.map3ButtText = self.g.create_text(580.5, 148, text = 'Map - 3',
## font = 'arial 19 italic bold',
## fill = 'yellow')
self.kresli()
self.g.bind_all('<Up>', self.up)
self.g.bind_all('<Down>', self.down)
self.g.bind_all('<Left>', self.left)
self.g.bind_all('<Right>', self.right)
## self.g.bind('<1>', self.Buttons)
def up(self, e):
self.move(-1, 0)
def down(self, e):
self.move(1, 0)
def left(self, e):
self.move(0, -1)
def right(self, e):
self.move(0, 1)
## def newGame(self):
## self.game.pack_forget()
## Sokoban()
## def map1(self):
## self.game.pack_forget()
## Sokoban('map1.txt')
##
## def map2(self):
## self.game.pack_forget()
## Sokoban('map2.txt')
## def Buttons(self, e):
## if '<1>' and self.newButt:
## self.newGame()
## if '<1>' and self.map1Butt:
## self.map1()
##
## if '<1>' and self.map2Butt:
## self.map2()
def kresli(self):
for a in range(8):
for b in range(17):
x,y = b*30, a*30
f = self.farba['*.+'.index(self.pole[a][b])]
self.g.create_rectangle(x,y,x+30,y+30,fill=f)
self.player.kresli(self.g, self.farba[3])
for box in self.box:
box.kresli(self.g, self.farba[4])
def setBox(self, r, s):
for a in range(len(self.box)):
if self.box[a].r==r and self.box[a].s==s:
return a
return -1
def move(self, dr, ds):
r = self.player.r + dr
s = self.player.s + ds
if r<0 or r>=8 or s<0 or s>=510 or self.pole[r][s] == '*':
return
c = self.setBox(r, s)
if c >= 0:
rr = r + dr
ss = s + ds
if rr < 0 or rr>=8 or ss<0 or ss>=510 or self.pole[rr][ss] == '*':
return
if self.setBox(rr, ss) >= 0:
return
self.box[c].move(dr, ds)
self.player.move(dr, ds)
for c in self.box:
if self.pole[c.r][c.s] != '+':
return
class Player:
def __init__(self, r, s):
self.r = r
self.s = s
def kresli(self, g, farba):
self.g = g
x,y = self.s*30, self.r*30
self.plr = tkinter.PhotoImage(file='player.png')
self.id1 = g.create_image(x+15, y+15, image = self.plr)
def move(self, dr, ds):
self.r = self.r + dr
self.s = self.s + ds
self.g.move(self.id1, ds*30, dr*30)
class Box:
def __init__(self, r, s):
self.r = r
self.s = s
def kresli(self, g, farba):
self.g = g
x,y = self.s*30, self.r*30
self.bx = tkinter.PhotoImage(file='box.png')
self.id2 = g.create_image(x+15, y+15, image = self.bx)
def move(self, dr, ds):
self.r = self.r + dr
self.s = self.s + ds
self.g.move(self.id2, ds*30, dr*30)
root = Tk()
root.geometry('510x240')
app = Window(root)
root.mainloop()
Sokoban()
That not-working wall of code is very hard to work with so I will try to answer your menu buttons have to stay part with an example code.
Basically you create two frames, one holds widgets going to be cleared and second one holds other widgets plus first frame.
import tkinter as tk
root = tk.Tk()
root.geometry("300x300")
frame1 = tk.Frame(root)
frame2 = tk.Frame(frame1)
frame1.pack()
frame2.pack()
menubar = tk.Menu(frame1)
menubar.add_command(label="Bring Back!", command=frame2.pack)
menubar.add_command(label="Quit!", command=root.destroy)
tk.Button(frame2, text="Forget only frame2", command=frame2.pack_forget).pack()
tk.Label(frame2, text="Label on frame2").pack()
root.config(menu=menubar)
root.mainloop()

Python Tkinter Custom Window

I have this simple Tkinter Custom Window. I am a beginner and only learnt tkinter a few months ago. I have no experience in real software development. So, I would like to know if the way it was coded is acceptable? I know that when i say acceptable , it could mean a lot of things. I just want to know what are the things i should improve in my coding style & the way i think?
import Tkinter as tk
''' Creating Tkinter Tk instance '''
class Application(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
self.bind("<ButtonPress-1>", self.StartMove)
self.bind("<ButtonRelease-1>", self.StopMove)
self.bind("<B1-Motion>", self.OnMotion)
self.Init()
self.Layout()
self.AddButtons()
''' Setting Main Tk window size & styles '''
def Init(self):
self.geometry("1280x700+0+0")
self.overrideredirect(True)
self['background'] = '#201F29'
self['highlightthickness'] = 2
self['relief'] = 'groove'
'''Layout of the Tk window'''
def Layout(self):
self.exitmenu = tk.Frame(self)
self.exitmenu.place(x = 1217, y = 0)
self.container = tk.Frame(self,width = 1268,height = 648 , relief = 'flat',bd = 0)
self.container.place(x = 5,y = 40)
''' Adding Exit button and Minimize button to the Tk window'''
def AddButtons(self):
self.minibutton = tk.Button(self.exitmenu,text = '0',font=('webdings',8,'bold'),relief = 'flat' , command = self.minimize )
self.minibutton.pack(side = 'left')
self.exitbutton = tk.Button(self.exitmenu,text = 'r',font=('webdings',8),relief = 'flat' ,bg = '#DB6B5A', command = self.destroy )
self.exitbutton.pack(side = 'left')
def minimize(self):
self.overrideredirect(False)
self.wm_state('iconic')
self.overrideredirect(True)
'''Methods for moving window frame'''
def StartMove(self, event):
self.x = event.x
self.y = event.y
def StopMove(self, event):
self.x = None
self.y = None
def OnMotion(self, event):
x1 = self.x
y1 = self.y
x2 = event.x
y2 = event.y
deltax = x2 - x1
deltay = y2 - y1
a = self.winfo_x() + deltax
b = self.winfo_y() + deltay
self.geometry("+%s+%s" % (a, b))
def Main():
app = Application()
app.mainloop()
if __name__ == "__main__":
Main()
Read PEP-8 Install and run one or all of PEP8 checker, pyFlakes, pyChecker, pylint.
The first thing that stands out is that docstrings are supposed to be within the function rather than before it - then they become a part of the function code and can be accessed by help.

Using Python Tkinter: Always on top window isn't showing custom class tooltip text

I can't get tooltip to work with my always on top window. Obviously the problem is you can't create something on top of something that's always on top; so i was wondering if there was a workaround or solution. The Popup needs to be always on top of other windows, but i also need to have all the tooltips show up properly.
Here's a stripped down version of what I have so far:
from Tkinter import *
class GUI:
def __init__(self, root):
Popup = Toplevel(root)
Popup.resizable(0,0)
Popup.attributes("-toolwindow", 1)
Popup.wm_attributes("-topmost", 1)
PFrame = Frame(Popup)
self.B = Button(PFrame, width=10,height=10)
self.B.pack()
self.createToolTip(self.B,"Click this button.")
PFrame.pack()
class ToolTip(object):
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, text):
self.text = text
if self.tipwindow or not self.text: return
x,y,cx,cy = self.widget.bbox("insert")
x = x + self.widget.winfo_rootx() +15
y = y + cy + self.widget.winfo_rooty() +65
self.tipwindow = tw = Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d"%(x,y))
label = Label(tw, text=self.text, justify=LEFT)
label.pack(ipadx=1)
def hidetip(self):
tw = self.tipwindow
self.tipwindow = None
if tw: tw.destroy()
def createToolTip(self,widget,text):
toolTip = self.ToolTip(widget)
def enter(event): self.tt = root.after(1500,show,event)
def show(event): toolTip.showtip(text)
def leave(event):
if self.tt: root.after_cancel(self.tt)
toolTip.hidetip()
widget.bind('<Enter>', enter)
widget.bind('<Leave>', leave)
if __name__ == '__main__':
root = Tk()
App = GUI(root)
root.mainloop()
I fixed it by adding tw.wm_attributes("-topmost", 1) to the showtip function. Let me know if this solution is incorrect or if there is a better way.

Categories