SorterWindow.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. #
  4. # SorterWindow.py
  5. #
  6. # GUI for calling the tensorflow based animal picture sorter.
  7. #
  8. # Copyright 2022 Stephen Stengel <stephen.stengel@cwu.edu> and friends
  9. #
  10. from tkinter import *
  11. from tkinter import ttk
  12. from tkinter import messagebox
  13. from tkinter import filedialog
  14. import os
  15. import threading
  16. from functools import partial
  17. from AnimalSorter import sortAnimalsIntoFolders
  18. #Heavy influence from the tutorial at: https://tkdocs.com/tutorial/firstexample.html
  19. STEPHEN_DEBUG = False
  20. class SorterWindow:
  21. #I like setting these here because they will cause error messages if something goes wrong. --Stephen
  22. sourceStr = None
  23. sourceStr_entry = ""
  24. destStr = None
  25. destStr_entry = ""
  26. mainframe = None
  27. myProgressBar = None
  28. myRunButton = None
  29. mySortThread = None
  30. waitTime = None
  31. def __init__(self, root):
  32. self.waitTime = 2000
  33. self.root = root
  34. self.root.title("Animal Sorter!")
  35. ## Create menu
  36. m_help = Menu(self.root, tearoff=0)
  37. m_help.add_command(label="Instructions", command = lambda: self.root.event_generate("<<OpenInstructionsDialog>>"))
  38. m_help.add_command(label="About", command = lambda: self.root.event_generate("<<OpenAboutDialog>>"))
  39. self.root["menu"] = m_help
  40. self.root.bind("<<OpenInstructionsDialog>>", self.launchInstructions)
  41. self.root.bind("<<OpenAboutDialog>>", self.launchAbout)
  42. #Create the background frame
  43. self.mainframe = ttk.Frame(self.root, padding="10 10 10 10")
  44. self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
  45. #Set weights for resizing
  46. self.root.columnconfigure(0, weight=1)
  47. self.root.rowconfigure(0, weight=1)
  48. self.mainframe.columnconfigure(0, weight=0)
  49. self.mainframe.rowconfigure(0, weight=0)
  50. self.mainframe.columnconfigure(1, weight=0) #note 0
  51. self.mainframe.rowconfigure(1, weight=1)
  52. self.mainframe.columnconfigure(2, weight=1)
  53. self.mainframe.rowconfigure(2, weight=1)
  54. self.mainframe.columnconfigure(3, weight=1)
  55. self.mainframe.rowconfigure(3, weight=1)
  56. #Create the text entry fields
  57. self.sourceStr = StringVar()
  58. self.sourceStr_entry = ttk.Entry(self.mainframe, width=80, textvariable=self.sourceStr)
  59. self.sourceStr_entry.grid(column=2, row=1, sticky=(W, E))
  60. self.destStr = StringVar()
  61. self.destStr_entry = ttk.Entry(self.mainframe, width=80, textvariable=self.destStr)
  62. self.destStr_entry.grid(column=2, row=2, sticky=(W, E))
  63. #Create the buttons
  64. sourceTitle = "Choose a source folder..."
  65. sourceCommand = partial(self.openFolderDialog, self.sourceStr, sourceTitle)
  66. ttk.Button(self.mainframe, text="Source Folder", command = sourceCommand).grid(column=3, row=1, sticky = W + E)
  67. destTitle = "Choose a destination folder..."
  68. destCommand = partial(self.openFolderDialog, self.destStr, destTitle)
  69. ttk.Button(self.mainframe, text="Destination Folder", command = destCommand).grid(column=3, row=2, sticky = W + E)
  70. self.myRunButton = ttk.Button(self.mainframe, text="Run Sorter", command=self.runSorting)
  71. self.myRunButton.grid(column=3, row=3, sticky = W + E)
  72. #Create progress bar
  73. self.myProgressBar = ttk.Progressbar(self.mainframe, orient=HORIZONTAL, length=200, mode= "indeterminate")
  74. self.myProgressBar.grid(column = 2, row = 3, sticky = W + E)
  75. #Add a little padding to each widget
  76. for child in self.mainframe.winfo_children():
  77. child.grid_configure(padx=5, pady=5)
  78. #Set default entry field texts
  79. if STEPHEN_DEBUG:
  80. self.sourceStr.set(os.path.normpath("/home/stephen/Documents/School/0spring22/animal-crossing/animal-crossing-gui/test-images"))
  81. self.destStr.set(os.path.normpath("/home/stephen/Documents/School/0spring22/animal-crossing/animal-crossing-gui/test-outputs"))
  82. else:
  83. self.sourceStr.set(os.path.normpath("Enter path to images to be sorted"))
  84. self.destStr.set(os.path.normpath("Enter path to folder where the sorted pictures will go."))
  85. #Open a popup for the about button.
  86. def launchAbout(*args):
  87. theMessage = "This program takes images in a folder and sorts " \
  88. "them into an output folder based on what animal the" \
  89. " machine learning algorithm thinks they are."
  90. theMessage += \
  91. "\n\nAnimal Crossing Project\n\n" \
  92. + "CWU 2022\n\n" \
  93. + "Adara Andonie\n" \
  94. "Alex Worland\n" \
  95. "Harlow Huber\n" \
  96. "Lincoln Huber\n" \
  97. "Stephen Stengel\n"
  98. messagebox.showinfo(title = "About this program", message = theMessage)
  99. #Open a popup for the instructions button.
  100. def launchInstructions(*args):
  101. theMessage = "Click the \"Source Folder\" button to choose what folder of pictures to sort.\n\n" \
  102. "Then click the \"Destination Folder\" button to choose where to copy the sorted pictures to.\n\n" \
  103. "Then click the \"Run Sorter\" button to perform the sorting."
  104. messagebox.showinfo(title = "Instructions", message = theMessage)
  105. def openFolderDialog(self, thisStr, thisTitle, *args):
  106. thisStr.set(filedialog.askdirectory(title = thisTitle))
  107. def runSorting(self, *args):
  108. source = self.sourceStr.get()
  109. dest = self.destStr.get()
  110. #check if either empty
  111. if source == "":
  112. print("source empty! Make a popup to inform them!")
  113. return
  114. elif dest == "":
  115. print("Dest empty! Make popup!")
  116. return
  117. #check if strings are the same
  118. elif source == dest:
  119. print("source == dest. Make popup for this as well!")
  120. return
  121. #run the function
  122. self.myProgressBar.start()
  123. self.myRunButton.state(["disabled"])
  124. if STEPHEN_DEBUG:
  125. self.mySortThread = threading.Thread(target = self.testFunction, args = (source, dest,))
  126. else:
  127. self.mySortThread = threading.Thread(target = sortAnimalsIntoFolders, args = (source, dest,))
  128. self.mySortThread.start()
  129. self.root.after(self.waitTime, self.threadChecker)
  130. #Check if the sorting thread is done every few seconds.
  131. def threadChecker(self):
  132. print("Threadchecker called!")
  133. if self.mySortThread.is_alive():
  134. self.root.after(self.waitTime, self.threadChecker)
  135. else:
  136. self.resetAfterComplete()
  137. def resetAfterComplete(self):
  138. self.myProgressBar.stop()
  139. self.mySortThread = None
  140. self.myRunButton.state(["!disabled"])
  141. print("reset complete")
  142. #create popup to say done?
  143. def testFunction(self, *args):
  144. try:
  145. print("Test function!")
  146. print("source: " + str(self.sourceStr.get()))
  147. print("dest: " + str(self.destStr.get()))
  148. except ValueError:
  149. pass