Source code for csengine.tkinter

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