当然是拷贝,克隆别人公开源代码啦。。。
第一步,https://git-scm.com/downloads 根据系统版本,下载对应的git
第二步,打开github,看到自己感兴趣或者业务逻辑相符合大的,并筛选是python写的,优秀的项目,
点绿色code 复制代码
第三步,执行我写的程序粘贴,(代理自己找一个,,因为github服务器距离我们较远,频繁抽风),然后点击"执行 Git Clone“
第四步,点克隆,并右击PyCharm "Open Folder as PyCharm Project" 创建一个虚拟环境的python,就可以啦
附录
[Python] 纯文本查看 复制代码import sys
import os
import subprocess
import threading
import json
import random
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QTextEdit, QPushButton, QCheckBox,
QGroupBox, QComboBox, QFileDialog, QMessageBox, QProgressBar,
QSplitter, QFrame, QScrollArea, QStyle)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSettings, QUrl
from PyQt5.QtGui import QFont, QPalette, QIcon, QDesktopServices
class CloneThread(QThread):
"""克隆线程"""
output_signal = pyqtSignal(str)
finished_signal = pyqtSignal(bool, str)
def __init__(self, url, target_dir, use_proxy, proxy_url):
super().__init__()
self.url = url
self.target_dir = target_dir
self.use_proxy = use_proxy
self.proxy_url = proxy_url
self.is_running = True
def run(self):
try:
# 设置代理
if self.use_proxy and self.proxy_url:
self.output_signal.emit(f"设置代理: {self.prsoxy_url}\n")
try:
subprocess.run(['git', 'config', '--global', 'http.proxy', self.proxy_url],
check=True, capture_output=True, text=True)
subprocess.run(['git', 'config', '--global', 'https.proxy', self.proxy_url],
check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
self.output_signal.emit(f"设置代理失败: {e}\n")
# 执行git clone
self.output_signal.emit(f"开始克隆: {self.url}\n")
self.output_signal.emit(f"目标目录: {self.target_dir}\n")
self.output_signal.emit("-" * 50 + "\n")
# 确保目标目录存在
os.makedirs(self.target_dir, exist_ok=True)
# 切换到目标目录执行clone
original_dir = os.getcwd()
os.chdir(self.target_dir)
process = subprocess.Popen(['git', 'clone', self.url],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True)
# 实时输出
while self.is_running:
line = process.stdout.readline()
if not line:
break
self.output_signal.emit(line)
process.wait()
os.chdir(original_dir)
if process.returncode == 0:
self.output_signal.emit("\n✅ 克隆完成!\n")
self.finished_signal.emit(True, "克隆完成")
else:
self.output_signal.emit(f"\n❌ 克隆失败,返回码: {process.returncode}\n")
self.finished_signal.emit(False, "克隆失败")
except Exception as e:
self.output_signal.emit(f"❌ 发生错误: {str(e)}\n")
self.finished_signal.emit(False, f"发生错误: {str(e)}")
def stop(self):
self.is_running = False
class GitCloneGUI(QMainWindow):
def __init__(self):
super().__init__()
self.settings = QSettings("GitCloneTool", "Config")
self.clone_thread = None
self.init_ui()
self.load_settings()
def init_ui(self):
"""初始化UI"""
self.setWindowTitle("Git Clone 工具 - PyQt5版")
self.setGeometry(100, 100, 900, 700)
# 设置样式
self.setStyleSheet("""
QMainWindow {
background-color: #f5f5f5;
}
QGroupBox {
font-weight: bold;
margin-top: 1ex;
border: 1px solid #cccccc;
border-radius: 5px;
padding-top: 10px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
}
QPushButton {
background-color: #007acc;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
font-weight: bold;
}
QPushButton:hover {
background-color: #005a9e;
}
QPushButton:disabled {
background-color: #cccccc;
color: #666666;
}
QPushButton#success {
background-color: #28a745;
}
QPushButton#danger {
background-color: #dc3545;
}
QTextEdit {
border: 1px solid #cccccc;
border-radius: 4px;
padding: 5px;
font-family: 'Consolas', 'Monaco', monospace;
}
QLineEdit, QComboBox {
padding: 6px;
border: 1px solid #cccccc;
border-radius: 4px;
}
QProgressBar {
border: 1px solid #cccccc;
border-radius: 4px;
text-align: center;
}
QProgressBar::chunk {
background-color: #007acc;
width: 20px;
}
""")
# 创建中心部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
# 主布局
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(15)
main_layout.setContentsMargins(20, 20, 20, 20)
# 代理设置组
self.create_proxy_group(main_layout)
# URL输入组
self.create_url_group(main_layout)
# 目录设置组
self.create_directory_group(main_layout)
# 按钮组
self.create_button_group(main_layout)
# 输出区域
self.create_output_group(main_layout)
# 状态栏
self.create_status_bar()
self.set_random_icon()
def create_proxy_group(self, layout):
"""创建代理设置组"""
proxy_group = QGroupBox("代理设置")
proxy_layout = QHBoxLayout()
# 代理历史选择
proxy_layout.addWidget(QLabel("代理地址:"))
self.proxy_combo = QComboBox()
self.proxy_combo.setEditable(True)
self.proxy_combo.setMinimumWidth(300)
proxy_layout.addWidget(self.proxy_combo)
# 启用代理复选框
self.use_proxy_check = QCheckBox("启用代理")
self.use_proxy_check.setChecked(True)
proxy_layout.addWidget(self.use_proxy_check)
# 保存代理按钮
self.save_proxy_btn = QPushButton("保存代理")
self.save_proxy_btn.clicked.connect(self.save_proxy_to_history)
proxy_layout.addWidget(self.save_proxy_btn)
# 清除代理历史按钮
self.clear_proxy_btn = QPushButton("清除历史")
self.clear_proxy_btn.setObjectName("danger")
self.clear_proxy_btn.clicked.connect(self.clear_proxy_history)
proxy_layout.addWidget(self.clear_proxy_btn)
proxy_layout.addStretch()
proxy_group.setLayout(proxy_layout)
layout.addWidget(proxy_group)
def create_url_group(self, layout):
"""创建URL输入组"""
url_group = QGroupBox("Git 仓库地址")
url_layout = QVBoxLayout()
# URL输入框
self.url_input = QTextEdit()
self.url_input.setMaximumHeight(80)
self.url_input.setPlaceholderText(
"输入Git仓库URL,例如:\nhttps://github.com/jark006/FtpServer.git\n或者完整的 git clone 命令")
url_layout.addWidget(self.url_input)
# 示例按钮区域隐藏
# (已移除快速示例按钮以保持界面简洁)
url_group.setLayout(url_layout)
layout.addWidget(url_group)
def create_directory_group(self, layout):
"""创建目录设置组"""
dir_group = QGroupBox("输出目录设置")
dir_layout = QHBoxLayout()
dir_layout.addWidget(QLabel("保存路径:"))
self.dir_input = QLineEdit()
self.dir_input.setText("D:\\clone")
dir_layout.addWidget(self.dir_input)
self.browse_btn = QPushButton("浏览")
self.browse_btn.clicked.connect(self.browse_directory)
dir_layout.addWidget(self.browse_btn)
# 设置默认目录按钮
self.set_default_btn = QPushButton("设为默认")
self.set_default_btn.clicked.connect(self.set_default_directory)
dir_layout.addWidget(self.set_default_btn)
dir_group.setLayout(dir_layout)
layout.addWidget(dir_group)
def create_button_group(self, layout):
"""创建按钮组"""
button_layout = QHBoxLayout()
self.clone_btn = QPushButton("🚀 执行 Git Clone")
self.clone_btn.clicked.connect(self.start_clone)
self.clone_btn.setMinimumHeight(40)
button_layout.addWidget(self.clone_btn)
self.clear_btn = QPushButton("🗑️ 清空")
self.clear_btn.clicked.connect(self.clear_input)
self.clear_btn.setMinimumHeight(40)
button_layout.addWidget(self.clear_btn)
self.stop_btn = QPushButton("⏹️ 停止")
self.stop_btn.clicked.connect(self.stop_clone)
self.stop_btn.setMinimumHeight(40)
self.stop_btn.setEnabled(False)
button_layout.addWidget(self.stop_btn)
self.open_dir_btn = QPushButton("📂 打开克隆位置")
self.open_dir_btn.clicked.connect(self.open_clone_location)
self.open_dir_btn.setMinimumHeight(40)
button_layout.addWidget(self.open_dir_btn)
# self.random_icon_btn = QPushButton("🎲 随机图标")
# self.random_icon_btn.setMinimumHeight(40)
# self.random_icon_btn.clicked.connect(self.set_random_icon)
# button_layout.addWidget(self.random_icon_btn)
layout.addLayout(button_layout)
def create_output_group(self, layout):
"""创建输出区域"""
output_group = QGroupBox("输出信息")
output_layout = QVBoxLayout()
self.output_text = QTextEdit()
self.output_text.setReadOnly(True)
self.output_text.setFont(QFont("Consolas", 9))
output_layout.addWidget(self.output_text)
output_group.setLayout(output_layout)
layout.addWidget(output_group)
def create_status_bar(self):
"""创建状态栏"""
self.status_bar = self.statusBar()
self.status_label = QLabel("就绪")
self.status_bar.addWidget(self.status_label)
# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setMaximumWidth(200)
self.progress_bar.setVisible(False)
self.status_bar.addPermanentWidget(self.progress_bar)
def set_random_icon(self):
"""随机设置窗口图标(使用Qt内置标准图标)"""
try:
candidates = [
QStyle.SP_ComputerIcon,
QStyle.SP_DesktopIcon,
QStyle.SP_DriveHDIcon,
QStyle.SP_DirIcon,
QStyle.SP_DirHomeIcon,
QStyle.SP_DirOpenIcon,
QStyle.SP_FileIcon,
QStyle.SP_TrashIcon,
QStyle.SP_DialogOkButton,
QStyle.SP_DialogCancelButton,
QStyle.SP_DialogApplyButton,
QStyle.SP_DialogResetButton,
QStyle.SP_MediaPlay,
QStyle.SP_MediaStop,
QStyle.SP_MediaPause,
QStyle.SP_BrowserReload,
QStyle.SP_BrowserStop
]
sp = random.choice(candidates)
icon = self.style().standardIcon(sp)
if not icon.isNull():
self.setWindowIcon(icon)
except Exception as e:
# 忽略图标设置异常,不中断程序
pass
def load_settings(self):
"""加载设置"""
# 加载代理历史
proxy_history = self.settings.value("proxy_history", [])
if proxy_history:
self.proxy_combo.addItems(proxy_history)
self.proxy_combo.setCurrentText(proxy_history[0])
# 加载默认目录
default_dir = self.settings.value("default_directory", "D:\\clone")
self.dir_input.setText(default_dir)
# 加载代理启用状态
use_proxy = self.settings.value("use_proxy", True, type=bool)
self.use_proxy_check.setChecked(use_proxy)
def save_settings(self):
"""保存设置"""
# 保存代理历史(最多10个)
proxy_history = []
for i in range(min(self.proxy_combo.count(), 10)):
proxy_history.append(self.proxy_combo.itemText(i))
self.settings.setValue("proxy_history", proxy_history)
# 保存默认目录
self.settings.setValue("default_directory", self.dir_input.text())
# 保存代理启用状态
self.settings.setValue("use_proxy", self.use_proxy_check.isChecked())
def save_proxy_to_history(self):
"""保存代理到历史记录"""
current_proxy = self.proxy_combo.currentText().strip()
if current_proxy and current_proxy not in [self.proxy_combo.itemText(i) for i in
range(self.proxy_combo.count())]:
self.proxy_combo.insertItem(0, current_proxy)
self.proxy_combo.setCurrentIndex(0)
def clear_proxy_history(self):
"""清除代理历史"""
reply = QMessageBox.question(self, "确认", "确定要清除所有代理历史记录吗?",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
self.proxy_combo.clearEditText()
self.proxy_combo.clear()
self.settings.remove("proxy_history")
def insert_example(self, example):
"""插入示例URL"""
self.url_input.setPlainText(example)
def browse_directory(self):
"""浏览选择目录"""
directory = QFileDialog.getExistingDirectory(self, "选择保存目录", self.dir_input.text())
if directory:
self.dir_input.setText(directory)
def set_default_directory(self):
"""设置默认目录"""
self.settings.setValue("default_directory", self.dir_input.text())
QMessageBox.information(self, "成功", "已设置为默认目录")
def open_clone_location(self):
"""打开当前克隆保存位置"""
path = self.dir_input.text().strip()
if not path:
QMessageBox.warning(self, "警告", "未设置保存目录")
return
try:
if not os.path.exists(path):
os.makedirs(path, exist_ok=True)
except Exception:
pass
QDesktopServices.openUrl(QUrl.fromLocalFile(path))
def clear_input(self):
"""清空输入"""
self.url_input.clear()
self.output_text.clear()
self.status_label.setText("已清空")
def start_clone(self):
"""开始执行clone操作"""
url_input = self.url_input.toPlainText().strip()
if not url_input:
QMessageBox.warning(self, "警告", "请输入Git仓库URL")
return
target_dir = self.dir_input.text().strip()
if not target_dir:
QMessageBox.warning(self, "警告", "请选择保存目录")
return
# 清理URL
if url_input.startswith('git clone '):
clean_url = url_input[10:].strip()
else:
clean_url = url_input.strip()
# 更新UI状态
self.clone_btn.setEnabled(False)
self.stop_btn.setEnabled(True)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0) # 不确定进度
self.status_label.setText("正在克隆...")
# 保存当前代理到历史
self.save_proxy_to_history()
# 在新线程中执行clone
self.clone_thread = CloneThread(
clean_url,
target_dir,
self.use_proxy_check.isChecked(),
self.proxy_combo.currentText().strip() if self.use_proxy_check.isChecked() else ""
)
self.clone_thread.output_signal.connect(self.add_output)
self.clone_thread.finished_signal.connect(self.clone_finished)
self.clone_thread.start()
def stop_clone(self):
"""停止克隆"""
if self.clone_thread and self.clone_thread.isRunning():
self.clone_thread.stop()
self.clone_thread.terminate()
self.clone_thread.wait()
self.add_output("\n⏹️ 用户停止操作\n")
self.clone_finished(False, "用户停止")
def add_output(self, text):
"""添加输出信息"""
self.output_text.moveCursor(self.output_text.textCursor().End)
self.output_text.insertPlainText(text)
self.output_text.moveCursor(self.output_text.textCursor().End)
def clone_finished(self, success, message):
"""克隆完成回调"""
self.clone_btn.setEnabled(True)
self.stop_btn.setEnabled(False)
self.progress_bar.setVisible(False)
self.status_label.setText(message)
if success:
self.clone_btn.setObjectName("success")
self.clone_btn.style().unpolish(self.clone_btn)
self.clone_btn.style().polish(self.clone_btn)
else:
self.clone_btn.setObjectName("")
self.clone_btn.style().unpolish(self.clone_btn)
self.clone_btn.style().polish(self.clone_btn)
def closeEvent(self, event):
"""关闭事件"""
if self.clone_thread and self.clone_thread.isRunning():
reply = QMessageBox.question(self, "确认", "克隆操作仍在进行中,确定要退出吗?",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
self.clone_thread.stop()
self.clone_thread.terminate()
self.clone_thread.wait()
self.save_settings()
event.accept()
else:
event.ignore()
else:
self.save_settings()
event.accept()
def main():
app = QApplication(sys.argv)
# 设置应用程序属性
app.setApplicationName("Git Clone Tool")
app.setApplicationVersion("1.0")
app.setOrganizationName("GitCloneTool")
window = GitCloneGUI()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()