From 30b92bd488a911c4272641e60e0e24ffe6fd667d Mon Sep 17 00:00:00 2001 From: MarkinoTeck <93880780+MarkinoTeck@users.noreply.github.com> Date: Sun, 9 Jun 2024 21:11:01 +0200 Subject: [PATCH 1/4] some ui-changes and features - gui design: https://excalidraw.com/#json=_gYLOEMIdrXcCb3VUiZ8-,gJ0XoeU5ao8QGx4KIrMM5Q - ability to select only some files of the folder - groups (inspired by @lotsahelp) --- .gitignore | 3 +- settings-example.json | 14 +++ src/FarmUpload.py | 183 ++++++---------------------- src/Log.py | 16 --- src/gui/Main.py | 153 +++++++++++++++++++++++ src/gui/SelectSettings.py | 30 +++++ src/gui/main/FileList.py | 29 +++++ src/gui/main/PrinterList.py | 33 +++++ src/{ => logic/classes}/BambuFTP.py | 8 +- src/logic/classes/Logger.py | 13 ++ src/{ => logic/classes}/Printer.py | 15 ++- 11 files changed, 325 insertions(+), 172 deletions(-) delete mode 100644 src/Log.py create mode 100644 src/gui/Main.py create mode 100644 src/gui/SelectSettings.py create mode 100644 src/gui/main/FileList.py create mode 100644 src/gui/main/PrinterList.py rename src/{ => logic/classes}/BambuFTP.py (99%) create mode 100644 src/logic/classes/Logger.py rename src/{ => logic/classes}/Printer.py (75%) diff --git a/.gitignore b/.gitignore index dd78fd0..88a427a 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,5 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -.DS_Store \ No newline at end of file +.DS_Store +.vscode/settings.json diff --git a/settings-example.json b/settings-example.json index 2a30b2d..10afc46 100644 --- a/settings-example.json +++ b/settings-example.json @@ -2,13 +2,27 @@ "printers": [ { "name": "Printer #1", + "group": "B", "ip": "192.168.1.1", "pw": "12345678" }, { "name": "Printer #2", + "group": "A", "ip": "192.168.1.2", "pw": "12345678" + }, + { + "name": "Printer #3", + "group": "B", + "ip": "192.168.1.3", + "pw": "12345678" + }, + { + "name": "Printer #4", + "group": "A", + "ip": "192.168.1.4", + "pw": "12345678" } ] } \ No newline at end of file diff --git a/src/FarmUpload.py b/src/FarmUpload.py index fbe4d17..2ec6392 100644 --- a/src/FarmUpload.py +++ b/src/FarmUpload.py @@ -1,160 +1,57 @@ -import tkinter as tk -from tkinter import filedialog -import tkinter.scrolledtext as st -import json, os -import threading, queue +import customtkinter as CTk +from typing import Literal +from gui.SelectSettings import SelectSettings +from gui.Main import Main -from Printer import Printer -from Log import Logger +class App(CTk.CTk): -class App(): - def __init__(self): - self.settings = None - self.printers = [] - self.fileDirectory = "" - - self.ui = tk.Tk() - self.loadUI() - - self.logQueue = queue.Queue() - self.log = Logger(self.logUI) - - self.ui.after(50, self.processQueue) - - self.ui.mainloop() - - def loadSettings(self): - - settingsDirectory = filedialog.askopenfilename() - self.s.config(text=os.path.basename(settingsDirectory)) - f = open(settingsDirectory) - self.settings = json.load(f) - f.close() - - # loading printers into app printer array - - for printer in self.settings["printers"]: - var = tk.BooleanVar() - self.printers.append(Printer( - name = printer["name"], - ip = printer["ip"], - pw = printer["pw"], - enabled = var - )) - - checkbox = tk.Checkbutton(self.printerSelectFrame, text=printer["name"], variable=var, onvalue=True, offvalue=False) - checkbox.pack(side=tk.LEFT, pady=10) - - self.s.update_idletasks() - - def loadUI(self): - - self.ui.title("FarmUpload") - - w = tk.Label(self.ui, text='Select the printers to send to:') - w.pack() - - choose_settings_button = tk.Button(self.ui, text="Choose Settings JSON File", command=self.loadSettings) - choose_settings_button.pack(pady=10, padx=10) - - self.s = tk.Label(self.ui, text='No settings file selected') - self.s.pack(pady=10, padx=10) - - select_printers = tk.Label(self.ui, text="Select which printers to send to:") - select_printers.pack(pady=10) - - self.printerSelectFrame = tk.Frame(master=self.ui, highlightbackground="black", highlightthickness=1) - self.printerSelectFrame.pack() + ## design GUI (https://excalidraw.com/#json=_gYLOEMIdrXcCb3VUiZ8-,gJ0XoeU5ao8QGx4KIrMM5Q) - choose_folder_button = tk.Button(self.ui, text="Choose Folder to Upload", command=self.chooseFolder) - choose_folder_button.pack(pady=10, padx=10) - - self.v = tk.Label(self.ui, text='No directory selected') - self.v.pack(pady=10) - - send_button = tk.Button(self.ui, text="Send to Farm", command=self.send) - send_button.pack(pady=10) + def __init__(self): + super().__init__() + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + self.title("Farm Upload") + self._current_window = "" - # Logging + self.main_window = None + self.printers: dict = {} + self.printers_selected: dict = {} + self.settings_path: str = None + self.settings: dict = None + self.selected_folder: str = None + self.selected_folder_files: dict = {} - self.logUI = st.ScrolledText(self.ui) - self.logUI.pack() - - def chooseFolder(self): - self.fileDirectory = filedialog.askdirectory() - self.v.config(text=os.path.basename(self.fileDirectory)) - self.v.update_idletasks() + self.changeWindow("SelectSettings") - def processQueue(self): - try: - msg = self.logQueue.get_nowait() - self.log.write(msg) - except queue.Empty: - pass - def sendOnePrinter(self, printer, toSend): + def changeWindow(self, window_name: Literal["SelectSettings", "Main"]): # find better name to 'main' + """Select what page to show to the user. - printer.connect() - - if printer.connected: + Args: + window_name (str): page to open - self.logQueue.put("Connected to " + str(printer.name)) + Raises: + NotImplementedError: if the page is not implemented + """ + if self.main_window: + self.main_window.destroy() - for filename in toSend: - try: - with open(os.path.join(self.fileDirectory,filename), 'rb') as file: - self.logQueue.put("Sending " + str(filename) + " to printer " + printer.name + "..." ) - - printer.ftp.storbinary(f'STOR {filename}', file) - self.logQueue.put("Success: " + str(filename) + " to printer " + printer.name) + if window_name == "SelectSettings": + self.main_window = SelectSettings(self) + self.main_window.grid(row=0, column=0, padx=25, pady=(12, 25), sticky="nsew") - except: - try: - with open(os.path.join(self.fileDirectory,filename), 'rb') as file: - self.logQueue.put("Reattempting to send " + str(filename) + " to printer " + printer["name"] + "..." ) - printer.ftp.storbinary(f'STOR {filename}', file) - self.logQueue.put("Success: " + str(filename) + " to printer " + printer.name) - except: - self.logQueue.put("Failure: " + str(filename) + " to printer " + printer.name) + elif window_name == "Main": - printer.disconnect() + self.main_window = Main(self) + self.main_window.grid(row=0, column=0, padx=25, pady=(12, 25), sticky="nsew") + self._current_window = window_name else: - self.logQueue.put("Could not connect to " + printer.name) - - - def send(self): - - self.log.wipe() - - toSend = os.listdir(self.fileDirectory) - - self.log.write("Files to be sent: " + str(toSend)) - - printerThreads = list() - - for printer in self.printers: - - if printer.enabled.get(): - - thread = threading.Thread(target=self.sendOnePrinter, args=(printer, toSend)) - printerThreads.append(thread) - thread.start() - - else: - self.log.write("Skipping " + printer.name) - - while any(t.is_alive() for t in printerThreads): - self.processQueue() - # keeps ui from hanging - self.ui.update() - - self.processQueue() - - self.logQueue.put("Process Complete!") - - self.processQueue() + raise NotImplementedError(f"{window_name} window does not exist") if __name__ == "__main__": - app = App() \ No newline at end of file + app = App() + + app.mainloop() \ No newline at end of file diff --git a/src/Log.py b/src/Log.py deleted file mode 100644 index 8acf73f..0000000 --- a/src/Log.py +++ /dev/null @@ -1,16 +0,0 @@ -import tkinter as tk -import logging - -class Logger(): - """This class allows you to log to a Tkinter Text or ScrolledText widget""" - - def __init__(self, logUI): - self.logUI = logUI - - def write(self, message): - self.logUI.insert(tk.INSERT, message + "\n") - self.logUI.see("end") - self.logUI.update() - - def wipe(self): - self.logUI.delete('1.0', tk.END) \ No newline at end of file diff --git a/src/gui/Main.py b/src/gui/Main.py new file mode 100644 index 0000000..626c672 --- /dev/null +++ b/src/gui/Main.py @@ -0,0 +1,153 @@ +import customtkinter as CTk +from tkinter import filedialog +from logic.classes.Logger import Logger +from logic.classes.Printer import Printer +from gui.main.FileList import FileList +from gui.main.PrinterList import PrinterList +import os, glob, threading + + +class Main(CTk.CTkFrame): + def __init__(self, master, **kwargs): + super().__init__(master, **kwargs) + self.grid_columnconfigure(0, weight=2) + self.grid_columnconfigure(1, weight=1) + self.grid_rowconfigure((0, 1, 2, 3, 4, 5), weight=1) + self.grid_rowconfigure(6, weight=100) + + self.app = self.master + + # label "settings file name in cool font" + self.title = CTk.CTkLabel(self, text=f"{os.path.basename(self.app.settings_path)}", justify="center", font=("Arial", 18)) + self.title.grid(row=0, column=0, columnspan=2, padx=20, pady=10, sticky="w") + + ## PRINTERS + # label "printers" + self.title_printer = CTk.CTkLabel(self, text="Select Printes", justify="center", font=("Arial", 15)) + self.title_printer.grid(row=1, column=0, padx=20, pady=10, sticky="n") + + # printer list (selection) + self.printer_list = PrinterList(self) + self.printer_list.grid(row=3, column=0, padx=20, pady=10, sticky="nesw") + + # select group (make this a pop up with ceckbox?) + options = ["*Select a Group", "*Select All Printers", "*Unselect All Printers"] + self.printer_group_options + self.printer_group_selection = CTk.CTkOptionMenu(self, values=options, command=self.selectGroup) + self.printer_group_selection.grid(row=2, column=0, padx=20, pady=10, sticky="nesw") + self.printer_group_selection.set("*Select a Group") + + # button "manipulate printers" + self.conferma_button = CTk.CTkButton(self, text="Manipulate Printers", command=self.manipulatePrinters, state="disabled") + self.conferma_button.grid(row=4, column=0, padx=20, pady=(10, 0), sticky="nesw") + + # button "Send to Printers" + self.conferma_button = CTk.CTkButton(self, text="Send to Printers", command=self.sendToPrinters) + self.conferma_button.grid(row=4, column=1, padx=20, pady=(10, 0), sticky="nesw") + + ## FOLDERS + # label "folder" + self.title_folder = CTk.CTkLabel(self, text=f"Folder: {None}", justify="center", font=("Arial", 15)) + self.title_folder.grid(row=1, column=1, padx=20, pady=10, sticky="n") + + # button "select folder" + self.conferma_button = CTk.CTkButton(self, text="Select folder", command=self.selectFolder) + self.conferma_button.grid(row=2, column=1, padx=20, pady=(10, 0), sticky="nesw") + + # file list (selection) + self.file_list = FileList(self) + self.file_list.grid(row=3, column=1, columnspan=2, padx=20, pady=10, sticky="nesw") + + ## LOGS + # label "logs" + self.title_logs = CTk.CTkLabel(self, text="Logs:", anchor="w", font=("Arial", 15)) + self.title_logs.grid(row=5, column=0, padx=20, pady=10, sticky="nesw") + + # progress_bar + #self.progressbar = CTk.CTkProgressBar(self, orientation="horizontal") + #self.progressbar.grid(row=5, column=1, padx=20, pady=10) + + # scrollable logs + self.logs_textbox = CTk.CTkTextbox(self) + self.logs_textbox.grid(row=6, column=0, columnspan=2, padx=20, pady=(0, 10), sticky="nesw") + self.logger = Logger(self.logs_textbox) + + @property + def printer_group_options(self) -> list: + result = [] + if self.app.printers: + for name, printer in self.app.printers.items(): + if printer.group not in result: + result.append(printer.group) + return result + + def manipulatePrinters(self): + pass + + def selectFolder(self): + self.app.selected_folder = filedialog.askdirectory() + files = glob.glob(os.path.join(self.app.selected_folder, '*.3mf')) + self.app.selected_folder_files = {item: True for item in files} + self.file_list.update() + + def selectGroup(self, value): + if value == "*Don't use Groups": + pass + elif value == "*Select All Printers": + self.app.printers_selected = self.app.printers + elif value == "*Unselect All Printers": + self.app.printers_selected = {} + else: + new_items = {} + for name, printer in self.app.printers.items(): + if printer.group == value: + new_items[name] = printer + self.app.printers_selected = new_items + + self.printer_list.update() + + + def sendToPrinters(self): + print(self.app.printers_selected, self.app.selected_folder_files) + self.logger.wipe() + + files = self.app.selected_folder_files + to_send = [os.path.basename(key) for key in files if files[key]] + printerThreads = list() + + for _, printer in self.app.printers_selected.items(): + thread = threading.Thread(target=self.sendOnePrinter, args=(printer, to_send)) + printerThreads.append(thread) + thread.start() + + while any(t.is_alive() for t in printerThreads): + self.app.update() + self.logger.write("Process Complete!") + + + def sendOnePrinter(self, printer: Printer, toSend): + printer.connect() + + if printer.connected: + + self.logger.write(f"Connected to {printer.name}") + + for file_path in toSend: + try: + with open(os.path.join(self.app.selected_folder, file_path), 'rb') as f: + self.logger.write(f"Sending {file_path} to printer {printer.name} ...") + printer.ftp.storbinary(f'STOR {file_path}', f) + self.logger.write(f"Success: {file_path} to printer {printer.name}") + except: + try: + with open(os.path.join(self.app.selected_folder, file_path), 'rb') as f: + self.logger.write(f"Reattempting to send {file_path} to printer {printer.name} ...") + printer.ftp.storbinary(f'STOR {file_path}', f) + self.logger.write(f"Success: {file_path} to printer {printer.name}") + except: + self.logger.write(f"Failiure: {file_path} to printer {printer.name}") + + printer.disconnect() + self.logger.write(f"Disconnected from {printer.name}") + + else: + self.logger.write(f"Could not connect to {printer.name}") diff --git a/src/gui/SelectSettings.py b/src/gui/SelectSettings.py new file mode 100644 index 0000000..bd56344 --- /dev/null +++ b/src/gui/SelectSettings.py @@ -0,0 +1,30 @@ +import customtkinter as CTk +from tkinter import filedialog +import json + +class SelectSettings(CTk.CTkFrame): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure((0, 1), weight=1) + + # label "settings file" + self.label = CTk.CTkLabel(self, text="Select a settings file to start.") + self.label.grid(row=0, column=0, padx=15, pady=(8, 15), sticky="s") + + # button "Select file" + self.button_select_settings = CTk.CTkButton(self, text="Select settings file", command=self.selectsettings) + self.button_select_settings.grid(row=1, column=0, padx=15, pady=(0, 15), sticky="nesw") + + + def selectsettings(self): + file = filedialog.askopenfilename( + initialdir = ".", + title = "Select settings File", + filetypes = (("json file","*.json"),("json file","*.json")) + ) + with open(file) as f: + self.master.settings = json.load(f) + self.master.settings_path = file + self.master.changeWindow("Main") \ No newline at end of file diff --git a/src/gui/main/FileList.py b/src/gui/main/FileList.py new file mode 100644 index 0000000..b96486c --- /dev/null +++ b/src/gui/main/FileList.py @@ -0,0 +1,29 @@ +import customtkinter as CTk +import os + +class FileList(CTk.CTkScrollableFrame): + def __init__(self, master, **kwargs): + super().__init__(master, **kwargs) + self.app = self.master.master.master.app + + self.checkboxes = {} + + def update(self): + for checkbox in self.checkboxes: + checkbox.destroy() + + for file_path, status in self.app.selected_folder_files.items(): + file_name = os.path.basename(file_path) + self.checkboxes[file_path] = CTk.CTkCheckBox(self, text=file_name, command=self.checkboxEvent) + self.checkboxes[file_path].grid(row=0, column=0, padx=5, pady=(5, 0)) + if status: + self.checkboxes[file_path].select() + + def checkboxEvent(self): + new_states = {} + for path, checkbox in self.checkboxes.items(): + if checkbox.get(): + new_states[path] = True + else: + new_states[path] = False + self.app.selected_folder_files = new_states diff --git a/src/gui/main/PrinterList.py b/src/gui/main/PrinterList.py new file mode 100644 index 0000000..80a0cad --- /dev/null +++ b/src/gui/main/PrinterList.py @@ -0,0 +1,33 @@ +import customtkinter as CTk +from logic.classes.Printer import Printer + +class PrinterList(CTk.CTkScrollableFrame): + def __init__(self, master, **kwargs): + super().__init__(master, **kwargs) + self.app = self.master.master.master.app + + self.checkboxes = {} + + for i, printer in enumerate(self.app.settings["printers"]): + self.app.printers[printer["name"]] = Printer( + name = printer["name"], + group = printer["group"], + ip = printer["ip"], + pw = printer["pw"] + ) + self.checkboxes[printer['name']] = CTk.CTkCheckBox(self, text=printer['name'], command=self.checkboxEvent) + self.checkboxes[printer['name']].grid(row=i, column=0, padx=5, pady=(5, 0)) + + def checkboxEvent(self): + new_states = {} + for name, checkbox in self.checkboxes.items(): + if checkbox.get(): + new_states[name] = self.app.printers[name] + self.app.printers_selected = new_states + + def update(self): + for name, checkbox in self.checkboxes.items(): + if name in self.app.printers_selected: + checkbox.select() + else: + checkbox.deselect() \ No newline at end of file diff --git a/src/BambuFTP.py b/src/logic/classes/BambuFTP.py similarity index 99% rename from src/BambuFTP.py rename to src/logic/classes/BambuFTP.py index 428f6df..53701d1 100644 --- a/src/BambuFTP.py +++ b/src/logic/classes/BambuFTP.py @@ -5,7 +5,7 @@ """ import ftplib -import ssl, os +import ssl class Error(Exception): pass CRLF = '\r\n' @@ -26,12 +26,12 @@ class BambuFTP(ftplib.FTP_TLS): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._sock = None - + @property def sock(self): """Return the socket.""" return self._sock - + @sock.setter def sock(self, value): """When modifying the socket, ensure that it is ssl wrapped.""" @@ -69,7 +69,7 @@ def storlines(self, cmd, fp, callback=None): # if _SSLSocket is not None and isinstance(conn, _SSLSocket): # conn.unwrap() return self.voidresp() - + def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None): """Store a file in binary mode. A new port is created for you. diff --git a/src/logic/classes/Logger.py b/src/logic/classes/Logger.py new file mode 100644 index 0000000..9c03c15 --- /dev/null +++ b/src/logic/classes/Logger.py @@ -0,0 +1,13 @@ + + +class Logger(): + """This class allows you to log to a CTkinter ScrolledText widget""" + + def __init__(self, logUI): + self.logUI = logUI + + def write(self, message): + self.logUI.insert("end", f"{message}\n") + + def wipe(self): + self.logUI.delete("0.0", "end") \ No newline at end of file diff --git a/src/Printer.py b/src/logic/classes/Printer.py similarity index 75% rename from src/Printer.py rename to src/logic/classes/Printer.py index 227dd71..fb26ba1 100644 --- a/src/Printer.py +++ b/src/logic/classes/Printer.py @@ -1,12 +1,11 @@ - -from BambuFTP import BambuFTP +from logic.classes.BambuFTP import BambuFTP class Printer(): - def __init__(self, name, ip, pw, enabled): - self.name = name; - self.ip = ip; - self.pw = pw; - self.enabled = enabled + def __init__(self, name, group, ip, pw): + self.name = name + self.group = group + self.ip = ip + self.pw = pw self.connected = False self.ftp = BambuFTP() @@ -22,7 +21,7 @@ def connect(self): self.connected = True except: return False - + def disconnect(self): try: self.ftp.quit() From 5abcb8db14922bacd3223df5eb3c2c78e7aeb62c Mon Sep 17 00:00:00 2001 From: MarkinoTeck <93880780+MarkinoTeck@users.noreply.github.com> Date: Sun, 9 Jun 2024 21:44:50 +0200 Subject: [PATCH 2/4] fixes Fixes: - folder label was not updating - upload starting message now re-added Other: - I didn't metion @Rad-hi that had the idea for the select\unselect all that I added --- src/gui/Main.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui/Main.py b/src/gui/Main.py index 626c672..8243295 100644 --- a/src/gui/Main.py +++ b/src/gui/Main.py @@ -88,6 +88,7 @@ def selectFolder(self): files = glob.glob(os.path.join(self.app.selected_folder, '*.3mf')) self.app.selected_folder_files = {item: True for item in files} self.file_list.update() + self.title_folder.configure(text=f"Folder: {os.path.basename(self.app.settings_path)}") def selectGroup(self, value): if value == "*Don't use Groups": @@ -107,14 +108,18 @@ def selectGroup(self, value): def sendToPrinters(self): - print(self.app.printers_selected, self.app.selected_folder_files) self.logger.wipe() files = self.app.selected_folder_files + printers_selected = self.app.printers_selected + to_send = [os.path.basename(key) for key in files if files[key]] + printer_names = [key for key in printers_selected if [key]] + + self.logger.write(f"Printers selected: {printer_names}\nFiles selected: {to_send}") printerThreads = list() - for _, printer in self.app.printers_selected.items(): + for _, printer in printers_selected.items(): thread = threading.Thread(target=self.sendOnePrinter, args=(printer, to_send)) printerThreads.append(thread) thread.start() From 08045e7eb5a28fdf72db4a0c357213d07b495bdf Mon Sep 17 00:00:00 2001 From: MarkinoTeck <93880780+MarkinoTeck@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:12:09 +0200 Subject: [PATCH 3/4] modified readme and added some types --- README.md | 2 +- src/gui/Main.py | 1 + src/logic/classes/Logger.py | 4 ++-- src/logic/classes/Printer.py | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 419bdd6..d52ea88 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,6 @@ Hit "Send to Farm" to upload all files in the folder to your print farm. The log ## Todo -- Test on printers other than BambuLab P1P +- Test on printers other than BambuLab P1P and P1S - Support deleting existing files from the printer's SD - Handle uploading files that share a name with an existing file on the SD diff --git a/src/gui/Main.py b/src/gui/Main.py index 8243295..731dcf6 100644 --- a/src/gui/Main.py +++ b/src/gui/Main.py @@ -76,6 +76,7 @@ def printer_group_options(self) -> list: result = [] if self.app.printers: for name, printer in self.app.printers.items(): + printer: Printer if printer.group not in result: result.append(printer.group) return result diff --git a/src/logic/classes/Logger.py b/src/logic/classes/Logger.py index 9c03c15..c8b63be 100644 --- a/src/logic/classes/Logger.py +++ b/src/logic/classes/Logger.py @@ -1,9 +1,9 @@ - +import customtkinter as CTk class Logger(): """This class allows you to log to a CTkinter ScrolledText widget""" - def __init__(self, logUI): + def __init__(self, logUI: CTk.CTkTextbox): self.logUI = logUI def write(self, message): diff --git a/src/logic/classes/Printer.py b/src/logic/classes/Printer.py index fb26ba1..9ef9759 100644 --- a/src/logic/classes/Printer.py +++ b/src/logic/classes/Printer.py @@ -1,7 +1,7 @@ from logic.classes.BambuFTP import BambuFTP class Printer(): - def __init__(self, name, group, ip, pw): + def __init__(self, name: str, group: str, ip: str, pw: str): self.name = name self.group = group self.ip = ip @@ -9,7 +9,6 @@ def __init__(self, name, group, ip, pw): self.connected = False self.ftp = BambuFTP() - #self.ftp.set_debuglevel(2) self.ftp.set_pasv(True) From bdf3e65b08c7aa9cfa59d4756ee57235fc687c21 Mon Sep 17 00:00:00 2001 From: MarkinoTeck <93880780+MarkinoTeck@users.noreply.github.com> Date: Tue, 11 Jun 2024 22:14:08 +0200 Subject: [PATCH 4/4] modified file uploading --- src/FarmUpload.py | 3 +-- src/gui/Main.py | 35 ++++++++++++++++++----------------- src/gui/main/FileList.py | 33 ++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/FarmUpload.py b/src/FarmUpload.py index 2ec6392..d5f0f64 100644 --- a/src/FarmUpload.py +++ b/src/FarmUpload.py @@ -19,8 +19,7 @@ def __init__(self): self.printers_selected: dict = {} self.settings_path: str = None self.settings: dict = None - self.selected_folder: str = None - self.selected_folder_files: dict = {} + self.selected_files: list = [] self.changeWindow("SelectSettings") diff --git a/src/gui/Main.py b/src/gui/Main.py index 731dcf6..6c22ed7 100644 --- a/src/gui/Main.py +++ b/src/gui/Main.py @@ -4,7 +4,7 @@ from logic.classes.Printer import Printer from gui.main.FileList import FileList from gui.main.PrinterList import PrinterList -import os, glob, threading +import os, threading class Main(CTk.CTkFrame): @@ -27,7 +27,7 @@ def __init__(self, master, **kwargs): self.title_printer.grid(row=1, column=0, padx=20, pady=10, sticky="n") # printer list (selection) - self.printer_list = PrinterList(self) + self.printer_list = PrinterList(self, label_anchor="w") self.printer_list.grid(row=3, column=0, padx=20, pady=10, sticky="nesw") # select group (make this a pop up with ceckbox?) @@ -44,17 +44,17 @@ def __init__(self, master, **kwargs): self.conferma_button = CTk.CTkButton(self, text="Send to Printers", command=self.sendToPrinters) self.conferma_button.grid(row=4, column=1, padx=20, pady=(10, 0), sticky="nesw") - ## FOLDERS - # label "folder" - self.title_folder = CTk.CTkLabel(self, text=f"Folder: {None}", justify="center", font=("Arial", 15)) - self.title_folder.grid(row=1, column=1, padx=20, pady=10, sticky="n") + ## FILES + # label "files" + self.title_files = CTk.CTkLabel(self, text=f"Files: {len(self.app.selected_files)}", justify="center", font=("Arial", 15)) + self.title_files.grid(row=1, column=1, padx=20, pady=10) - # button "select folder" - self.conferma_button = CTk.CTkButton(self, text="Select folder", command=self.selectFolder) - self.conferma_button.grid(row=2, column=1, padx=20, pady=(10, 0), sticky="nesw") + # button "select files" + self.select_files = CTk.CTkButton(self, text="Select files", command=self.selectFiles) + self.select_files.grid(row=2, column=1, padx=20, pady=(10, 0), sticky="nesw") # file list (selection) - self.file_list = FileList(self) + self.file_list = FileList(self, label_anchor="w") self.file_list.grid(row=3, column=1, columnspan=2, padx=20, pady=10, sticky="nesw") ## LOGS @@ -84,12 +84,13 @@ def printer_group_options(self) -> list: def manipulatePrinters(self): pass - def selectFolder(self): - self.app.selected_folder = filedialog.askdirectory() - files = glob.glob(os.path.join(self.app.selected_folder, '*.3mf')) - self.app.selected_folder_files = {item: True for item in files} + def selectFiles(self): + filetypes = [("3D Manufacturing Format files", "*.3mf")] + files = list(filedialog.askopenfilenames(title="Select .3mf files", filetypes=filetypes)) + for file in files: + if file not in self.app.selected_files: + self.app.selected_files.append(file) self.file_list.update() - self.title_folder.configure(text=f"Folder: {os.path.basename(self.app.settings_path)}") def selectGroup(self, value): if value == "*Don't use Groups": @@ -111,10 +112,10 @@ def selectGroup(self, value): def sendToPrinters(self): self.logger.wipe() - files = self.app.selected_folder_files + files = self.app.selected_files printers_selected = self.app.printers_selected - to_send = [os.path.basename(key) for key in files if files[key]] + to_send = [os.path.basename(key) for key in files] printer_names = [key for key in printers_selected if [key]] self.logger.write(f"Printers selected: {printer_names}\nFiles selected: {to_send}") diff --git a/src/gui/main/FileList.py b/src/gui/main/FileList.py index b96486c..989ce0c 100644 --- a/src/gui/main/FileList.py +++ b/src/gui/main/FileList.py @@ -9,21 +9,28 @@ def __init__(self, master, **kwargs): self.checkboxes = {} def update(self): - for checkbox in self.checkboxes: - checkbox.destroy() + for path, checkbox in self.checkboxes.items(): + # I hate this, but its late and for now it works so I don't care. + try: + checkbox.destroy() + except: + continue + try: + del checkbox + except: + continue + self.checkboxes = {} - for file_path, status in self.app.selected_folder_files.items(): + for i, file_path in enumerate(self.app.selected_files): file_name = os.path.basename(file_path) self.checkboxes[file_path] = CTk.CTkCheckBox(self, text=file_name, command=self.checkboxEvent) - self.checkboxes[file_path].grid(row=0, column=0, padx=5, pady=(5, 0)) - if status: - self.checkboxes[file_path].select() + self.checkboxes[file_path].grid(row=i, column=0, padx=5, pady=(5, 0), sticky="w") + self.checkboxes[file_path].select() + + self.master.master.master.title_files.configure(text=f"Files: {len(self.app.selected_files)}") def checkboxEvent(self): - new_states = {} - for path, checkbox in self.checkboxes.items(): - if checkbox.get(): - new_states[path] = True - else: - new_states[path] = False - self.app.selected_folder_files = new_states + for i, (path, checkbox) in enumerate(self.checkboxes.items()): + if checkbox.get() == 0: + self.app.selected_files.pop(i) + self.update()