Пример #1
0
class Clock:
    def __init__(self):
        self.time1 = time.strftime('%H:%M:%S')
        self.mFrame = Frame()
        self.mFrame.pack(side=TOP, expand=YES, fill=X)
        self.watch = Label(self.mFrame,
                           text=self.time1,
                           font=('times', 12, 'bold'))
        self.watch.pack()
        self.changeLabel()  #first call it manually

    def changeLabel(self):
        self.time1 = time.strftime('%H:%M:%S')
        self.watch.configure(text=self.time1)
        self.mFrame.after(200,
                          self.changeLabel)  #it'll call itself continuously
Пример #2
0
class App:
    def __init__(self, master):
        self.frame = Frame(master)
        self.frame.pack()

        self.var = StringVar()
        self.data_queue = Queue()

        self.button = Button(self.frame, text=self.var)
        self.button.pack(side=LEFT)

        # start the "fetch" thread (just generate data from the clock)
        self.stop_thread = False
        self.thread = Thread(target=self.get_data, args=("DATA thread", self.data_queue))
        self.thread.start()

        # when user clicks "close" widget, call self.stop_all()
        master.protocol("WM_DELETE_WINDOW", self.stop_all )

        self.frame.after(500, self.updater)

    def get_data(self, threadname, q):
        while not self.stop_thread:
            a = str(time.time())
            self.data_queue.put(a)
            print(f'get_data: put "{a}" to queue')
            time.sleep(3)

    def updater(self):
        while not self.data_queue.empty():
            var = self.data_queue.get()
            print(f'updater: got {var}')
        self.frame.after(500, self.updater)     # call again after 500ms

    def stop_all(self):
        """Catch the "window close event".

        We must kill the thread and then call normal quit() function.
        """

        self.stop_thread = True
        self.thread.join()      # wait for thread to stop
        self.frame.quit()       # call normal frame quit()
Пример #3
0
class TicTacToeGUI:

    ai = None

    def __init__(self, master):
        # Initial Frame
        self.frame = Frame(master)
        self.frame.pack(fill="both", expand=True)

        # Board canvas
        self.canvas = Canvas(self.frame, width=300, height=300)

        # Symbol selection buttons
        self.x_button = Button(self.frame, text='Play as X', height=4,
                               command=self.set_player_x, bg='white',
                               fg='black')
        self.o_button = Button(self.frame, text='Play as O', height=4,
                               command=self.set_player_o, bg='white',
                               fg='red')

        # Game start button and info box
        self.start_button = Button(self.frame, text="START", height=4,
                                   command=self.start, bg='white', fg='purple')
        self.info_box = Label(self.frame, text='Tic Tac Toe Game', height=4,
                              bg='white', fg='blue')

        self.clean_game_board()

    def start(self):
        """Sets up game board, starts a tic tac toe game,
        and a new AI if one doesn't exist. AI makes first move if
        playing as X
        """
        self.set_game_board()
        self.game = TicTacToe()
        self.game.start()
        if not self.ai:
            self.ai = TicTacToeAI()

        if self.ai_symbol == 'x':
            self.ai_action()

    def _board(self):
        """Draws tic tac toe board"""
        self.canvas.create_rectangle(0, 0, 300, 300, outline="black")
        self.canvas.create_rectangle(100, 300, 200, 0, outline="black")
        self.canvas.create_rectangle(0, 100, 300, 200, outline="black")

    def user_action(self, event):
        """Attempts to take action that matches user click. If the move is valid,
        then calls the AI to make the next move. If not, displays the error.
        """
        move_x = event.x // 100
        move_y = event.y // 100
        move_result = self.game.update(self.player_symbol, (move_x, move_y))
        if move_result == "Success":
            board_x = (200 * move_x + 100) / 2
            board_y = (200 * move_y + 100) / 2
            if self.player_symbol == 'x':
                self.draw_x(board_x, board_y)
            else:
                self.draw_o(board_x, board_y)
            if not self.completed():
                # Wait a bit before calling the ai, for visual style
                self.frame.after(500, self.ai_action)
        else:
            self.info_box['text'] = move_result

    def ai_action(self):
        """Gets the next move from the AI based on current game state,
        and plays.
        """
        state = self.game.get_board()
        move = self.ai.get_move(state)
        move_result = self.game.update(self.ai_symbol, move)
        if move_result == "Success":
            board_x = (200 * move[0] + 100) / 2
            board_y = (200 * move[1] + 100) / 2
            if self.ai_symbol == 'x':
                self.draw_x(board_x, board_y)
            else:
                self.draw_o(board_x, board_y)
            self.completed()

    def completed(self):
        """Checks the game status. If completed, displays the result,
        and asks whether the player would like to start another game.
        """
        status = self.game.done()
        if status == 'e':
            return False
        message = "Click to start a new game."
        if status == 't':
            message = "Tie game. " + message
        else:
            message = "Player " + status.upper() + " has won. " + message
        self.info_box.pack_forget()
        self.start_button.pack(fill="both", expand=True)
        self.start_button["text"] = message
        self.start_button["command"] = self.clean_game_board

    def draw_x(self, x, y):
        self.canvas.create_line(x + 20, y + 20, x - 20, y - 20, width=4,
                                fill="black")
        self.canvas.create_line(x - 20, y + 20, x + 20, y - 20, width=4,
                                fill="black")

    def draw_o(self, x, y):
        self.canvas.create_oval(x + 25, y + 25, x - 25, y - 25, width=4,
                                outline="red")

    def set_game_board(self):
        """Hides game start buttons, reveals the game board and info box."""
        self.start_button.pack_forget()
        self.x_button.pack_forget()
        self.o_button.pack_forget()
        self.canvas.delete(ALL)
        self.canvas.pack(fill="both", expand=True)
        self.info_box.pack(fill="both", expand=True)
        self.canvas.bind("<ButtonPress-1>", self.user_action)
        self._board()

    def clean_game_board(self):
        """Hides game board and label, reveals game start buttons."""
        self.canvas.pack_forget()
        self.info_box.pack_forget()
        self.start_button.pack_forget()
        self.x_button.pack(fill="both", expand=True)
        self.o_button.pack(fill="both", expand=True)

    def set_player_x(self):
        self.player_symbol = 'x'
        self.ai_symbol = 'o'
        self.start()

    def set_player_o(self):
        self.player_symbol = 'o'
        self.ai_symbol = 'x'
        self.start()
Пример #4
0
class GUIMod(threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
        self.img_dict = {}
        self.data_room = []
        self.data_role = {'userName': '******', 'state': '\0', 'roomName': '\0'}
        self.realRoot = None
        self.root = None
        self.rootFrame = None
        self.entryFrame = None

    def loadImg(self):
        self.img_dict['1'] = PhotoImage(file='img/咖啡.png')  # 空闲
        self.img_dict['2'] = PhotoImage(file='img/键盘.png')  # 打字中
        self.img_dict['3'] = PhotoImage(file='img/在线.png')  # 在线
        self.img_dict['4'] = PhotoImage(file='img/忙碌.png')  # 忙碌
        self.img_dict['5'] = PhotoImage(file='img/等待.png')  # 给我点时间,我要思考
        self.img_dict['6'] = PhotoImage(file='img/快进.png')  # 搞快点
        self.img_dict['7'] = PhotoImage(file='img/疑问.png')  # 令人困惑
        self.img_dict['8'] = PhotoImage(file='img/震惊.png')  # 令人震惊
        self.img_dict['9'] = PhotoImage(file='img/灯泡.png')  # 有想法了
        self.img_dict['10'] = PhotoImage(file='img/用户.png')  # 我是主角
        self.img_dict['11'] = PhotoImage(file='img/空白.png')

    def change_state(self, state_list: list):
        try:
            old = self.data_role['state'].split('_')
            if (self.data_role['state'] == '\0'
                    or self.data_role['userName'] == '\0'
                    or self.data_role['roomName'] == '\0'):
                return
            for i in range(len(old)):
                if (state_list[i] == '-1'):
                    state_list[i] = old[i]
            new = '_'.join(state_list)
            if (new == self.data_role['state']):
                return
            myHttpRequest(url_changeState, {
                'userName': self.data_role['userName'],
                'state': new
            })
        except:
            print('change_state error')

    def set_line(self, item: dict, frame: Frame):
        children = frame.winfo_children()
        children[0].configure(text=item['userName'])
        mark = item['state'].split('_')
        children[1].configure(image=self.img_dict[mark[0]])
        children[2].configure(image=self.img_dict[mark[1]])

    def get_role(self):
        for i in range(len(self.data_room)):
            if (self.data_room[i]['userName'] == self.data_role['userName']):
                self.data_role['state'] = self.data_room[i]['state']
                break

    def rebuild(self):
        self.rootFrame.destroy()
        self.rootFrame = Frame(self.root, height=100, width=200)
        self.rootFrame.grid(row=2, column=0)
        for i in range(len(self.data_room)):
            item = self.data_room[i]
            f = Frame(self.rootFrame)
            f.grid(row=i, column=0, pady=3, sticky=W)
            mark = item['state'].split('_')
            Label(f, text=item['userName'], width=10).grid(row=0,
                                                           column=0,
                                                           padx=5)
            Label(f, image=self.img_dict[mark[0]], width=35).grid(row=0,
                                                                  column=1,
                                                                  padx=5)
            Label(f, image=self.img_dict[mark[1]], width=35).grid(row=0,
                                                                  column=2,
                                                                  padx=5)
            if (item['userName'] == self.data_role['userName']):
                Label(f, image=self.img_dict['10'], width=35).grid(row=0,
                                                                   column=3,
                                                                   padx=5)
            else:
                Label(f, image=self.img_dict['11'], width=35).grid(row=0,
                                                                   column=3,
                                                                   padx=5)

    def report_keyboard(self):
        if (is_input_flag == True):
            self.change_state(['2', '-1'])
        else:
            self.change_state(['1', '-1'])

    def update(self):
        if (self.data_role['userName'] != '\0'
                and self.data_role['roomName'] != '\0'):
            self.report_keyboard()
            data_room_new = myHttpRequest(
                url_getRoom, {'roomName': self.data_role['roomName']})
            if (len(data_room_new) != len(self.data_room)):
                self.data_room = data_room_new
                self.rebuild()
            else:
                for i in range(len(self.data_room)):
                    if (self.data_room[i] != data_room_new[i]):
                        self.set_line(data_room_new[i],
                                      self.rootFrame.winfo_children()[i])
                self.data_room = data_room_new
                self.get_role()

        self.root.after(1000, self.update)

    def run(self):
        self.realRoot = Tk()
        self.realRoot.attributes('-alpha', 0.75)
        self.realRoot.title('TRPGHelper')

        self.realRoot.protocol("WM_DELETE_WINDOW", lambda: os._exit(0))

        width = 260
        height = 400
        img = ImageTk.PhotoImage(
            Image.open('img/bg.png').resize((width, height)))
        l = Label(self.realRoot, image=img)
        l.grid(row=0, column=0)

        self.root = Frame(self.realRoot)
        self.root.grid(row=0, column=0)

        self.loadImg()
        self.entryFrame = Frame(self.root, height=50, width=120)
        self.rootFrame = Frame(self.root, height=100, width=120)

        self.entryFrame.grid(column=0)
        self.rootFrame.grid(row=2, column=0, sticky=W)

        ButtonRoomName = Button(self.entryFrame, height=1, text='房间')
        ButtonUserName = Button(self.entryFrame, height=1, text='用户')
        EntryRoomName = Entry(self.entryFrame, width=10)
        EntryUserName = Entry(self.entryFrame, width=10)

        MenuButtonState = Menubutton(self.entryFrame, text='选择状态', width=10)
        ms = Menu(MenuButtonState, tearoff=False)
        ms.add_command(label='我在线哦',
                       command=lambda: self.change_state(['-1', '3']))
        ms.add_command(label='真的很忙',
                       command=lambda: self.change_state(['-1', '4']))
        ms.add_command(label='等等我!',
                       command=lambda: self.change_state(['-1', '5']))
        ms.add_command(label='搞快点!',
                       command=lambda: self.change_state(['-1', '6']))
        ms.add_command(label='令人困惑',
                       command=lambda: self.change_state(['-1', '7']))
        ms.add_command(label='瓦特法?',
                       command=lambda: self.change_state(['-1', '8']))
        ms.add_command(label='懂了悟了',
                       command=lambda: self.change_state(['-1', '9']))
        MenuButtonState.configure(menu=ms)

        def _call_roomNameButton(roomName):
            self.data_role['roomName'] = roomName

        def _call_userNameButton_right():
            myHttpRequest(url_delUser,
                          {'userName': self.data_role['userName']})

        def _call_userNameButton_left(userName):
            if (userName != self.data_role['userName']):
                self.change_state(['1', '4'])
            self.data_role['userName'] = userName
            try:
                if (self.data_role['userName'] != '\0'):
                    myHttpRequest(url_register,
                                  {'userName': self.data_role['userName']})
                    myHttpRequest(
                        url_createRoom, {
                            'userName': self.data_role['userName'],
                            'roomName': self.data_role['roomName']
                        })
                    self.rebuild()
            except:
                print('userNameButton error')

        ButtonRoomName.bind(
            "<Button-1>", lambda f: _call_roomNameButton(EntryRoomName.get()))
        ButtonUserName.bind(
            "<Button-1>",
            lambda f: _call_userNameButton_left(EntryUserName.get()))
        ButtonUserName.bind("<Button-3>",
                            lambda f: _call_userNameButton_right())

        ButtonRoomName.grid(column=0, row=0, ipady=2, pady=5, ipadx=2, padx=5)
        ButtonUserName.grid(column=0, row=1, ipady=2, pady=5, ipadx=2, padx=5)
        EntryRoomName.grid(column=1, row=0, pady=5, ipadx=2, padx=5)
        EntryUserName.grid(column=1, row=1, pady=5, ipadx=2, padx=5)
        MenuButtonState.grid(column=2, row=0, pady=5, ipadx=2, padx=5)

        self.root.after(1000, self.update)
        self.root.mainloop()
Пример #5
0
class Gui:
    def __init__(self):
        self.root = tkinter.Tk(className="TicTacToe")
        self.settingsFrame = Frame(self.root, padx=10, pady=10)
        self.gameFrame = Frame(self.root)

        self.gameFrame.pack(side=LEFT, expand=True)
        self.settingsFrame.pack(side=RIGHT, expand=True)

        self.game = TicTacToe()
        self.currentCode = self.game.activePlayer
        self.startPlayer = self.currentCode

        self.canvas = Canvas(self.gameFrame, width=300, height=300)
        self.drawGrid()
        self.createSettingsFrame()

        self.canvas.bind('<Button-1>', self.clickHandler)
        self.root.mainloop()

    def clickHandler(self, event):
        x, y = event.x, event.y

        if x < 100 and y < 100:
            self.drawCode(self.currentCode, 0)
        elif x > 100 and x < 200 and y < 100:
            self.drawCode(self.currentCode, 1)
        elif x > 200 and x < 300 and y < 100:
            self.drawCode(self.currentCode, 2)
        elif x < 100 and y > 100 and y < 200:
            self.drawCode(self.currentCode, 3)
        elif x > 100 and x < 200 and y > 100 and y < 200:
            self.drawCode(self.currentCode, 4)
        elif x > 200 and x < 300 and y > 100 and y < 200:
            self.drawCode(self.currentCode, 5)
        elif x < 100 and y > 200 and y < 300:
            self.drawCode(self.currentCode, 6)
        elif x > 100 and x < 200 and y > 200 and y < 300:
            self.drawCode(self.currentCode, 7)
        elif x > 200 and x < 300 and y > 200 and y < 300:
            self.drawCode(self.currentCode, 8)

    def createSettingsFrame(self):
        Label(self.settingsFrame, text="Score",
              font="Arial 15", pady=15).pack(side=TOP)
        Label(self.settingsFrame, text="Player1",
              font="Arial 10").pack(side=TOP)
        Label(self.settingsFrame, textvariable=self.game.winsPlayerOne,
              font="Arial 8").pack(side=TOP)
        Label(self.settingsFrame, text="Player2",
              font="Arial 10").pack(side=TOP)
        Label(self.settingsFrame, textvariable=self.game.winsPlayerTwo,
              font="Arial 8").pack(side=TOP)

    def drawGrid(self):
        self.canvas.create_line(0, 100, 400, 100)
        self.canvas.create_line(0, 200, 400, 200)
        self.canvas.create_line(100, 0, 100, 300)
        self.canvas.create_line(200, 0, 200, 300)
        self.canvas.create_line(300, 0, 300, 300)
        self.canvas.pack()

    def deleteAll(self):
        for ele in TEXT_IDS:
            self.canvas.delete(ele)
        self.game.resetGrid()
        self.canvas.bind('<Button-1>', self.clickHandler)

    def drawCode(self, code, pos):
        arr = DRAWING_POSITIONS[pos]

        if self.game.checkMoveIfValid(pos, code):
            TEXT_IDS.append(self.canvas.create_text(
                arr[0], arr[1], text=self.currentCode, font="Arial 40"))
            row = self.game.checkWin(self.currentCode)
            remis = self.game.checkRemis()
            if remis:
                self.canvas.unbind('<Button-1>')
                self.gameFrame.after(2000, self.deleteAll)
            if row:
                if row == 1:
                    TEXT_IDS.append(self.canvas.create_line(25, 50, 275, 50))
                elif row == 2:
                    TEXT_IDS.append(self.canvas.create_line(25, 150, 275, 150))
                elif row == 3:
                    TEXT_IDS.append(self.canvas.create_line(25, 250, 275, 250))
                elif row == 4:
                    TEXT_IDS.append(self.canvas.create_line(50, 25, 50, 275))
                elif row == 5:
                    TEXT_IDS.append(self.canvas.create_line(150, 25, 150, 275))
                elif row == 6:
                    TEXT_IDS.append(self.canvas.create_line(250, 25, 250, 275))
                elif row == 7:
                    TEXT_IDS.append(self.canvas.create_line(25, 25, 275, 275))
                elif row == 8:
                    TEXT_IDS.append(self.canvas.create_line(25, 275, 275, 25))
                self.canvas.unbind('<Button-1>')
                self.gameFrame.after(2000, self.deleteAll)
            self.game.switchPlayer()
            self.currentCode = self.game.activePlayer
Пример #6
0
class widgets:
    def __init__(self, main):
        super().__init__()
        self.window = main
        # Set window title
        self.window.title('Create ReactJs Project')
        # Set window size
        self.window.geometry("720x530")
        #disable resizing
        self.window.resizable(False, False)
        # Set window background
        self.window.configure(bg='#262625')
        # choose application icon
        p1 = PhotoImage(file=f'{content.absolutePath}images/logo.png')
        # Setting icon of master window
        self.window.iconphoto(False, p1)
        #checking OS for future use
        status, result = subprocess.getstatusoutput(f"cd /d images")
        if status == 0:
            self.getOS = 'windows'
        else:
            self.getOS = 'linux'
        #create frames
        self.taskDone = False
        self.headerFrame = Frame(self.window, background="#262625")
        self.headerFrame.grid(row=0, column=0, sticky="NSEW", pady=10)
        self.footerFrame = Frame(self.window)
        self.createHeader()
        self.createFirstWindow()

    def createHeader(self):  #creates global header for application
        self.titleIamge = Canvas(self.headerFrame,
                                 width=600,
                                 height=120,
                                 background="#262625",
                                 bd=0,
                                 highlightthickness=0)
        self.titleIamge.grid(row=0, column=0, padx=60)
        self.img = ImageTk.PhotoImage(
            Image.open(f"{content.absolutePath}images/titleIcon.png"))
        self.titleIamge.create_image(285, 80, image=self.img)

    def createFirstWindow(self):  # creates first window
        self.contentFrame1 = Frame(self.window,
                                   background="#262625",
                                   highlightbackground="#42B3D2",
                                   highlightthickness=1,
                                   relief=tk.RAISED,
                                   borderwidth=0)
        self.contentFrame1.grid(row=1, column=0, sticky="NS", pady=10, padx=0)
        self.createForm()

    def createForm(self):  #creates form for fisrt window
        # ================================= creation and placement of form labels ========================== #
        Label(self.contentFrame1,
              text="Select Project Directory",
              background="#262625",
              foreground="#c7c9c7",
              font=("", 10)).grid(row=0,
                                  column=0,
                                  sticky=tk.W + tk.N,
                                  padx=20,
                                  pady=23)
        Label(self.contentFrame1,
              text="Enter Project Name",
              background="#262625",
              foreground="#c7c9c7",
              font=("", 10)).grid(row=1,
                                  column=0,
                                  sticky=tk.W,
                                  padx=20,
                                  pady=10)
        Label(self.contentFrame1,
              text="Enter Project Version",
              background="#262625",
              foreground="#c7c9c7",
              font=("", 10)).grid(row=2,
                                  column=0,
                                  sticky=tk.W,
                                  padx=20,
                                  pady=10)
        Label(self.contentFrame1,
              text="Enter Project Description",
              background="#262625",
              foreground="#c7c9c7",
              font=("", 10)).grid(row=3,
                                  column=0,
                                  sticky=tk.W + tk.N,
                                  padx=20,
                                  pady=10)
        # ================================= creation and placements of form inputs ========================== #
        self.e1 = tk.Entry(self.contentFrame1,
                           borderwidth=0,
                           disabledbackground="#343634",
                           disabledforeground="#c7c9c7",
                           font=("Times New Roman", 12))
        self.e1.grid(row=0, column=1, columnspan=2, padx=8)
        self.e1.insert(0, " -- nothing selected --")
        self.e1.configure(state='disabled')
        self.e2 = tk.Entry(self.contentFrame1,
                           background="#343634",
                           fg="#c7c9c7",
                           borderwidth=0,
                           font=("Times New Roman", 12))
        self.e2.grid(row=1, column=1, columnspan=2, padx=8)
        self.e3 = tk.Entry(self.contentFrame1,
                           background="#343634",
                           fg="#c7c9c7",
                           disabledbackground="#343634",
                           disabledforeground="#c7c9c7",
                           borderwidth=0,
                           font=("Times New Roman", 12))
        self.e3.grid(row=2, column=1, columnspan=2, padx=8)
        self.e3.insert(0, "1.0.0")
        self.e3.configure(state='disabled')
        #textarea for input
        self.textarea = st.ScrolledText(self.contentFrame1,
                                        width=18,
                                        height=4,
                                        borderwidth=0,
                                        highlightthickness=0,
                                        font=("Times New Roman", 12),
                                        background="#343634",
                                        fg="#c7c9c7")
        self.textarea.grid(row=3, column=1, columnspan=2, padx=6, pady=8)

        # ================================= creation and placements of action buttons ========================== #

        #for choose button
        self.folderimg = PhotoImage(
            file=f"{content.absolutePath}images/folderIcon.png")
        self.chooseBtn = tk.Button(self.contentFrame1,
                                   background="#262625",
                                   image=self.folderimg,
                                   borderwidth=0,
                                   highlightthickness=0,
                                   activebackground="#2b2b2a",
                                   cursor="hand1",
                                   command=self.selectFolder).grid(row=0,
                                                                   column=3,
                                                                   sticky=tk.W,
                                                                   pady=4)
        #generating information buttons
        messages = content.messages
        self.infoimg = PhotoImage(
            file=f"{content.absolutePath}images/infoIcon.png")
        for j in range(0, len(messages)):
            self.infoBtn = tk.Button(
                self.contentFrame1,
                image=self.infoimg,
                borderwidth=0,
                highlightthickness=0,
                background="#262625",
                activebackground="#2b2b2a",
                cursor="hand1",
                command=partial(self.messageWidget, messages[j][0],
                                messages[j][1])).grid(row=j + 1,
                                                      column=3,
                                                      sticky=tk.W + tk.N,
                                                      pady=10)
        #for submit button
        self.startimg = PhotoImage(
            file=f"{content.absolutePath}images/startIcon.png")
        self.submitBtn = tk.Button(self.contentFrame1,
                                   text="Start",
                                   borderwidth=0,
                                   highlightthickness=0,
                                   background="#1d84bf",
                                   foreground="white",
                                   activebackground="#2b2b2a",
                                   cursor="hand1",
                                   font=("", 12),
                                   command=self.processData).grid(row=5,
                                                                  column=1,
                                                                  sticky=tk.W +
                                                                  tk.E,
                                                                  padx=15,
                                                                  pady=20)

        # ================================= creation and placements of decorative images ========================== #
        self.graphicImage = Canvas(self.contentFrame1,
                                   width=130,
                                   height=140,
                                   background="#262625",
                                   bd=0,
                                   highlightthickness=0)
        self.graphicImage.grid(row=3, column=3, rowspan=3, padx=60)
        self.img2 = ImageTk.PhotoImage(
            Image.open(f"{content.absolutePath}images/graphics1.png"))
        self.graphicImage.create_image(70, 70, image=self.img2)

    #---------------- action functions --------------------------------------------------------------------------------#
    def selectFolder(self):  #folder selection widget for form input
        self.folder_selected = filedialog.askdirectory()
        self.e1.configure(state='normal')
        self.e1.delete(0, last=len(self.e1.get()))
        self.e1.insert(0, self.folder_selected)
        self.e1.configure(state='disabled')

    def processData(self):  #handling of form data once submitted
        #storing form data
        if self.e1.get() == ' -- nothing selected --':
            self.messageWidget(
                "Please select project directory.\nThis field can't be empty! ",
                "warning")
        elif re.search("[A-Za-z]", self.e2.get()) == None:
            self.messageWidget(
                "Empty Or Invalid value in Name field! Also this field requires at least one alphabet",
                "warning")
        elif re.search("[A-Za-z]", self.textarea.get("1.0", tk.END)) == None:
            self.messageWidget(
                "Empty Or Invalid value in Description field! Also this field requires at least one alphabet",
                "warning")
        else:
            self.dirPath = self.e1.get()
            self.name = self.e2.get()
            self.version = self.e3.get()
            self.description = self.textarea.get("1.0", tk.END)
            self.path = os.path.join(self.dirPath, self.name)
            os.mkdir(self.path)
            self.createSecondWindow()  #!important creation of second window

    # ===================================== ++++++++++++++++++++++++++ =================================== #
    # ============================================  Second window ======================================== #
    # ===================================== ++++++++++++++++++++++++++ =================================== #

    def createSecondWindow(self):  #second window initialization
        #distroy current widgets
        self.contentFrame1.destroy()
        self.contentFrame2 = Frame(self.window,
                                   background="#262625",
                                   highlightbackground="#42B3D2",
                                   highlightthickness=1,
                                   relief=tk.RAISED,
                                   borderwidth=0)
        self.contentFrame2.grid(row=1, column=0, sticky="NS", pady=10, padx=0)
        self.createProgressbar()
        self.createConsole()

        #cancle button to terminate process
        self.cancelBtn = tk.Button(self.contentFrame2,
                                   borderwidth=0,
                                   text="Terminate tasks",
                                   highlightthickness=0,
                                   background="#bf1d1d",
                                   activebackground="#2b2b2a",
                                   cursor="hand1",
                                   foreground="white",
                                   font=("", 12),
                                   command=self.cancelTask).grid(row=3,
                                                                 column=0,
                                                                 sticky=tk.W,
                                                                 pady=15,
                                                                 padx=250)
        self.fetchCommands()

    def cancelTask(self):  #cancle processes
        if messagebox.askyesno(
                "Are you sure?",
                "Do you really wants to cancel installation process?"):
            self.contentFrame2.destroy()
            self.cleanDir()
            self.createFirstWindow()
        else:
            pass

    def updateFrame(self, ind):  #frame updater function for gif(loader)
        self.frame = self.loaderFrames[ind]
        ind += 1
        ind = ind % (len(self.loaderFrames) - 1)
        self.label.configure(image=self.frame)
        self.contentFrame2.after(200, self.updateFrame, ind)

    def createProgressbar(self):  #main progressbar components

        #status label for progressbar
        self.statusLabel = Label(self.contentFrame2,
                                 text="initializing commands..",
                                 background="#262625",
                                 foreground="#c7c9c7",
                                 font=("", 10))
        self.statusLabel.grid(row=0, column=0, sticky="NW", padx=55, pady=15)

        #loader gif
        self.loaderFrames = [
            PhotoImage(file=f'{content.absolutePath}images/loader.gif',
                       format='gif -index %i' % (i)) for i in range(24)
        ]
        self.label = Label(self.contentFrame2, background="#262625")
        self.label.grid(row=0, column=0, sticky="W", padx=25)
        self.contentFrame2.after(0, self.updateFrame, 0)

        #progressbar
        self.p = Progressbar(self.contentFrame2,
                             orient=tk.HORIZONTAL,
                             length=570,
                             mode="determinate",
                             takefocus=True,
                             maximum=100)
        self.p.grid(row=1, column=0, sticky="E", padx=40, pady=5, ipady=0)

    def createConsole(self):  #console creation

        #textarea for logs
        self.showLogsArea = st.ScrolledText(self.contentFrame2,
                                            width=61,
                                            height=8,
                                            borderwidth=0,
                                            highlightthickness=0,
                                            background="#343634",
                                            fg="#68D9B5",
                                            font=('arial', 12, 'normal'))

        self.showLogsArea.grid(row=2, column=0, columnspan=3, pady=10, padx=20)

    def printLogs(self, log, commandDesc=''):  #inserts text in textarea
        self.statusLabel['text'] = commandDesc
        self.showLogsArea.configure(state='normal')
        # Inserting Text which is read only
        self.showLogsArea.update()
        last_char_visible = self.showLogsArea.bbox("end-1c")
        self.showLogsArea.insert(tk.INSERT, log)
        self.showLogsArea.update()
        if last_char_visible:
            self.showLogsArea.see("end")
        self.showLogsArea.configure(state='disabled')

    def runCommands(self, command,
                    commandDesc):  #core function for running commands

        #Acquiring and Releasing lock(mutex) to prevent deadlock and synchronization
        self.lock.acquire()
        self.printLogs(
            " > " + command + '\n' +
            '----------------------------------------------\n', commandDesc)

        #check for the node version
        if command == "node -v":
            result = subprocess.getoutput(f"node -v")
            result = re.search("v(\d+\.)", result).group()
            result = result[1:len(result) - 1]
            if int(result) < 10:
                self.messageWidget(
                    "Node version 10 or above not detected! first install node with version 10 or above",
                    "error")
                self.contentFrame2.destroy()
                os.killpg(os.getpgid(result.pid), signal.SIGTERM)
                self.cleanDir()
                self.createFirstWindow()
        cdCommand = {'linux': 'cd ', 'windows': 'cd /d '}
        status, result = subprocess.getstatusoutput(
            f"{cdCommand[self.getOS]}{self.path}&&{command}")
        if status != 0:
            self.messageWidget(f"Exit status : {status} \n{result}", "error")
            self.cleanDir()
            self.contentFrame2.destroy()
            #self.cleanDir()
            self.createFirstWindow()
        self.p.step(99 // len(self.commandList))
        self.printLogs(result + '\n')
        self.counter += 1
        if self.counter >= len(self.commandList):
            self.generateFiles()
        self.lock.release()

    def fetchCommands(self):  #fetch commands from commands.json
        #multithreading synchronization lock
        self.lock = threading.Lock()

        #reading json
        cmdFile = open(f"{content.absolutePath}commands.json", "r")
        commandJsonObject = json.load(cmdFile)
        self.commandList = commandJsonObject["linux"]['commands']
        self.counter = 0
        for cmd in self.commandList:
            t1 = threading.Thread(target=self.runCommands,
                                  args=(cmd, self.commandList[cmd]))
            t1.daemon = True
            t1.start()
            #Here intentionally not joining the processes after t1.start()

    def messageWidget(self, message, mtype):  #message widget
        if mtype == 'info':
            messagebox.showinfo("Information", message)
        elif mtype == "error":
            messagebox.showerror("Opps An Error Occured", message)
        elif mtype == "warning":
            messagebox.showwarning("Warning", message)

    def generateFiles(self):  #generating static files
        dirPath, name, version, description = self.dirPath, self.name, self.version, self.description
        projectDir = self.path
        #======================= generating files with content========================================
        filedict = {
            '.gitignore': content.git,
            '.prettierrc.json': content.pretty,
            '.eslintrc.json': content.eslint,
            'src/index.html': content.indexHtml,
            'src/App.js': content.appJs,
            'src/style.css': content.styleCss
        }
        os.mkdir(os.path.join(projectDir, 'src'))
        #os.system(f"mkdir {projectDir}/src")
        for fil in filedict:
            with open(f"{projectDir}/{fil}", '+w') as rw:
                rw.write(filedict[fil])

        #==================== updating package file =========

        #=== package.json
        jsonFile = open(f"{projectDir}/package.json", "r")
        json_object = json.load(jsonFile)
        jsonFile.close()

        jsonFile = open(f"{projectDir}/package.json", "w")
        json_object['name'] = name
        json_object['version'] = version
        json_object['description'] = description
        json_object['scripts'][
            'format'] = "prettier \"src/**/*.{js,html}\" --write"
        json_object['scripts']['lint'] = "eslint \"src/**/*.{js,jsx}\" --quiet"
        json_object['scripts']['dev'] = "parcel src/index.html"

        json.dump(json_object, jsonFile, indent=2)
        jsonFile.close()

        #===== package-lock.json
        jsonFile = open(f"{projectDir}/package-lock.json", "r")
        json_object = json.load(jsonFile)
        jsonFile.close()

        jsonFile = open(f"{projectDir}/package-lock.json", "w")
        json_object['name'] = name
        json_object['version'] = version

        json.dump(json_object, jsonFile, indent=2)
        jsonFile.close()
        self.statusLabel['text'] = "installation finished!"
        self.printLogs("\nHappy Coding \n")
        self.taskDone = True
        self.contentFrame2.after(4000, self.createThirdWindow)

    # ===================================== ++++++++++++++++++++++++++ =================================== #
    # ============================================  Third window ======================================== #
    # ===================================== ++++++++++++++++++++++++++ =================================== #

    def createThirdWindow(self):  #initialization
        self.contentFrame2.destroy()
        self.contentFrame3 = Frame(self.window,
                                   background="#262625",
                                   highlightbackground="#42B3D2",
                                   highlightthickness=1,
                                   relief=tk.RAISED,
                                   borderwidth=0)
        self.contentFrame3.grid(row=1, column=0, sticky="NS", pady=10, padx=0)
        self.showInfo()

    def showInfo(self):
        # ================================= creation and placements of decorative images ========================== #
        self.tickImage = Canvas(self.contentFrame3,
                                width=40,
                                height=40,
                                background="#262625",
                                bd=0,
                                highlightthickness=0)
        self.tickImage.grid(row=0, column=0, sticky=tk.W, padx=14)
        self.img3 = ImageTk.PhotoImage(
            Image.open(f"{content.absolutePath}images/tick.png"))
        self.tickImage.create_image(20, 20, image=self.img3)
        Label(self.contentFrame3,
              text="React Project has been created successfully",
              background="#262625",
              foreground="#c7c9c7",
              font=("", 12)).grid(row=0,
                                  column=0,
                                  sticky=tk.W + tk.N,
                                  padx=50,
                                  pady=13)
        Label(self.contentFrame3,
              text=f"Project Directory : {self.path}",
              background="#262625",
              foreground="#c7c9c7",
              font=("", 10)).grid(row=1,
                                  column=0,
                                  sticky=tk.W + tk.N,
                                  padx=20,
                                  pady=7)
        Label(self.contentFrame3,
              text="npm run format",
              background="#1a1919",
              foreground="#68D9B5",
              width=72,
              font=("Times New Roman", 12)).grid(row=2,
                                                 column=0,
                                                 sticky=tk.W,
                                                 padx=20,
                                                 pady=7)
        Label(
            self.contentFrame3,
            text=
            "> Run this command to format whole project's source code. You can always change this format settings in '.prettierrc.json' file ",
            background="#262625",
            foreground="#c7c9c7",
            font=("", 10),
            wraplength=616,
            justify="left").grid(
                row=3,
                column=0,
                sticky=tk.W + tk.N,
                padx=20,
            )
        Label(self.contentFrame3,
              text="npm run lint -- --fix",
              background="#1a1919",
              foreground="#68D9B5",
              width=72,
              font=("Times New Roman", 12)).grid(row=4,
                                                 column=0,
                                                 sticky=tk.W,
                                                 padx=20,
                                                 pady=7)
        Label(
            self.contentFrame3,
            text=
            "> Run this command to fix all auto-fixable errors. You can always change lint settings in '.eslintrc.json' file",
            background="#262625",
            foreground="#c7c9c7",
            font=("", 10),
            wraplength=616,
            justify="left").grid(
                row=5,
                column=0,
                sticky=tk.W + tk.N,
                padx=20,
            )

        Label(self.contentFrame3,
              text="npm run dev",
              background="#1a1919",
              foreground="#68D9B5",
              width=72,
              font=("Times New Roman", 12)).grid(row=6,
                                                 column=0,
                                                 sticky=tk.W,
                                                 padx=20,
                                                 pady=7)
        Label(
            self.contentFrame3,
            text=
            "> Run this command to start development server with babel. Parcel web bundler is pre-configured.",
            background="#262625",
            foreground="#c7c9c7",
            font=("", 10),
            wraplength=616,
            justify="left").grid(
                row=7,
                column=0,
                sticky=tk.W + tk.N,
                padx=20,
            )

        #cancle button to terminate process
        self.QuitBtn = tk.Button(self.contentFrame3,
                                 borderwidth=0,
                                 text="Finish",
                                 highlightthickness=0,
                                 background="#bf1d1d",
                                 activebackground="#2b2b2a",
                                 cursor="hand1",
                                 font=("", 12),
                                 foreground="white",
                                 command=lambda: self.window.destroy()).grid(
                                     row=10,
                                     column=0,
                                     sticky=tk.W,
                                     pady=10,
                                     padx=280)

    def close(self):  #warning
        if messagebox.askyesno("Are you sure?",
                               "Do you really want to exit this application?"):
            if not self.taskDone:
                self.cleanDir()
            self.window.destroy()

    def cleanDir(
            self):  #cleaning project directory if installation is unsuccessful
        try:
            os.system(f"rm -r {self.dirPath}/{self.name}")
        except:
            pass
Пример #7
0
class WorkTimer:
    def __init__(self, master):
        # initialize the master
        self.master = master
        master.title("SDL Work Timer")

        # timer counter
        self.timers = 0

        # master dictionary
        # this will contain indexed dictionaries representing instances of
        # the work timer.  Those dictionaries will contain timers so that the clocks remain separate.
        '''
		{
			'notebook1': {
				'masterframe': masterframe, # this is the frame containing the entire timer
				'timer': timerobject, # this is the timer object, holding timer
									  # functions and numbers
				'toggle_button', timer_toggler, # needs to be accessible to alter color
				'date': date_label,
				'hours': hour_label,
				'mins': min_label,
				'secs': second_label,
				'desc': short_text,

				'filename': filename_entry,
				'todo': todo_editor,
				'done': done_editor,
				'notes': notes_editor,
			},
		}
		'''
        self.timer_dict = {}

        self.timerframe = Frame(self.master, padx=10, pady=10)
        self.systemframe = Frame(self.master, padx=10, pady=10)

        self.timer_notebook = Notebook(self.timerframe)

        self.create_timer(self.timer_dict, self.timer_notebook)

        # <> system frame elements <>
        # instantiate system frame elements
        self.new_timer_button = Button(
            self.systemframe,
            width=10,
            text='New timer',
            command=lambda: self.create_timer(self.timer_dict, self.
                                              timer_notebook))
        self.system_label = Label(self.systemframe, text='System:', anchor=W)
        self.system_output = Label(self.systemframe,
                                   text='Loading...',
                                   anchor=W)
        self.system_copyright = Label(
            self.systemframe,
            text='Designed and Developed by Stardust Labs',
            anchor=E)

        # grid system frame elements

        self.system_label.grid(row=0, column=0, sticky=W)
        self.system_output.grid(row=0, column=1)
        self.new_timer_button.grid(row=0, column=2)
        self.system_copyright.grid(row=1, column=0, columnspan=3, sticky=E)

        # grid master elements
        self.timer_notebook.grid()
        self.timerframe.grid(row=0, column=0)
        self.systemframe.grid(row=1, column=0)

        self.tick()

        self.system_output.config(text='Ready')

    def create_timer(self, timer_dict, notebook):
        # name the timer and instantiate it within the master dictionary
        self.timers += 1
        name = 'notebook' + str(self.timers)
        self.timer_dict[name] = {}
        this_timer = self.timer_dict[name]
        this_timer['id'] = self.timers

        # initialize dictionary abstractions
        this_timer['masterframe'] = Frame(notebook, padx=5, pady=5)
        masterframe = this_timer['masterframe']
        this_timer['timer'] = Timer()

        # frames used to contain timer elements
        timerframe = Frame(masterframe, padx=10, pady=10)
        textframe = Frame(masterframe, padx=10, pady=10)

        # <> Timerframe Elements <>

        # instantiate timer buttons
        this_timer['toggle_button'] = Button(
            timerframe,
            width=9,
            text="Start",
            background='#f00',
            command=lambda: this_timer['timer'].toggle_timer(this_timer))
        timer_reset_button = Button(
            timerframe,
            width=9,
            text='Reset',
            command=lambda: this_timer['timer'].reset(this_timer))
        save_timelog_button = Button(
            timerframe,
            width=9,
            text='Save',
            command=lambda: self.save_timelog(this_timer))

        # container frame for output, will be inline with other items
        timer_outputframe = Frame(timerframe)

        # instantiate output items
        date_label = Label(timer_outputframe, text='Date:', anchor=W)
        this_timer['date'] = Label(timer_outputframe,
                                   text=str(this_timer['timer'].output_date))
        hour_label = Label(timer_outputframe, text='Hour:', anchor=W)
        this_timer['hours'] = Label(timer_outputframe,
                                    text=str(this_timer['timer'].output_hour))
        min_label = Label(timer_outputframe, text='Min:', anchor=W)
        this_timer['mins'] = Label(timer_outputframe,
                                   text=str(this_timer['timer'].output_min))
        sec_label = Label(timer_outputframe, text='Sec:', anchor=W)
        this_timer['secs'] = Label(timer_outputframe,
                                   text=str(this_timer['timer'].output_sec))

        # grid output items to timer_outputframe so it can be grid later
        date_label.grid(row=0, column=0, sticky=W)
        hour_label.grid(row=1, column=0, sticky=W)
        min_label.grid(row=2, column=0, sticky=W)
        sec_label.grid(row=3, column=0, sticky=W)
        this_timer['date'].grid(row=0, column=1, sticky=E)
        this_timer['hours'].grid(row=1, column=1, sticky=E)
        this_timer['mins'].grid(row=2, column=1, sticky=E)
        this_timer['secs'].grid(row=3, column=1, sticky=E)

        # instantiate description label and entry
        description_label = Label(timerframe, text='Desc:', anchor=E)
        this_timer['desc'] = Entry(timerframe)

        def change_tab(event,
                       self=self,
                       tab_id=this_timer['id'] - 1,
                       entry=this_timer['desc']):
            return self.update_tab(event, tab_id, entry)

        this_timer['desc'].bind('<KeyRelease>', change_tab)

        # grid timerframe elements
        this_timer['toggle_button'].grid(row=0, column=0, pady=5)
        timer_reset_button.grid(row=1, column=0, pady=5)
        save_timelog_button.grid(row=2, column=0, pady=5)
        timer_outputframe.grid(row=3, column=0, pady=5)
        description_label.grid(row=4, column=0, sticky=W)
        this_timer['desc'].grid(row=5, column=0)

        # <> Textframe Elements <>
        filename_frame = Frame(textframe)
        text_editor_frame = Notebook(textframe)
        text_system_frame = Frame(textframe)

        # instantiate filename_frame elements
        filename_label = Label(filename_frame, text='Filename:')
        this_timer['filename'] = Entry(filename_frame, width=50)

        # grid filename_frame elements
        filename_label.grid(row=0, column=0)
        this_timer['filename'].grid(row=0, column=1)

        # instantiate text editors
        this_timer['todo'] = Text(text_editor_frame,
                                  font=('consolas', '10'),
                                  width=75,
                                  height=17)
        this_timer['done'] = Text(text_editor_frame,
                                  font=('consolas', '10'),
                                  width=75,
                                  height=17)
        this_timer['notes'] = Text(text_editor_frame,
                                   font=('consolas', '10'),
                                   width=75,
                                   height=17)

        # grid text editors and add them to the notebook
        this_timer['todo'].grid(padx=5)
        this_timer['done'].grid(padx=5)
        this_timer['notes'].grid(padx=5)
        text_editor_frame.add(this_timer['todo'], text='TODO')
        text_editor_frame.add(this_timer['done'], text='DONE')
        text_editor_frame.add(this_timer['notes'], text='NOTES')

        # instantiate text system elements
        text_save_button = Button(text_system_frame,
                                  text='Save Report',
                                  command=lambda: self.save_report(this_timer),
                                  anchor=E)
        text_load_button = Button(text_system_frame,
                                  text='Load Report',
                                  command=lambda: self.load_report(this_timer),
                                  anchor=E)

        # grid text system elements
        text_save_button.grid(row=0, column=0, padx=5, pady=5, sticky=E)
        text_load_button.grid(row=0, column=1, padx=5, pady=5, sticky=E)

        # instantiate notebook delete button
        timer_delete_button = Button(
            text_system_frame,
            text='Delete Timer',
            command=lambda: self.destroy_timer(this_timer),
            anchor=E)

        # grid notebook delete button
        timer_delete_button.grid(row=0, column=2, padx=5, pady=5, sticky=E)

        # grid all textframe internal frames
        filename_frame.grid(row=0, column=0)
        text_editor_frame.grid(row=1, column=0)
        text_system_frame.grid(row=2, column=0)

        # grid submaster frames
        timerframe.grid(row=0, column=0)
        textframe.grid(row=0, column=1)

        # add the timer to the main notebook
        this_timer['masterframe'].grid()
        notebook.add(this_timer['masterframe'], text='New Timer')

    def destroy_timer(self, timer):
        name = timer['desc'].get()
        title = 'Deleting timer {timer_name}'.format(timer_name=name)
        message = 'Are you sure you want to delete {timer_name}?'.format(
            timer_name=name)
        destroy = messagebox.askquestion(title, message)
        if destroy:
            self.timer_notebook.forget(timer['masterframe'])

    def save_timelog(self, timer):
        self.system_update(
            'Logging {desc} time...'.format(desc=timer['desc'].get()))
        description = timer['desc'].get()

        time = '{date} {h}:{m}:{s} - {desc}\r\n'.format(
            date=timer['timer'].output_date,
            h=timer['timer'].output_hour,
            m=timer['timer'].output_min,
            s=timer['timer'].output_sec,
            desc=description,
        )

        with open('timelog.txt', 'a') as timelog:
            timelog.write(time)

        self.system_update(
            '{desc} time logged.'.format(desc=timer['desc'].get()))

    def save_report(self, timer):
        self.system_update(
            'Saving {desc} report...'.format(desc=timer['desc'].get()))
        todo_header = '---=== TODO ===---\r\n\r\n'
        done_header = '---=== DONE ===---\r\n\r\n'
        notes_header = '---=== NOTES ===---\r\n\r\n'

        todo_body = timer['todo'].get('1.0', END)
        done_body = timer['done'].get('1.0', END)
        notes_body = timer['notes'].get('1.0', END)

        output_text = '{todo_h}{todo}\r\n\r\n{done_h}{done}\r\n\r\n{notes_h}{notes}'.format(
            todo_h=todo_header,
            todo=todo_body,
            done_h=done_header,
            done=done_body,
            notes_h=notes_header,
            notes=notes_body,
        )

        filename = timer['filename'].get()

        with open('reports/' + filename + '.txt', 'w') as report:
            report.write(output_text)

        self.system_update(
            '{desc} report saved.'.format(desc=timer['desc'].get()))

    def load_report(self, timer):
        self.system_update(
            'Loading {file}'.format(file=timer['filename'].get()))

        filename = timer['filename'].get()

        with open(filename + '.txt', 'r') as report:
            input_text = report.read()

            input_text = input_text.split('---=== DONE ===---')
            todo_text = input_text[0]
            input_text = input_text[1].split('---=== NOTES ===---')
            done_text = input_text[0]
            notes_text = input_text[1]

            todo_text = todo_text.replace('---=== TODO ===---',
                                          '').replace('\r\n', '')
            done_text = done_text.replace('\r\n', '')
            notes_text = notes_text.replace('\r\n', '')

            timer['todo'].delete('1.0', END)
            timer['todo'].insert(END, todo_text)
            timer['done'].delete('1.0', END)
            timer['done'].insert(END, done_text)
            timer['notes'].delete('1.0', END)
            timer['notes'].insert(END, notes_text)

        self.system_update(
            '{file} loaded.'.format(file=timer['filename'].get()))

    def tick(self):
        for work_timer in self.timer_dict:
            self.timer_dict[work_timer]['timer'].tick(
                self.timer_dict[work_timer])
        self.systemframe.after(1000, self.tick)

    def update_tab(self, event, tab_id, entry_widget):
        updated_title = entry_widget.get()
        self.timer_notebook.tab(tab_id, text=updated_title)

    def system_update(self, sys_text):
        self.system_output.config(text=sys_text)
Пример #8
0
class Set:
    def __init__(self, parent):
        self.parent = parent
        parent.title("Set")
        parent.iconbitmap("assets/cardsIcon.ico")
        #parent.config(bg="#384d9c")

        ################### Instance Variables ###################
        self.numbers = {1: "one", 2: "two", 3: "three"}
        self.colors = {"r": "red", "g": "green", "p": "purple"}
        self.shadings = {"e": "non", "h": "half", "f": "fully"}
        self.shapes = {"S": "squiggle", "D": "diamond", "O": "oval"}
        self.deck = [
            Card(w, x, y, z) for w in self.numbers for x in self.colors
            for y in self.shadings for z in self.shapes
        ]
        # field is defined at the bottom since it requires the tk frame it's in
        self.helds = set()
        self.justDealt = False
        self.solutions = []
        self.puzField = []
        self.puzSols = []
        self.score = 0
        self.setsOnBoard = 0
        self.gameState = 0  #0 is no game currently being played, 1 is solitaire, 2 is puzzle
        self.startTime = 0
        self.imgDict = {
            str(w) + x + y + z:
            PhotoImage(file="assets/" + str(w) + x + y + z + ".png")
            for w in self.numbers for x in self.colors for y in self.shadings
            for z in self.shapes
        }
        self.empty = PhotoImage(file="assets/empty.png")

        ########################## GUI ##########################
        self.gameFrame = Frame(parent)
        self.fieldFrame = LabelFrame(self.gameFrame, text="")
        # the foundSets LabelFrame for puzzle mode will also go here

        self.lowerFrame = Frame(parent)
        self.foundSets = LabelFrame(self.lowerFrame, text="Sets Found")
        self.messageFrame = Frame(self.lowerFrame)
        self.message = Label(self.messageFrame, text="")
        self.msgUpdate = self.messageFrame.after(1, self.resetMessage, "")
        self.scoreboard = Label(self.lowerFrame, text="")
        self.deckStatus = Label(self.lowerFrame, text="")
        self.solsButton = Button(self.lowerFrame,
                                 text="Show current solutions.",
                                 command=self.showSolutions)
        self.field = [[
            Spot(
                i, j, None,
                Button(self.fieldFrame,
                       image=self.empty,
                       bg="white",
                       command=lambda i=i, j=j: self.bClick(i, j)))
            for j in range(7)
        ] for i in range(3)]
        #This is the game board, 21 spots max. A set is unavoidable with 20 spots. It is redefined in the startSolit method.
        #For puzzle mode the field will have only 4 columns and a seperate widget showing the found sets will be gridded into the fieldFrame ___TODO____

        self.solitButton = Button(self.lowerFrame,
                                  text="Start/Restart a Solitaire Game",
                                  command=lambda: solit.start(self))
        self.puzzleButton = Button(self.lowerFrame,
                                   text="Start/Restart a Puzzle Game",
                                   command=lambda: puz.start(self))

        self.gameFrame.pack()
        self.fieldFrame.grid(row=0, column=0, padx=40)  #.grid(row=0, column=0)
        for y in range(
                3
        ):  #grid 12 of the 21 possible field buttons, additional buttons will get gridded in only if they have a card
            for x in range(4):
                self.field[y][x].button.grid(row=y, column=x)

        self.lowerFrame.pack()
        self.messageFrame.pack()
        self.message.pack()
        self.scoreboard.pack()
        self.deckStatus.pack()
        self.solsButton.pack()
        self.solitButton.pack()
        self.puzzleButton.pack(pady=(0, 40))

    ######################## METHODS ########################
    def gameOver(self):
        timeDif = datetime.datetime.now(
        ) - self.startTime  # datetime timedelta object
        finTime = "Finished in {} minute{}, {}.{} second{}.".format(
            timeDif.seconds // 60,
            "s" if timeDif.seconds // 60 != 1 else "",
            timeDif.seconds % 60,
            str(timeDif)
            [8:
             11],  #this truncates to milliseconds, doesn't round, shouldn't ever really be an issue
            "s" if timeDif.seconds > 0 else "")
        for x in self.field:
            for y in x:
                y.button.config(state=DISABLED)
        if self.gameState == 1:
            self.message.config(text="No sets and deck is empty, game over! " +
                                finTime)
        else:
            self.message.config(text="All sets found, game over! " + finTime)
        self.gameState = 0

    def start_shared(self):
        self.deck = [
            Card(w, x, y, z) for w in self.numbers for x in self.colors
            for y in self.shadings for z in self.shapes
        ]
        self.helds = set()
        self.score = 0

        if self.buttonsInField() != 12:
            for button in self.fieldFrame.winfo_children():
                button.grid_forget()  #ungrid the buttons in the field
            for x in range(
                    3
            ):  #grid 12 of the 21 possible field buttons, additional buttons will get gridded in only if they have a card
                for y in range(4):
                    self.field[x][y].button.grid(row=x, column=y)
        for row in self.field:
            for spot in row:
                spot.card = None
                spot.button.config(state=NORMAL)
        self.startTime = datetime.datetime.now()

    def sets_finder(self, cardsList):
        self.solutions = []
        self.setsOnBoard = 99
        sets = []

        for i in cardsList:
            for j in cardsList:
                if j != i:
                    for k in cardsList:
                        if k != j and k != i and {
                                i, j, k
                        } not in sets and self.is_set([i, j, k]):
                            sets.append({i, j, k})
                            self.solutions.append({i, j, k})
        if self.gameState == 1:
            self.setsOnBoard = len(sets)
            solit.sets_finder_handler(self, self.setsOnBoard)
        if self.gameState == 2:
            return sets

    def is_set(self, givenCards, extraInfo="no"):
        """takes a set of exactly 3 card objects, extraInfo if called from having full helds in checkSet()), not if called from setsFinder()"""
        attrSwitcher = {
            "number": {x.number
                       for x in givenCards},
            "color": {x.color
                      for x in givenCards},
            "shading": {x.shading
                        for x in givenCards},
            "shape": {x.shape
                      for x in givenCards}
        }

        if extraInfo == "yes":
            for attribute in attrSwitcher:
                if len(attrSwitcher.get(attribute)) == 2:
                    falsRay = [False, "two of", "one of", attribute]

                    notSetAtts = [
                        eval(
                            "self." + attribute + "s.get(card." + attribute +
                            ")", {
                                "self": self,
                                "card": card
                            }) for card in givenCards
                    ]
                    #the above is hacky but the easiest way to reconcile with the dictionaries
                    print(notSetAtts)
                    for x in notSetAtts:
                        if notSetAtts.count(x) == 2 and x not in falsRay:
                            falsRay[1] = str(x)
                        if notSetAtts.count(x) == 1:
                            falsRay[2] = str(x)
                    print(falsRay)
                    return falsRay  #to show reason why it's not a set
            return [True]
        else:
            for attribute in attrSwitcher:
                if len(attrSwitcher.get(attribute)) == 2:
                    return False
            return True

    def checkSet(
        self
    ):  #TODO ___ this is convoluted, maybe store spots in helds instead of cards?
        infoRay = self.is_set(self.helds, extraInfo="yes")
        if infoRay[0]:
            if self.gameState == 1:
                solit.found_a_set(self)
            else:
                puz.found_a_set(self)
        else:
            #Make a grammatically correct string explaining why helds is not a set. Handles a few possible cases.
            #REMEMBER infoRay = [False, "two of", "one of", attribute]
            self.messageFrame.after_cancel(self.msgUpdate)
            whyNot = "two cards {} {} and the other {} {}."
            badAttr = infoRay[3]
            whyNotSwitcher = {
                "number": " symbol",
                "color": "",
                "shading": "-shaded",
                "shape": ""
            }
            switchText = whyNotSwitcher.get(badAttr)
            isNums = badAttr == "number"
            isShapes = badAttr == "shape"
            edgeCase = False

            if isShapes:
                for card in self.helds:
                    print(self.shapes[card.shape])
                    #print(infoRay[2])
                    if self.shapes[card.shape] == infoRay[
                            2] and card.number == 1:  #if the bat attr is shapes and the "one off" is a singular shape
                        edgeCase = True
                        print("edge case")
                        break
            if edgeCase:
                part4 = "a{} ".format("n" if infoRay[2] ==
                                      "oval" else "") + infoRay[2] + switchText
            elif isNums and infoRay[2] != "one" or isShapes:
                part4 = infoRay[2] + switchText + "s"
            else:
                part4 = infoRay[2] + switchText
            self.message.config(text="Not a set, " + whyNot.format(
                "have" if isNums or isShapes else "are", infoRay[1] +
                switchText + "s"
                if isNums and infoRay[1] != "one" or isShapes else infoRay[1] +
                switchText, "has" if isNums or isShapes else "is", part4))
            for x in self.field:
                for y in x:
                    if y.card in self.helds:
                        y.button.config(
                            bg="white"
                        )  #this is one reason to make helds spots instead of cards
            self.msgUpdate = self.messageFrame.after(
                5000, self.resetMessage, "There are " + str(self.setsOnBoard) +
                " sets on the board right now." if self.gameState == 1 else
                str(self.score) + " of 6 sets found.")
        self.helds = set()

    ##########################################################################################

    def bClick(self, i, j):
        if self.field[i][j].card not in self.helds:
            self.field[i][j].button.config(bg="orange")
            self.helds.add(self.field[i][j].card)
            if len(self.helds) > 2:
                self.checkSet()
        else:
            self.field[i][j].button.config(bg="white")
            self.helds.remove(self.field[i][j].card)

    def dealTo(self, i, j):
        self.field[i][j].card = self.deck.pop()
        self.update_card(i, j)

    def update_card(self, i, j):
        """updates the button image"""
        self.field[i][j].button.config(
            bg="white"
        )  #this is redundant for some calls but better than putting it everywhere
        self.field[i][j].button.config(
            image=self.imgDict[str(self.field[i][j].card.number) +
                               self.field[i][j].card.color +
                               self.field[i][j].card.shading +
                               self.field[i][j].card.shape])

    def showSolutions(self):
        msgText = ""
        for sol in self.solutions:
            for card in sol:
                msgText += str.capitalize(self.numbers[card.number] + " " +
                                          self.colors[card.color] + " " +
                                          self.shadings[card.shading] +
                                          "-shaded " +
                                          self.shapes[card.shape] + ", ")
            msgText = msgText[:-2]
            msgText += ".\n"
        self.message.config(text=msgText)

    def buttonsInField(self):
        i = 0
        for x in self.field:
            for y in x:
                if y.button.winfo_ismapped():
                    i += 1
        return i

    def resetMessage(self, msgText):
        """This only needs to be a separate method for timed event handling purposes"""
        self.message.config(text=msgText)