Skip to content

Commit

Permalink
Update Script - Add GUI for Unicode Entry
Browse files Browse the repository at this point in the history
Added Tkinter GUI window (gui.py) for input of special characters in certain filter modes. This is because windows console cannot handle most Unicode characters.
  • Loading branch information
ThioJoe committed Nov 17, 2021
1 parent 2f76482 commit 3b16aa9
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 13 deletions.
48 changes: 35 additions & 13 deletions YouTubeSpammerPurge.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
### IMPORTANT: I OFFER NO WARRANTY OR GUARANTEE FOR THIS SCRIPT. USE AT YOUR OWN RISK.
### I tested it on my own and implemented some failsafes as best as I could,
### but there could always be some kind of bug. You should inspect the code yourself.
version = "1.5.0-Testing"
version = "1.5.0"
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#

from gui import *

import os
import re
from datetime import datetime
Expand Down Expand Up @@ -672,16 +674,26 @@ def prepare_filter_mode_username(currentUser, deletionEnabledLocal, scanMode):
currentUserName = currentUser[1]
channelChars = make_char_set(currentUserName) # Converts channel name to set of characters to compare with entered filter characters

print("\nInput ONLY any special characters / emojis you want to search for in usernames. Do not include commas or spaces!")
print("Note: Letters and numbers will not be included for safety purposes, even if you enter them.")
print("Example: 👋🔥✔️✨")
print("\nNext, you will input ONLY any special characters / emojis you want to search for in usernames. Don't include commas or spaces!")
print(" Note: Letters and numbers will not be included for safety purposes, even if you enter them.")
print(" Example: 👋🔥✔️✨")
input("\nPress Enter to open the entry window...")
print("-------------------------------------------")

validEntry = False
while validEntry == False:
inputChars = input("Input the characters to search (no commas or spaces): ")
inputChars = make_char_set(inputChars, stripLettersNumbers=True, stripKeyboardSpecialChars=False, stripPunctuation=False)
print("\nWaiting for input Window. Press 'Execute' after entering valid characters to continue...", end="\r")
try:
inputChars = take_input_gui(stripLettersNumbers=True, stripKeyboardSpecialChars=False, stripPunctuation=False)
except NameError: # Catch if user closes GUI window, exit program.
print(" ") # Clears the line because of \r on previous print
print("\nSomething went wrong with the input, or you closed the window improperly.")
input("Press Enter to exit...")
exit()

if any(x in inputChars for x in channelChars):
print("WARNING! Character(s) you entered are within your own username, ' " + currentUserName + " '! : " + str(inputChars & channelChars))
print(" (Symbols above may not show in windows console, copy and paste them somewhere to see the symbols")
if scanMode == 1:
print("Are you SURE you want to search your own comments? (You WILL still get a confirmation before deleting)")
if choice("Choose") == True:
Expand All @@ -693,7 +705,7 @@ def prepare_filter_mode_username(currentUser, deletionEnabledLocal, scanMode):
if choice("Continue?") == True:
validEntry = True
else:
print("Usernames will be scanned for ANY of these individual characters: " + str(inputChars))
print(" Usernames will be scanned for ANY of the characters shown in the previous window.")
if choice("Begin scanning? ") == True:
validEntry = True
deletionEnabledLocal = "HalfTrue"
Expand All @@ -704,15 +716,25 @@ def prepare_filter_mode_username(currentUser, deletionEnabledLocal, scanMode):
# 3
# For Filter mode 3, user inputs characters in comment text to filter
def prepare_filter_mode_comment_text(currentUser, deletionEnabledLocal, scanMode):
print("\nInput ONLY any special characters / emojis you want to search for in all comments. Do not include commas or spaces!")
print("Note: Letters, numbers, and punctuation will not be included for safety purposes, even if you enter them.")
print("Example: 👋🔥✔️✨")
print("\nNext, you will input ONLY any special characters / emojis you want to search for in all comments. Do not include commas or spaces!")
print(" Note: Letters, numbers, and punctuation will not be included for safety purposes, even if you enter them.")
print(" Example: 👋🔥✔️✨")
input("\nPress Enter to open the entry window...")
print("-------------------------------------------")

validEntry = False
while validEntry == False:
inputChars = input("Input the characters to search (no commas or spaces): ")
inputChars = make_char_set(inputChars, stripLettersNumbers=True, stripKeyboardSpecialChars=False, stripPunctuation=True)
print("\nWaiting for input Window. Press 'Execute' after entering valid characters to continue...", end="\r")

try:
inputChars = take_input_gui(stripLettersNumbers=True, stripKeyboardSpecialChars=False, stripPunctuation=True)
except NameError: # Catch if user closes GUI window, exit program.
print(" ") # Clears the line
print("\nSomething went wrong with the input, or you closed the window improperly.")
input("Press Enter to exit...")
exit()

print("Comment text will be scanned for ANY of these individual characters: " + str(inputChars))
print(" Comment text will be scanned for ANY of the characters shown in the previous window.")
if choice("Begin scanning? ") == True:
validEntry = True
deletionEnabledLocal = "HalfTrue"
Expand Down
Binary file added assets/cancelButton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/executeButton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/inputSubmitButton.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/inputTextBox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/outputTextBox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
298 changes: 298 additions & 0 deletions gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@

# This file was generated by the Tkinter Designer by Parth Jadhav
# https://github.com/ParthJadhav/Tkinter-Designer

import YouTubeSpammerPurge as myFunctions

from pathlib import Path
import os
import sys

# from tkinter import *
# Explicit imports to satisfy Flake8
from tkinter import Menu, TclError, Tk, Canvas, Entry, Text, Button, PhotoImage
from tkinter import END
from functools import partial



OUTPUT_PATH = Path(__file__).parent
ASSETS_PATH = OUTPUT_PATH / Path("./assets")

# Probably not needed anymore because of resource_path(), but will keep in case
def relative_to_assets(path: str) -> Path:
return ASSETS_PATH / Path(path)

# Checks if the application is running as a pyinstaller bundle, if so, specifies correct path to resources - use resource_path() when specifying resources
# Also need to add to 'datas' in pyinstaller spec file, such as: a.datas += [('icon.png', '.\\assets\\icon.png', 'DATA')]
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'): # If running as a pyinstaller bundle
#print("Test1") # For Debugging
#print(os.path.join(sys._MEIPASS, relative_path)) # For Debugging
return os.path.join(sys._MEIPASS, relative_path)
#print("Test2") # for Debugging
#print(os.path.join(os.path.abspath("assets"), relative_path)) # For debugging
return os.path.join(os.path.abspath("assets"), relative_path) # If running as script, specifies resource folder as /assets


# To allow canvas objects to be updated and called upon, need to create object wrapper
class CanvasObject:
id = 0
def __init__(self, canvas, canvas_id):
self.canvas = canvas # keep a reference of the canvas
self.canvas_id = canvas_id # keep a reference of the item id on the canvas
self.id = CanvasObject.id # each CanvasObject has its own unique id
CanvasObject.id += 1

def itemconfig(self, **kwargs):
self.canvas.itemconfig(self.canvas_id, kwargs)


################## RIGHT CLICK MENU ######################
# Code From: https://stackoverflow.com/a/4552646/17312053

def rClicker(e):
#''' right click context menu for all Tk Entry and Text widgets'''
try:
def rClick_Copy(e, apnd=0):
e.widget.event_generate('<Control-c>')

def rClick_Cut(e):
e.widget.event_generate('<Control-x>')

def rClick_Paste(e):
e.widget.event_generate('<Control-v>')

e.widget.focus()

nclst=[
(' Cut', lambda e=e: rClick_Cut(e)),
(' Copy', lambda e=e: rClick_Copy(e)),
(' Paste', lambda e=e: rClick_Paste(e)),
]

rmenu = Menu(None, tearoff=0, takefocus=0)

for (txt, cmd) in nclst:
rmenu.add_command(label=txt, command=cmd)

rmenu.tk_popup(e.x_root+40, e.y_root+10,entry="0")

except TclError:
print(' - rClick menu, something wrong')
pass

return "break"

def rClickbinder(r):
try:
for b in [ 'Text', 'Entry', 'Listbox', 'Label']: #
r.bind_class(b, sequence='<Button-3>',
func=rClicker, add='')
except TclError:
print(' - rClickbinder, something wrong')
pass

###################### Main Window ############################

def take_input_gui(stripLettersNumbers=False, stripKeyboardSpecialChars=False, stripPunctuation=False):
window = Tk()
window.title("YT Spammer Purge")
window.iconphoto(False, PhotoImage(file=resource_path("icon.png")))

window.geometry("276x279")
window.configure(bg = "#FFFFFF")

#################### Functions ####################

# Get text from textbox and then clear the box
def submit(boxName):
global returnText
text = boxName.get()
boxName.delete('0', END)
returnText = myFunctions.make_char_set(text, stripLettersNumbers=stripLettersNumbers, stripKeyboardSpecialChars=stripKeyboardSpecialChars, stripPunctuation=stripPunctuation)

outputTextBox.config(state='normal')
outputTextBox.delete('1.0', END)
outputTextBox.insert(END, returnText)
outputTextBox.config(state='disabled')

if len(returnText) > 0:
warningMessage.itemconfig(state="hidden")
elif len(returnText) == 0:
warningMessage.itemconfig(state="normal")

def quit():
global returnText
returnText = []
window.destroy()

def execute():
try:
if len(returnText) == 0:
warningMessage.itemconfig(state="normal")
pass
elif len(returnText) > 0:
window.destroy()
except NameError:
warningMessage.itemconfig(state="normal")



canvas = Canvas(
window,
#bg = "#FFFFFF",
bg = "#f0f0f0",
height = 279,
width = 276,
bd = 0,
highlightthickness = 0,
relief = "ridge"
)

canvas.place(x = 0, y = 0)
canvas.create_text(
19.0,
48.0,
anchor="nw",
text="Enter Search Terms:",
fill="#000000",
font=("Roboto", 12 * -1)
)

canvas.create_text(
19.0,
148.0,
anchor="nw",
text="Terms will be displayed back here:",
fill="#000000",
font=("Roboto", 12 * -1)
)

# Bottom text box
entry_image_1 = PhotoImage(
file=resource_path("outputTextBox.png"))
entry_bg_1 = canvas.create_image(
138.5,
186.0,
image=entry_image_1
)
outputTextBox = Text(
bd=0,
bg="#F8F8F8",
state="disabled",
highlightthickness=0,
#tag="False"

)
outputTextBox.place(
x=27.0,
y=164.0,
width=223.0,
height=38.0
)


entry_image_2 = PhotoImage(
file=resource_path("inputTextBox.png"))
entry_bg_2 = canvas.create_image(
138.5,
85.0,
image=entry_image_2
)
inputTextBox = Entry(
bd=0,
bg="#FCFCFC",
highlightthickness=0
)
inputTextBox.place(
x=27.0,
y=67.0,
width=223.0,
height=30.0
)

button_image_1 = PhotoImage(
file=resource_path("cancelButton.png"))
cancelButton = Button(
image=button_image_1,
borderwidth=0,
highlightthickness=0,
command=quit,
relief="flat"
)
cancelButton.place(
x=152.0,
y=225.0,
#width=78.0,
#height=32.0,
width=86.0,
height=40.0
)

button_image_2 = PhotoImage(
file=resource_path("executeButton.png"))
executeButton = Button(
image=button_image_2,
borderwidth=0,
highlightthickness=0,
command=execute,
relief="flat"
)
executeButton.place(
x=42.0,
y=225.0,
#width=78.0,
#height=32.0
width=86.0,
height=40.0
)

canvas.create_text(
32.0,
8.0,
anchor="nw",
text="Filter Terms Input",
fill="#000000",
font=("Roboto", 24 * -1)
)

# Submit Button
button_image_3 = PhotoImage(
file=resource_path("inputSubmitButton.png"))
inputSubmitButton = Button(
image=button_image_3,
borderwidth=0,
highlightthickness=0,
command=partial(submit, inputTextBox),
relief="flat"
)
inputSubmitButton.place(
x=174.0,
y=110.0,
#width=71.0,
#height=22.0
width=78.0,
height=28.0
)

# Warning about no input
# Object wrapper for canvas text object, so can update it with warningMessage.itemconfig(state=warningState)
warningMessage = CanvasObject(canvas, canvas.create_text(
19.0,
105.0,
anchor="nw",
text="No valid terms entered!",
fill="red",
font=("Roboto", 12 * -1),
state="hidden"
))

inputTextBox.bind('<Button-3>',rClicker, add='') # Binds right click menu to inputTextBox only

window.resizable(False, False)
window.mainloop()

return returnText

#take_input_gui() # For testing this module in standalone, uncomment

0 comments on commit 3b16aa9

Please sign in to comment.