This commit is contained in:
Евгений Александрович 2025-03-16 09:28:47 +03:00
commit fa5dfc3c68
3 changed files with 318 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/connections.db

20
README.MD Normal file
View File

@ -0,0 +1,20 @@
# GUI приложение для передачи файлов через SSH.
-----------------
## Основные функции:
### - Подключение к удаленному хосту.
### - Передача файлов на удаленный хост.
### - Просмотр списка сохранённых соединений.
### - Удаление соединений.
## Используемые библиотеки:
### - PySimpleGUI
### - paramiko
## Использование:
### 1. Запустите программу.
### 2. Добавьте хосты в таблицу соединений.
### 3. Выберите хост и введите данные для подключения.
### 4. Выберите файлы для передачи.
### 5. Нажмите кнопку "Передать файлы".

297
main.py Executable file
View File

@ -0,0 +1,297 @@
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import paramiko
import os
import sqlite3
class FileTransferApp:
def __init__(self, master):
self.master = master
self.master.title("SSH File Transfer")
self.master.geometry("600x700")
self.master.configure(bg="#f0f0f0")
# Настройка стилей
style = ttk.Style()
style.configure("TButton", padding=10, relief="flat", background="#007bff", foreground="white",
font=("Arial", 12))
style.map("TButton", background=[("active", "#0056b3")])
style.configure("TLabel", background="#f0f0f0", font=("Arial", 12))
style.configure("TFrame", background="#f0f0f0")
# Скругление кнопок
style.configure("Rounded.TButton", borderwidth=3, relief="flat", padding=10, background="#007bff",
foreground="white")
self.db_name = "connections.db"
self.create_connection_table()
# Заголовок
header_frame = ttk.Frame(master)
header_frame.pack(pady=20)
header_label = ttk.Label(header_frame, text="SSH File Transfer", font=("Arial", 18, "bold"))
header_label.pack()
self.label_connection = ttk.Label(master, text="Saved Connections:")
self.label_connection.pack(pady=(10, 0))
self.connection_var = tk.StringVar()
self.connection_dropdown = ttk.Combobox(master, textvariable=self.connection_var, font=("Arial", 12))
self.connection_dropdown.pack(pady=(0, 10), fill=tk.X)
# Список для хранения параметров подключения
self.host_entries = []
# Кнопка для добавления нового хоста
self.button_add_host = ttk.Button(master, text="Add Host", command=self.add_host_entry, style="Rounded.TButton")
self.button_add_host.pack(pady=(10, 5))
# Кнопка для удаления последнего хоста
self.button_remove_host = ttk.Button(master, text="Remove Last Host", command=self.remove_last_host_entry,
style="Rounded.TButton")
self.button_remove_host.pack(pady=(0, 10))
# Секция для ввода данных о хостах
self.host_frame = ttk.Frame(master)
self.host_frame.pack(pady=(0, 10))
# Кнопка для выбора файлов
self.button_browse = ttk.Button(master, text="Add Files", command=self.browse_files, style="Rounded.TButton")
self.button_browse.pack(pady=(0, 10))
# Listbox для отображения выбранных файлов
self.file_listbox = tk.Listbox(master, selectmode=tk.SINGLE, font=("Arial", 12), height=10)
self.file_listbox.pack(expand=True, fill=tk.BOTH, pady=(0, 10))
# Scrollbar для Listbox
scrollbar = tk.Scrollbar(master)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.file_listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.file_listbox.yview)
self.button_transfer = ttk.Button(master, text="Transfer Files", command=self.transfer_files,
style="Rounded.TButton")
self.button_transfer.pack(pady=(5, 5), fill=tk.X)
self.button_transfer.configure(style='Green.TButton')
# Кнопка для удаления выбранного файла
self.button_remove_file = ttk.Button(master, text="Remove Selected File", command=self.remove_selected_file,
style="Rounded.TButton")
self.button_remove_file.pack(pady=(5, 5))
# Добавление стиля для кнопки передачи
style.configure("Green.TButton", background="#28a745", foreground="white")
style.map("Green.TButton", background=[("active", "#218838")])
# Кнопка для сохранения данных подключения
self.button_save_connection = ttk.Button(master, text="Save Connection Data", command=self.save_connection_data,
style="Rounded.TButton")
self.button_save_connection.pack(pady=(5, 5))
# Кнопка для удаления выбранного соединения
self.button_delete_connection = ttk.Button(master, text="Delete Selected Connection",
command=self.delete_connection, style="Rounded.TButton")
self.button_delete_connection.pack(pady=(5, 5))
self.load_saved_connections()
def create_connection_table(self):
conn = sqlite3.connect(self.db_name)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS connections (
id INTEGER PRIMARY KEY,
host TEXT,
port INTEGER,
username TEXT,
password TEXT,
remote_directory TEXT
)
''')
conn.commit()
conn.close()
def load_saved_connections(self):
conn = sqlite3.connect(self.db_name)
cursor = conn.cursor()
cursor.execute("SELECT id, host FROM connections")
connections = cursor.fetchall()
connection_names = [f"{host} (ID: {id})" for id, host in connections]
self.connection_dropdown['values'] = connection_names
conn.close()
def load_connection(self, event):
selected_connection = self.connection_var.get()
if selected_connection:
connection_id = int(selected_connection.split(" (ID: ")[1][:-1])
conn = sqlite3.connect(self.db_name)
cursor = conn.cursor()
cursor.execute("SELECT host, port, username, password FROM connections WHERE id=?", (connection_id,))
result = cursor.fetchone()
conn.close()
if result:
host, port, username, password = result
self.clear_host_entries()
self.add_host_entry(host, port, username, password)
def clear_host_entries(self):
for entry in self.host_entries:
entry.destroy()
self.host_entries.clear()
def add_host_entry(self, host='', port='', username='', password=''):
frame = ttk.Frame(self.host_frame)
frame.pack(pady=(5, 0))
entry_host = ttk.Entry(frame)
entry_host.insert(0, host if host else "Host")
entry_host.pack(side=tk.LEFT)
entry_port = ttk.Entry(frame)
entry_port.insert(0, port if port else "22")
entry_port.pack(side=tk.LEFT)
entry_username = ttk.Entry(frame)
entry_username.insert(0, username if username else "Username")
entry_username.pack(side=tk.LEFT)
entry_password = ttk.Entry(frame, show='*')
entry_password.insert(0, password if password else "Password")
entry_password.pack(side=tk.LEFT)
# Поле для ввода удаленной директории
entry_remote_directory = ttk.Entry(frame)
entry_remote_directory.insert(0, "Remote Directory")
entry_remote_directory.pack(side=tk.LEFT)
# Сохраняем ссылку на поле удаленной директории
frame.entry_remote_directory = entry_remote_directory
self.host_entries.append(frame)
def remove_last_host_entry(self):
if self.host_entries:
last_entry = self.host_entries.pop()
last_entry.destroy()
def save_connection_data(self):
conn = sqlite3.connect(self.db_name)
cursor = conn.cursor()
for entry in self.host_entries:
host = entry.winfo_children()[0].get()
port = int(entry.winfo_children()[1].get())
username = entry.winfo_children()[2].get()
password = entry.winfo_children()[3].get()
remote_directory = entry.entry_remote_directory.get()
# Проверяем наличие существующей записи и обновляем её или создаем новую
cursor.execute("SELECT id FROM connections WHERE host=? AND port=? AND username=?", (host, port, username))
if cursor.fetchone():
cursor.execute(
"UPDATE connections SET password=?, remote_directory=? WHERE host=? AND port=? AND username=?",
(password, remote_directory, host, port, username))
messagebox.showinfo("Success", f"Connection data updated for {host}!")
else:
cursor.execute(
"INSERT INTO connections (host, port, username, password, remote_directory) VALUES (?, ?, ?, ?, ?)",
(host, port, username, password, remote_directory))
messagebox.showinfo("Success", f"Connection data saved for {host}!")
conn.commit()
conn.close()
self.load_saved_connections()
def delete_connection(self):
selected_connection = self.connection_var.get()
if not selected_connection:
messagebox.showerror("Error", "Please select a connection to delete.")
return
connection_id = int(selected_connection.split(" (ID: ")[1][:-1])
conn = sqlite3.connect(self.db_name)
cursor = conn.cursor()
cursor.execute("DELETE FROM connections WHERE id=?", (connection_id,))
conn.commit()
conn.close()
messagebox.showinfo("Success", "Connection deleted!")
self.load_saved_connections()
def browse_files(self):
file_paths = filedialog.askopenfilenames(title="Select Files")
if file_paths:
for file_path in file_paths:
# Добавляем выбранные файлы в Listbox
if file_path not in [self.file_listbox.get(i) for i in range(self.file_listbox.size())]:
self.file_listbox.insert(tk.END, file_path)
def remove_selected_file(self):
selected_indices = self.file_listbox.curselection()
if not selected_indices:
messagebox.showwarning("Warning", "Please select a file to remove.")
return
for index in reversed(selected_indices):
self.file_listbox.delete(index)
def transfer_files(self):
if not self.host_entries:
messagebox.showerror("Error", "Please add at least one host.")
return
if self.file_listbox.size() == 0:
messagebox.showerror("Error", "Please select files to transfer.")
return
for entry in self.host_entries:
host = entry.winfo_children()[0].get()
port = int(entry.winfo_children()[1].get())
username = entry.winfo_children()[2].get()
password = entry.winfo_children()[3].get()
remote_directory = entry.entry_remote_directory.get()
try:
# Создание SSH клиента
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname=host, port=port, username=username, password=password)
# Передача файлов
sftp = client.open_sftp()
for i in range(self.file_listbox.size()):
local_file_path = self.file_listbox.get(i)
remote_path = os.path.join(remote_directory, os.path.basename(
local_file_path))
sftp.put(local_file_path, remote_path)
sftp.close()
client.close()
messagebox.showinfo("Success", f"Files transferred successfully to {host}!")
except Exception as e:
messagebox.showerror("Error", f"Failed to transfer files to {host}: {str(e)}")
# Очистить список после успешной передачи
if i == (self.file_listbox.size() - 1):
self.file_listbox.delete(0, tk.END)
if __name__ == "__main__":
root = tk.Tk()
app = FileTransferApp(root)
root.mainloop()