コード例 #1
0
ファイル: display.py プロジェクト: dantanq/tkd-scoreboard
class Control(Frame):
    def __init__(self):
        # initialize RED and BLUE fighters
        self.RED = 0
        self.BLUE = 1

        # initialize score and kyonggo variables
        self.redPoints = 0
        self.bluePoints = 0
        self.redKyonggo = 0
        self.blueKyonggo = 0
        self.currentRound = 0
        self.display = None
        self.miniDisplay = None
        self.numRounds = 0
        self.timer = None
        self.isSuddenDeath = False
        self.callNextRound = True
        try:
            # for Python3
            super().__init__()
        except:
            # for Python2
            Frame.__init__(self)

        # set title and default style
        self.master.title("TKD Scoring System")
        # create style
        self.s = Style()
        self.s.configure("TButton", padding=10, font=(None, 20))
        self.s.configure("TCheckbutton", padding=10, font=(None, 20))
        self.s.configure("TOptionMenu", padding=10, font=(None, 20))
        self.pack(fill=BOTH, expand=True)
        # create setup frames, labels, and entries
        # time entry frame
        self.setTimeFrame = Frame(self)
        self.timerLabel = Label(self.setTimeFrame,
                                text="Time:",
                                font=(None, 20))
        self.secondsEntry = Entry(self.setTimeFrame, width=3, font=(None, 20))
        self.colonLabel = Label(self.setTimeFrame, text=":", font=(None, 20))
        self.minuteEntry = Entry(self.setTimeFrame, width=3, font=(None, 20))
        # round entry frame
        self.roundsFrame = Frame(self)
        self.roundsLabel = Label(self.roundsFrame,
                                 text="Number of Rounds:",
                                 font=(None, 20))
        self.roundsEntry = Entry(self.roundsFrame, width=3, font=(None, 20))
        # serial entry frame
        self.serialFrame = Frame(self)
        try:
            self.arduino_ports = ["None"] + [
                p.device for p in serial.tools.list_ports.comports()
            ]
        except:
            # if serial is not installed
            self.arduino_ports = ["None"]
        self.serialEntry = StringVar()
        self.serialEntry.set("None")
        self.serialLabel = Label(self.serialFrame,
                                 text="Serial Input:",
                                 font=(None, 20))
        self.serialCheck = OptionMenu(self.serialFrame, self.serialEntry,
                                      "None", *self.arduino_ports)
        self.createMatchButton = Button(self,
                                        text="Create Match",
                                        style="TButton",
                                        command=self.hideSetup)

        # initialize frames for UI
        # red frame and buttons
        self.redFrame = Frame(self)
        self.redScoreButton = Button(
            self.redFrame,
            text="Red +",
            style="TButton",
            command=lambda: self.incrementPoints(self.RED))
        self.redDeletePoint = Button(
            self.redFrame,
            text="Red -",
            style="TButton",
            command=lambda: self.deductPoints(self.RED))
        self.redKyonggoButton = Button(
            self.redFrame,
            text="Kyonggo +",
            style="TButton",
            command=lambda: self.callKyonggo(self.RED))
        self.redKyonggoDelete = Button(
            self.redFrame,
            text="Kyonggo -",
            style="TButton",
            command=lambda: self.deductKyonggo(self.RED))
        # blue frame and buttons
        self.blueFrame = Frame(self)
        self.blueScoreButton = Button(
            self.blueFrame,
            text="Blue +",
            style="TButton",
            command=lambda: self.incrementPoints(self.BLUE))
        self.blueDeletePoint = Button(
            self.blueFrame,
            text="Blue -",
            style="TButton",
            command=lambda: self.deductPoints(self.BLUE))
        self.blueKyonggoButton = Button(
            self.blueFrame,
            text="Kyonggo +",
            style="TButton",
            command=lambda: self.callKyonggo(self.BLUE))
        self.blueKyonggoDelete = Button(
            self.blueFrame,
            text="Kyonggo -",
            style="TButton",
            command=lambda: self.deductKyonggo(self.BLUE))
        # reset and new match frame and buttons
        self.resetFrame = Frame(self)
        self.startStop = StringVar()
        self.timerStartStop = Button(self.resetFrame,
                                     textvariable=self.startStop,
                                     style="TButton",
                                     command=self.timerPush)
        self.startStop.set("Start Round 1")
        self.newMatch = Button(self.resetFrame,
                               text="New Match",
                               style="TButton",
                               command=self.newMatch)
        self.resetMatch = Button(self.resetFrame,
                                 text="Reset Match",
                                 style="TButton",
                                 command=self.resetMatch)

        self.setup()

    # displays setup frames
    def setup(self):
        # timer frame
        self.setTimeFrame.pack(fill=X)
        # timer label and entry
        self.timerLabel.pack(side=LEFT, padx=5, pady=5)
        self.secondsEntry.pack(side=RIGHT)
        self.colonLabel.pack(side=RIGHT)
        self.minuteEntry.pack(side=RIGHT)

        # frame for number of rounds
        self.roundsFrame.pack(fill=X)
        # number of rounds label and entry
        self.roundsLabel.pack(side=LEFT, padx=5, pady=5)
        self.roundsEntry.pack(side=RIGHT)

        # frame for serial entry
        self.serialFrame.pack(fill=X, expand=True)
        # serial entry label and checkbox
        self.serialLabel.pack(side=LEFT, padx=5, pady=5)
        self.serialCheck.pack(side=RIGHT)

        # create match button
        self.createMatchButton.pack(side=BOTTOM)

    # hides setup widgets and initalizes timer and number of rounds
    def hideSetup(self):
        # check if minutes, seconds, and round entries are valid
        if len(self.minuteEntry.get()) < 1:
            minutes = 0
        else:
            try:
                minutes = int(self.minuteEntry.get())
            except:
                minutes = 0
        if len(self.secondsEntry.get()) < 1:
            seconds = 0
        else:
            try:
                seconds = int(self.secondsEntry.get()) % 60
                minutes += int(self.secondsEntry.get()) // 60
            except:
                seconds = 0
        if len(self.roundsEntry.get()) < 1:
            numRounds = 0
        else:
            try:
                numRounds = int(self.roundsEntry.get())
            except:
                numRounds = 0
        # set up serial input if checked
        if self.serialEntry.get() != "None":
            self.serialSetup()
        else:
            self.arduino = False
        # only moves on if entries are valid
        if ((minutes != 0) or (seconds != 0)) and (numRounds != 0):
            self.roundLength = [minutes, seconds]
            self.timer = Timer(self.roundLength)
            self.numRounds = numRounds
            self.currentRound = 1
            self.isSuddenDeath = False
            self.roundsFrame.pack_forget()
            self.setTimeFrame.pack_forget()
            self.createMatchButton.pack_forget()
            self.serialFrame.pack_forget()
            self.initUI()

    # set up serial input
    def serialSetup(self):
        try:
            self.arduino = True
            self.serialPort = self.serialEntry.get()
            self.baudRate = 9600
            self.ser = serial.Serial(self.serialPort,
                                     self.baudRate,
                                     timeout=0,
                                     writeTimeout=0)
            self.ser.flushInput()
        except:
            self.arduino = False
            print("Could Not Complete Serial Port Set Up")

    # creates user interface
    def initUI(self):
        # create display
        if self.display == None:
            self.display = Display(self.timer)
            self.display.attributes('-fullscreen', True)
        else:
            self.display.newTimer(self.timer)
            self.display.updateCurrentRound("R1")
        if self.miniDisplay == None:
            self.miniDisplay = miniDisplay(self.timer)
        else:
            self.miniDisplay.newTimer(self.timer)
            self.miniDisplay.updateCurrentRound("R1")

        # red point and kyonggo buttons
        self.redFrame.pack(fill=BOTH, side=LEFT)
        self.redScoreButton.pack(padx=5, pady=5, fill=X)
        self.redDeletePoint.pack(padx=5, pady=5, fill=X)
        self.redKyonggoButton.pack(padx=5, pady=5, fill=X)
        self.redKyonggoDelete.pack(padx=5, pady=5, fill=X)

        # blue point and kyonggo buttons
        self.blueFrame.pack(fill=BOTH, side=RIGHT)
        self.blueScoreButton.pack(padx=5, pady=5, fill=X)
        self.blueDeletePoint.pack(padx=5, pady=5, fill=X)
        self.blueKyonggoButton.pack(padx=5, pady=5, fill=X)
        self.blueKyonggoDelete.pack(padx=5, pady=5, fill=X)

        # timer start/stop button, reset button, and quit button
        self.resetFrame.pack(side=BOTTOM)
        self.startStop.set("Start Round " + str(self.currentRound))
        self.timerStartStop.pack(side=TOP, pady=5)
        self.newMatch.pack(side=LEFT, padx=5)
        self.resetMatch.pack(side=RIGHT, padx=5)

    def timerPush(self):
        # if round is over, reset time give option to start next round
        if self.timer.timeLeft[0] == self.timer.timeLeft[1] == 0:
            self.timer.reset()
            self.startStop.set("Start Round " + str(self.currentRound))
            self.display.updateCurrentRound("R" + str(self.currentRound))
            self.miniDisplay.updateCurrentRound("R" + str(self.currentRound))
            self.updateDisplayTimer()
        # pause timer, give option to unpause
        elif self.timer.isRunning():
            self.timer.stop()
            self.startStop.set("Start")
        # unpause timer, give option to pause
        else:
            if self.arduino:
                self.ser.flushInput()
            self.timer.start()
            if self.arduino:
                self.readSerialInput()
            self.startStop.set("Pause")
            if not self.callNextRound:
                self.callNextRound = True
            self.updateDisplayTimer()

    def resetMatch(self):
        if not self.timer.isRunning():
            self.timer.reset()
            self.redPoints = 0
            self.bluePoints = 0
            self.redKyonggo = 0
            self.blueKyonggo = 0
            self.currentRound = 1
            if self.isSuddenDeath:
                self.isSuddenDeath = False
            self.newMatch.pack_forget()
            self.resetMatch.pack_forget()
            self.timerStartStop.pack(side=TOP, pady=5)
            self.newMatch.pack(side=LEFT, padx=5)
            self.resetMatch.pack(side=RIGHT, padx=5)
            self.startStop.set("Start Round 1")
            self.display.reset(self.timer.getTimeString())
            self.miniDisplay.reset(self.timer.getTimeString())

    def updateDisplayTimer(self):
        if self.timer.isElapsed():
            self.timer.stop()
            if self.callNextRound:
                self.nextRound()
            if self.currentRound < self.numRounds or self.redPoints == self.bluePoints:
                self.display.updateTimer(self.timer.getTimeString())
            self.miniDisplay.updateTimer(self.timer.getTimeString())
        elif self.currentRound > self.numRounds:
            self.suddenDeath()
        else:
            self.display.updateTimer(self.timer.getTimeString())
            self.miniDisplay.updateTimer(self.timer.getTimeString())
            self.after(1000, self.updateDisplayTimer)

    def nextRound(self):
        self.callNextRound = False
        self.currentRound += 1
        if self.currentRound <= self.numRounds:
            self.startStop.set("Reset Timer")
        elif self.redPoints == self.bluePoints:
            self.startStop.set("Sudden Death")
        else:
            self.declareWinner()
            self.timerStartStop.pack_forget()

    def declareWinner(self):
        if self.redPoints > self.bluePoints:
            winner = "RED"
        else:
            winner = "BLUE"
        self.display.updateTimer(winner + " WINS")
        self.display.updateCurrentRound("")

    def suddenDeath(self):
        self.redPoints = 0
        self.display.updateRedPoints(0)
        self.miniDisplay.updateRedPoints(0)
        self.bluePoints = 0
        self.display.updateBluePoints(0)
        self.miniDisplay.updateBluePoints(0)
        self.display.updateTimer("SUDDEN DEATH")
        self.miniDisplay.updateTimer("SUDDEN DEATH")
        self.display.updateCurrentRound("")
        self.miniDisplay.updateCurrentRound("")
        self.isSuddenDeath = True
        if self.arduino:
            self.readSerialInput()
        self.timerStartStop.pack_forget()

    def readSerialInput(self):
        if self.timer.isRunning() or self.isSuddenDeath:
            output = self.ser.readline()
            if len(output) != 0:
                try:
                    fighter = int(output)
                    self.incrementPoints(fighter)
                except:
                    print("Invalid Serial Input")
                self.after(250, self.readSerialInput)
            else:
                self.after(50, self.readSerialInput)

    def incrementPoints(self, fighter):
        if fighter == self.RED:
            self.redPoints += 1
            self.display.updateRedPoints(self.redPoints)
            self.miniDisplay.updateRedPoints(self.redPoints)
        elif fighter == self.BLUE:
            self.bluePoints += 1
            self.display.updateBluePoints(self.bluePoints)
            self.miniDisplay.updateBluePoints(self.bluePoints)
        if self.isSuddenDeath:
            self.declareWinner()

    def deductPoints(self, fighter):
        if fighter == self.RED and self.redPoints > 0:
            self.redPoints -= 1
            self.display.updateRedPoints(self.redPoints)
            self.miniDisplay.updateRedPoints(self.redPoints)
        elif fighter == self.BLUE and self.bluePoints > 0:
            self.bluePoints -= 1
            self.display.updateBluePoints(self.bluePoints)
            self.miniDisplay.updateBluePoints(self.bluePoints)

    def callKyonggo(self, fighter):
        if fighter == self.RED:
            self.redKyonggo += 1
            self.display.updateRedKyonggo("Kyonggo: " + str(self.redKyonggo))
            self.miniDisplay.updateRedKyonggo("Kyonggo: " +
                                              str(self.redKyonggo))
            if self.redKyonggo % 2 == 0:
                self.bluePoints += 1
                self.display.updateBluePoints(self.bluePoints)
                self.miniDisplay.updateBluePoints(self.bluePoints)
        elif fighter == self.BLUE:
            self.blueKyonggo += 1
            self.display.updateBlueKyonggo("Kyonggo: " + str(self.blueKyonggo))
            self.miniDisplay.updateBlueKyonggo("Kyonggo: " +
                                               str(self.blueKyonggo))
            if self.blueKyonggo % 2 == 0:
                self.redPoints -= 1
                self.display.updateRedPoints(self.redPoints)
                self.miniDisplay.updateRedPoints(self.redPoints)

    def deductKyonggo(self, fighter):
        if fighter == self.RED and self.redKyonggo > 0:
            self.redKyonggo -= 1
            self.display.updateRedKyonggo("Kyonggo: " + str(self.redKyonggo))
            self.miniDisplay.updateRedKyonggo("Kyonggo: " +
                                              str(self.redKyonggo))
            if self.redKyonggo % 2 == 1:
                self.redPoints += 1
                self.display.updateRedPoints(self.redPoints)
                self.miniDisplay.updateRedPoints(self.redPoints)
        elif fighter == self.BLUE and self.blueKyonggo > 0:
            self.blueKyonggo -= 1
            self.display.updateBlueKyonggo("Kyonggo: " + str(self.blueKyonggo))
            self.miniDisplay.updateBlueKyonggo("Kyonggo: " +
                                               str(self.blueKyonggo))
            if self.blueKyonggo % 2 == 1:
                self.bluePoints += 1
                self.display.updateBluePoints(self.bluePoints)
                self.miniDisplay.updateBluePoints(self.bluePoints)

    def newMatch(self):
        if not self.timer.isRunning():
            self.redPoints = 0
            self.redKyonggo = 0
            self.bluePoints = 0
            self.blueKyonggo = 0
            self.display.reset("0:00")
            self.miniDisplay.reset("0:00")
            self.hideUI()
            self.setup()

    def hideUI(self):
        self.redFrame.pack_forget()
        self.blueFrame.pack_forget()
        self.resetFrame.pack_forget()
コード例 #2
0
class App(Tk):
    def __init__(self, **kwargs):
        super(App, self).__init__(**kwargs)
        tags = list(self.bindtags())
        tags.insert(2, 'App')
        self.bindtags(tags)

        themes = ThemeEngine()
        self.option_readfile(themes.options_file)
        themes.set_ttk_style()

        self.new_reader_icon = get_icon('ic_new_reader')
        self.new_writer_icon = get_icon('ic_new_writer')
        self.close_panel_icon = get_icon('ic_close')
        self.save_icon = get_icon('ic_save')
        self.edit_icon = get_icon('ic_mode_edit')
        self.link_icon = get_icon('ic_insert_link')
        self.clear_content_icon = get_icon('ic_delete_sweep')
        self.delete_icon = get_icon('ic_delete_forever')

        img = Image.open('.resources/wm_icon.png')
        app_icon = ImageTk.PhotoImage(image=img)

        if backup_enabled():
            check = check_backup()
            if check != 1:
                # print(check)
                pass

        self.title('Meta-Jurnl')
        self.iconphoto(True, app_icon)

        self.withdraw()

        menu_bar = Menu(master=self)
        self.config(menu=menu_bar)
        # menu_bar.pack(fill='x')

        file_menu = Menu(master=menu_bar,
                         tearoff=0,
                         relief='flat',
                         borderwidth=1)
        file_menu.add_command(label='New Entry', command=self.add_writer)
        file_menu.add_command(label='New Linked Entry')

        page_menu = Menu(master=file_menu, tearoff=0)
        page_menu.add_command(label='New Reader', command=self.add_reader)
        page_menu.add_command(label='New Writer', command=self.add_writer)

        file_menu.add_cascade(label='New', menu=page_menu, underline=0)
        file_menu.add_command(label='Save')
        file_menu.add_command(label='Quit', command=self.destroy)

        edit_menu = Menu(master=menu_bar, tearoff=0)
        edit_menu.add_command(label='Preferences')

        help_menu = Menu(master=menu_bar, tearoff=0)
        help_menu.add_command(label='Keyboard Shortcuts')
        help_menu.add_command(label='About')

        menu_bar.add_cascade(label='File', menu=file_menu, underline=0)
        menu_bar.add_cascade(label='Edit', menu=edit_menu, underline=0)
        menu_bar.add_cascade(label='Help', menu=help_menu, underline=0)

        toolbar = Frame(master=self, relief='flat', borderwidth=0, padding=5)
        toolbar.pack(fill='x')

        self.journal = Journal(master=self)
        self.journal.pack(fill='both', expand=True)

        self.autoimport()

        self.new_reader = Button(master=toolbar,
                                 image=self.new_reader_icon,
                                 command=self.add_reader)
        new_writer = Button(master=toolbar,
                            image=self.new_writer_icon,
                            command=self.add_writer)

        self.close_panel = Button(master=toolbar,
                                  text='Close Tab',
                                  image=self.close_panel_icon,
                                  command=self.journal.remove_page)
        self.clear_button = Button(master=toolbar,
                                   image=self.clear_content_icon,
                                   command=self.clear_fields)

        self.save_button = Button(master=toolbar,
                                  image=self.save_icon,
                                  command=self.save_entry)
        self.edit_button = Button(master=toolbar,
                                  image=self.edit_icon,
                                  command=self.edit_entry)
        self.link_button = Button(master=toolbar,
                                  image=self.link_icon,
                                  command=self.link_entry)
        self.delete_button = Button(master=toolbar, image=self.delete_icon)

        self.new_reader.pack(side='left', padx=(0, 5))
        new_writer.pack(side='left', padx=(0, 5))
        self.close_panel.pack(side='left', padx=(0, 5))
        self.clear_button.pack(side='left', padx=(0, 5))

        self.close_panel.state(['disabled'])
        self.clear_button.state(['disabled'])

        self.journal.update_idletasks()

        s_width = self.winfo_screenwidth() / 2
        s_height = self.winfo_screenheight() / 2
        dims = dimensions()
        try:
            w_width = self.winfo_reqwidth() if not dims[0] else dims[0]
            w_height = self.winfo_reqheight() if not dims[1] else dims[1]
        except IndexError:
            w_width = self.winfo_reqwidth()
            w_height = self.winfo_reqheight()
        pos = w_width, w_height, s_width - floor(
            w_width / 2), s_height - floor(w_height / 2)
        self.geometry('{}x{}+{}+{}'.format(int(pos[0]), int(pos[1]),
                                           int(pos[2]), int(pos[3])))

        self.bind('<Configure>', self.update_dimensions)
        self.bind_all('<<Check Save Button>>', self.check_writer_buttons)
        self.bind_all('<<Id Selected>>', self.check_reader_buttons)
        self.bind('<<NotebookTabChanged>>', self.change_buttons)

        self.change_buttons()

        self.after(1000, self.deiconify)

        self.mainloop()

    def update_dimensions(self, event):
        # s_width = self.winfo_screenwidth() / 2
        # s_height = self.winfo_screenheight() / 2
        w_width = max(self.winfo_width(), 1500)
        w_height = max(self.winfo_height(), 600)
        # pos = w_width, w_height, self.winfo_rootx(), self.winfo_rooty()
        # self.geometry('{}x{}+{}+{}'.format(
        #     int(pos[0]), int(pos[1]), int(pos[2]), int(pos[3])
        # ))
        dimensions((w_width, w_height))

    def add_reader(self):
        if not database_is_empty():
            self.journal.add_page('Reader')

    def add_writer(self):
        self.journal.add_page('Writer')

    # def add_tagged_writer(self):
    #     self.journal.add_page('Writer', tags=True)

    def change_buttons(self, event=None):
        self.new_reader.state(
            ['disabled' if database_is_empty() else '!disabled'])
        if self.journal.mode_ == 'Writer':
            self.close_panel.state(['!disabled'])
            self.clear_button.state(['!disabled'])
            self.save_button.pack_forget()
            self.edit_button.pack_forget()
            self.link_button.pack_forget()
            self.delete_button.pack_forget()
            self.save_button.pack(side='left', padx=(0, 5))
            self.link_button.pack(side='left', padx=(0, 5))
            self.check_writer_buttons()
        elif self.journal.mode_ == 'Reader':
            self.close_panel.state(['!disabled'])
            self.clear_button.state(['!disabled'])
            self.save_button.pack_forget()
            self.edit_button.pack_forget()
            self.link_button.pack_forget()
            self.delete_button.pack_forget()
            self.edit_button.pack(side='left', padx=(0, 5))
            self.link_button.pack(side='left', padx=(0, 5))
            self.delete_button.pack(side='left', padx=(0, 5))
            self.check_reader_buttons()
        else:
            self.close_panel.state(['disabled'])
            self.clear_button.state(['disabled'])
            self.save_button.pack_forget()
            self.edit_button.pack_forget()
            self.link_button.pack_forget()
            self.delete_button.pack_forget()

    def check_writer_buttons(self, event=None):
        saved = self.journal.check_saved(event)
        self.save_button.state(['disabled' if saved else '!disabled'])
        self.link_button.state(
            ['disabled' if not self.journal.id_ else '!disabled'])

    def check_reader_buttons(self, event=None):
        entry = True if self.journal.id_ else False
        self.edit_button.state(['disabled' if not entry else '!disabled'])
        self.link_button.state(['disabled' if not entry else '!disabled'])
        self.delete_button.state(['disabled' if not entry else '!disabled'])

    def save_entry(self):
        self.journal.save()
        self.change_buttons()

    def edit_entry(self):
        self.journal.add_page(mode='Writer', entry_id=self.journal.id_)

    def link_entry(self):
        self.journal.add_page(mode='Writer', parent=self.journal.id_)

    def delete_entry(self):
        # TODO Implement
        pass

    def clear_fields(self):
        if self.journal.mode_ == 'Writer':
            saved = self.journal.check_saved()
            if not saved:
                if askquestion(
                        'Clear Window Contents?',
                        'There are unsaved edits in this tab.\nSave before continuing?'
                ):
                    self.save_entry()
        self.journal.clear()

    def autoimport(self):
        import_entries()
        self.journal.refresh_readers()
        if autodelete_imports():
            delete_imports()
コード例 #3
0
class SubfoldersListFrame(Frame):
    """
    Frame con Entry degli autori
    """
    def __init__(self,
                 master,
                 data_path_var,
                 add_empty_author_entry=True,
                 **kwargs):
        """
        :param master:
        :param add_empty_author_entry: se `True`, aggiungi una Entry vuota in cima, altrimenti parti senza nessuna Entry
        :param kwargs:
        """
        super(SubfoldersListFrame, self).__init__(master, **kwargs)
        self.master = master

        self._subfolder_entries = []

        self.add_button = None

        self.data_path_var = data_path_var

        # Aggiungi entry vuota se richiesto
        if add_empty_author_entry:
            self.add_subfolder()

        # Pulsante 'Aggiungi autore'
        self.add_button = Button(self,
                                 image=icons.get_icon("add.png"),
                                 text="Add subfolder",
                                 compound="left",
                                 command=self.add_subfolder)
        self.add_button.pack(fill="x", pady=2)

    def add_subfolder(self, value=""):
        """
        Aggiungi un autore

        :param value: nome dell'autore
        :return:
        """
        # Rimuovi pulsante 'Aggiungi autore', se è stato posizionato
        if self.add_button is not None:
            self.add_button.pack_forget()

        # Crea frame con Entry e pulsante rimozione e posizionali
        f = Frame(self)
        status_label = Label(f, image=icons.get_icon("warning.png"))
        ae = SubfolderEntry(StringVar(value=value), f, status_label)
        self._subfolder_entries.append(ae)
        f.grid_columnconfigure(1, weight=1)
        ae.status_label.grid(row=0, column=0, sticky="we")
        ae.var.trace("w", lambda *_: self.update_entry_status_label(ae))
        Entry(f,
              textvariable=self._subfolder_entries[-1].var).grid(row=0,
                                                                 column=1,
                                                                 sticky="we")
        Button(
            f,
            image=icons.get_icon("search.png"),
            command=lambda: ae.var.set(
                f"{self.data_path_var.get().strip()}\\{ae.var.get().strip()}")
        ).grid(row=0, column=2, sticky="e")
        Button(f,
               image=icons.get_icon("folder.png"),
               command=lambda: ae.var.set(filedialog.askdirectory().replace(
                   "/", "\\").strip())).grid(row=0, column=3, sticky="e")
        Button(f,
               image=icons.get_icon("delete.png"),
               command=lambda: self.remove_subfolder(ae)).grid(row=0,
                                                               column=4,
                                                               sticky="e")
        f.pack(fill="x", pady=1, expand=True)

        # Riaggiungi pulsante 'Aggiungi autore', se necessario
        if self.add_button is not None:
            self.add_button.pack(fill="x", pady=2)

    def is_subfolder(self, path: str) -> bool:
        data_path = self.data_path_var.get().lower().rstrip("\\").strip()
        return bool(data_path) and path.lower().strip().startswith(
            data_path) and os.path.isdir(path)

    def update_entry_status_label(self, entry: SubfolderEntry) -> None:
        entry.status_label.configure(
            image=icons.get_icon("success.png" if self.is_subfolder(
                entry.var.get()) else "warning.png"))

    def remove_subfolder(self, author_entry):
        """
        Rimuove un autore dalla lista degli autori

        :param author_entry: `AutorEntry` dell'autore da rimuovere
        :return:
        """
        author_entry.frame.pack_forget()
        self._subfolder_entries.remove(author_entry)

    @property
    def subfolders(self):
        """
        Ritorna gli autori
        :return:
        """
        return self._subfolder_entries

    @subfolders.setter
    def subfolders(self, authors):
        """
        Imposta gli autori e ricostruisce la lista dei widget

        :param authors:
        :return:
        """
        self._subfolder_entries.clear()
        for widget in self.pack_slaves():
            widget.pack_forget()
        for author in authors:
            self.add_subfolder(author)

    def reevaluate_statuses(self):
        for x in self._subfolder_entries:
            self.update_entry_status_label(x)