from tkinter import Tk, messagebox, Label, Button, Canvas, NW
from PIL import Image, ImageTk
from tkinter.filedialog import askopenfilename
from tkinter.colorchooser import askcolor
import traceback, sys
import tkinter
import threading
from .utility import DebugUtility
from . import globals
from .utility import OSUtility
#from kivy.core.window import Window
"""
** INFORMATION **
*****************
The main application is in Kivy while you can use Tkinter GUI on the side. Because of known issues and conflicts
between Kivy and Tkinter, there can be only on tkinter.Tk() object in one session. This is why csengine provides
a handle to the Main Tkinter Object instantiated at the beginning of the app. This can be accessed by using
TkinterAPI.pI() function which maintains and returns the singleton Tkinter Object. It is very important to maintain
Tkinter Object as a singleton else the application can crash or produce unexpected results at any time.
To access the main tkinter Object, call TkinterAPI.pI(). This will return to you the TkinterObject used by the
application throughout.
If you want to use multiple windows use TkinterAPI.pI().TopLevel() object
DO NOT RUN tkinter's mainloop() function as it will cause conflicts between Kivy and Tkinter especially on MacOS.
To battle this, we are using Kivy.Clock Object to run every ..02 seconds running an update() on the tkinter.
The startCETK() method is called by Kivy on start of Kivy program, since Kivy needs to be taking hold of the main
application thread, and not Tkinter.
If you call destroy function of tkinter, then just be assured that the application will crash. The destroy function
can only be called only once, at the time of exiting the application.
"""
tkinterObject = None
from kivy.clock import Clock
[docs]
class TkinterAPI:
tkMaster = None
path = "" # The final Path from File Selection
fileTypes = [["All Files", "*.*"]] # Default FileTypes for File Chooser
color = None # The final color from the Color Chooser
EM_title = "Error" # Error Message Box Title Default
EM_Message = "An Error Occured!" # Error Message Box Message Default
TkkivyClock = None
aboutScreenVisible = False
# splashScrenVisible = False
KiVY_CLK_INTV_UNFOCUSED = 0.05
KiVY_CLK_INTV_FOCUSED = 0.5
focus_last = True
[docs]
@staticmethod
def pI():
global tkinterObject
if not tkinterObject:
tkinterObject = TkinterAPI()
return tkinterObject
[docs]
def getMaster(self):
for widget in self.tkMaster.winfo_children():
widget.destroy()
return self.tkMaster
[docs]
def startCETK(self):
TkinterAPI.TkkivyClock = Clock.schedule_interval(self.runMainLoop, self.KiVY_CLK_INTV_FOCUSED)
[docs]
def stopCETK(self):
TkinterAPI.TkkivyClock.cancel()
#############################################################################################
# File Dialog | UPDATED 9-April-2021
#############################################################################################
[docs]
@staticmethod
def selectFile(fileType):
imagePath= ""
try:
masterTk = TkinterAPI.pI().getMaster()
imagePath = askopenfilename(parent=masterTk, title="Open File", filetypes=fileType)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return imagePath
#############################################################################################
# Color Chooser Dialog | UPDATED 9-April-2021
#############################################################################################
[docs]
@staticmethod
def selectColor():
colorCode = ""
try:
masterTk = TkinterAPI.pI().getMaster()
colorCode = askcolor(parent=masterTk, title="Choose color")
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return colorCode
#############################################################################################
# Message Dialogs
#############################################################################################
## ERROR MESSAGE
[docs]
@staticmethod
def showErrorMessage(title="Error", message="An Error Occurred"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.showerror(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
#### WARNING MESSAGE
[docs]
@staticmethod
def showWarningMessage(title="Warning", message="Warning!"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.showwarning(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## INFO MESSAGE
[docs]
@staticmethod
def showInfoMessage(title="Information", message="This is just an Information"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.showinfo(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## QUESTION MESSAGE
[docs]
@staticmethod
def askQuestion(title="Information", message="This is just an Information"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.askquestion(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## OK CANCEL MESSAGE
[docs]
@staticmethod
def askOKCancel(title="Information", message="This is just an Information"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.askokcancel(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## YES NO MESSAGE
[docs]
@staticmethod
def askYesNo(title="Information", message="This is just an Information"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.askyesno(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## RETRY CANCEL MESSAGE
[docs]
@staticmethod
def askRetryCancel(title="Information", message="This is just an Information"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.askretrycancel(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## YES NO CANCEL MESSAGE
[docs]
@staticmethod
def askYesNoCancel(title="Information", message="This is just an Information"):
returnCode = None
try:
masterTk = TkinterAPI.pI().getMaster()
returnCode = messagebox.askyesnocancel(title=title, message=message)
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
return returnCode
## Splash Screens Class
[docs]
@staticmethod
def showSplashScreen():
try:
#create new Window
master = tkinter.Toplevel()
ws = master.winfo_screenwidth()
hs = master.winfo_screenheight()
imagePath = globals.SPLASH_SCREEN_IMAGE
im = Image.open(imagePath)
imgWidth, imgHeight = im.size
# fix geometry of master window
master.geometry(str(imgWidth) + "x" + str(imgHeight))
canvas = Canvas(master, width=imgWidth, height=imgHeight, bg='black', highlightthickness=0)
canvas.pack()
canvas.master.overrideredirect(True)
canvas.master.geometry('+' + str(int(ws/2 - imgWidth/2)) + '+' + str(int(hs/2- imgHeight/2)) + '')
#canvas.master.wm_attributes("-transparentcolor", "blue")
canvas.master.wm_attributes("-alpha", 1)
canvas.master.wm_attributes("-topmost", True)
canvas.master.lift()
img = ImageTk.PhotoImage(Image.open(imagePath))
# NOT WORKING FOR MAC - AS OF NOW
#canvas.create_image(0, 0, anchor=NW, image=img)
#canvas.config(bg='systemTransparent')
panel = tkinter.Label(canvas, image=img, width=imgWidth, height=imgHeight, bg="black")
if OSUtility.GetPlatformID() == "WIN":
master.wm_attributes("-transparentcolor", "blue")
# IMPORTANT - This is what makes the image visible on the label on MAC!!!
panel.image = img # keep a reference!
panel.pack(side="bottom", fill="both", expand="yes")
#destroy splash screen after timeout
master.after(globals.SPLASH_SCREEN_TIMEOUT, lambda: master.withdraw())
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
[docs]
def changeKivyClockSpeed(self, focused):
try:
TkinterAPI.TkkivyClock.cancel()
except:
pass
if focused:
TkinterAPI.TkkivyClock = Clock.schedule_interval(self.runMainLoop, self.KiVY_CLK_INTV_FOCUSED)
else:
TkinterAPI.TkkivyClock = Clock.schedule_interval(self.runMainLoop, self.KiVY_CLK_INTV_UNFOCUSED)
[docs]
def runMainLoop(self, x):
try:
if globals.WINDOW_OBJECT:
#DebugUtility.Log("Window Focus: " + str(globals.WINDOW_OBJECT.focus))
if self.focus_last != globals.WINDOW_OBJECT.focus:
self.focus_last = globals.WINDOW_OBJECT.focus
self.changeKivyClockSpeed(self.focus_last)
self.tkMaster.update()
except Exception as e:
DebugUtility.Err("Exception in Main Loop", e)
[docs]
@staticmethod
def showAboutScreen():
if TkinterAPI.aboutScreenVisible:
return
try:
# open new Window
master = tkinter.Toplevel()
imgPath = globals.ABOUT_SCREEN_IMAGE
ws = master.winfo_screenwidth()
hs = master.winfo_screenheight()
im = Image.open(imgPath)
imgWidth, imgHeight = im.size
# fix geometry of master window
master.geometry(str(imgWidth) + "x" + str(imgHeight))
canvas = Canvas(master, width=imgWidth, height=imgHeight, bg='white', highlightthickness=0)
canvas.pack()
canvas.master.overrideredirect(True)
canvas.master.geometry('+' + str(int(ws / 2 - imgWidth / 2)) + '+' + str(int(hs / 2 - imgHeight / 2)) + '')
# canvas.master.wm_attributes("-transparentcolor", "blue")
canvas.master.wm_attributes("-alpha", 1)
canvas.master.wm_attributes("-topmost", True)
canvas.master.lift()
img = ImageTk.PhotoImage(Image.open(imgPath))
# NOT WORKING FOR MAC - AS OF NOW
# canvas.create_image(0, 0, anchor=NW, image=im)
# The Label widget is a standard Tkinter widget used to display a text or image on the screen.
panel = tkinter.Label(canvas, image=img, width=imgWidth, height=imgHeight)
# Bind click events to be able to close about window
master.bind("<ButtonRelease>", lambda event: TkinterAPI.pI().closeAbout(master))
# IMPORTANT - This is what makes the image visible on the label on MAC!!!
panel.image = img # keep a reference!
panel.pack(side="bottom", fill="both", expand="yes")
TkinterAPI.aboutScreenVisible = True
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
traceback.print_exc(file=sys.stdout)
[docs]
@staticmethod
def closeAbout(master):
try:
master.withdraw()
master.destroy()
except Exception as e:
DebugUtility.Err(e, DebugUtility.InspectFrame())
TkinterAPI.aboutScreenVisible = False