大家好,我是一个完全不懂桌面应用开发的小白。但我用3分钟时间,在Trae这个AI编程工具里,通过SOLO模式+Agent Skills + DeepSeek,搞出了一个桌面应用。这篇文章我就手把手教你,如何用AI编程工具,让AI帮你写代码、做软件,全程不需要你写一行代码!🔥

 🧠 第一步:搞清楚我们要用什么


先说几个关键词,其实很简单:


简单说:你在Trae里打开SOLO模式,装上合适的Skills,用DeepSeek当大脑,告诉AI你想做什么,AI就帮你把代码写出来


🛠️ 第二步:3分钟极速教程(真的只需要3分钟!)


第1分钟:安装Trae并配置DeepSeek
去Trae官网下载安装包(完全免费)

安装后打开,点击右上角的AI侧边栏

进入AI管理 → 模型设置

添加自定义模型,选择Provider为Novita(或其他支持DeepSeek的服务商)

从模型下拉框选择 DeepSeek-V3-0324(最新版,代码能力超强)

粘贴你的API Key(没有的话去Novita注册免费试用,一般送额度)

搞定!现在Trae的大脑已经换成DeepSeek了。

第2分钟:创建SOLO模式+安装Skills
在Trae中新建一个项目文件夹(比如叫「my-app」)

点击左下角切换到 SOLO模式(重要!只有SOLO模式才能用Skills)

在对话中输入:

帮我创建一个Skill,用于开发Electron桌面应用。Skill需要包含:项目初始化、窗口配置、多页面路由、打包配置等规范。

第3分钟:让AI生成你的追剧神器!
在SOLO模式对话中,输入以下指令:

用Electron帮我开发一个桌面应用,名字叫「剧多多」。

功能要求:
1. 点击按钮,主窗口直接跳转到对应平台的官网
2. 窗口大小设置为1280x720
3. 应用图标放在桌面,双击打开直接进入
4. 需要使用webview加载网页,确保V#I#P视频可以正常播放(保留登录态)

请使用我刚刚创建的Electron开发Skill,生成完整的项目代码。


然后——坐等奇迹发生!🎉

AI会开始思考、规划、生成代码。你只需要看到提示时点击「接受全部」就行。

生成架构图

生成代码

import os
import sys
from pathlib import Path
 
 
def _find_widevine_cdm():
    chrome_app_roots = []
    for env_name in ("PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA"):
        base = os.environ.get(env_name)

 
    candidates = []
    for root in chrome_app_roots:
        if not root.exists():
            continue
        try:
            candidates.extend(
                list(root.glob("WidevineCdm/*/_platform_specific/win_x64/widevinecdm.dll"))
            )
            candidates.extend(
                list(root.glob("*/WidevineCdm/*/_platform_specific/win_x64/widevinecdm.dll"))
            )
        except OSError:
            continue
 
    if not candidates:
        return None
 
    def version_key(p: Path):
        version = p.parents[2].name
        parts = []
        for x in version.split("."):
            try:
                parts.append(int(x))
            except ValueError:
                parts.append(0)
        return tuple(parts)
 
    best = max(candidates, key=version_key)
    version_dir = best.parents[2]
    return str(best), version_dir.name
 
 
def _configure_qtwebengine_widevine():
    found = _find_widevine_cdm()
    if not found:
        return
    cdm_path, cdm_version = found
    flags = os.environ.get("QTWEBENGINE_CHROMIUM_FLAGS", "")
    if "--widevine-cdm-path" in flags:
        return
    extra = (
        f' --widevine-cdm-path="{cdm_path}"'
        f" --widevine-cdm-version={cdm_version}"
        " --disable-features=AutomationControlled"
        " --disable-direct-composition"
        " --disable-accelerated-video-decode"
    )
    os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = (flags + extra).strip()
 
 
_configure_qtwebengine_widevine()
 
 
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QLineEdit, QPushButton, QComboBox,
                             QLabel, QMessageBox, QProgressBar, QGridLayout, QFrame, QScrollArea, QStackedWidget)
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEngineSettings, QWebEnginePage, QWebEngineScript, QWebEngineUrlRequestInterceptor
from PyQt6.QtCore import QUrl, Qt, QSize, QStandardPaths, QTimer
from PyQt6.QtGui import QIcon, QAction, QFont, QCursor, QDesktopServices
import config
try:
    from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
    from PyQt6.QtMultimediaWidgets import QVideoWidget
    MULTIMEDIA_AVAILABLE = True
except Exception:
    QMediaPlayer = None
    QAudioOutput = None
    QVideoWidget = None
    MULTIMEDIA_AVAILABLE = False

VIDEO_PLATFORMS = []
 
class WebEngineView(QWebEngineView):
    def createWindow(self, _type):
        return self
 
class VideoUrlInterceptor(QWebEngineUrlRequestInterceptor):
    def __init__(self, handler):
        super().__init__()
        self.handler = handler
 
    def interceptRequest(self, info):
        url = info.requestUrl().toString()
        if not url:
            return
        lower = url.lower()
        if ".m3u8" in lower or ".mp4" in lower:
            self.handler(url)
 
class v_i_pVideoPlayer(QMainWindow):
    def __init__(self):
        super().__init__()

        self.resize(1280, 900)
        self.setStyleSheet("background-color: #4DB6AC;") # 全局青色背景
        self.h264_supported = None
        self.pending_parse = False
        self.last_video_url = ""
        self.player_available = MULTIMEDIA_AVAILABLE
        self.media_player = None
        self.audio_output = None
        self.video_widget = None
        
        # 初始化界面
        self.init_ui()
        
        # 加载默认首页
        self.load_url(config.DEFAULT_HOME_URL)
 
    def init_ui(self):
        # 主窗口部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        central_widget.setLayout(main_layout)
        
        # --- 1. 顶部 Header 区域 ---
        header_widget = QWidget()
        header_widget.setStyleSheet("background-color: #00695C; color: white;")
        header_widget.setFixedHeight(50)
        header_layout = QHBoxLayout()
        header_layout.setContentsMargins(20, 0, 20, 0)
        header_widget.setLayout(header_layout)

        title_label.setFont(QFont("Microsoft YaHei", 14, QFont.Weight.Bold))
        title_label.setStyleSheet("color: white;")
        
 
        info_label.setFont(QFont("Microsoft YaHei", 10))
        info_label.setStyleSheet("color: #E0F2F1;")
        
        header_layout.addWidget(title_label)
        header_layout.addStretch()
        header_layout.addWidget(info_label)
        
        main_layout.addWidget(header_widget)
        
        # --- 2. 控制/搜索区域 ---
        control_container = QWidget()
        control_container.setStyleSheet("background-color: #4DB6AC;") # 青色背景
        control_layout = QHBoxLayout()
        control_layout.setContentsMargins(20, 15, 20, 15)
        control_layout.setSpacing(10)
        control_container.setLayout(control_layout)
        
        # 导航按钮组 (小一点)
        nav_layout = QHBoxLayout()
        nav_layout.setSpacing(5)
        
        btn_style_nav = """
            QPushButton {
                background-color: rgba(255, 255, 255, 0.2);
                color: white;
                border: none;
                border-radius: 4px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: rgba(255, 255, 255, 0.4);
            }
        """
        
        self.btn_back = QPushButton("←")
        self.btn_back.setFixedSize(30, 30)
        self.btn_back.setStyleSheet(btn_style_nav)
        self.btn_back.clicked.connect(self.navigate_back)
        
        self.btn_forward = QPushButton("→")
        self.btn_forward.setFixedSize(30, 30)
        self.btn_forward.setStyleSheet(btn_style_nav)
        self.btn_forward.clicked.connect(self.navigate_forward)
        
        self.btn_refresh = QPushButton("↻")
        self.btn_refresh.setFixedSize(30, 30)
        self.btn_refresh.setStyleSheet(btn_style_nav)
        self.btn_refresh.clicked.connect(self.reload_page)
        
        self.btn_home = QPushButton("首页")
        self.btn_home.setFixedSize(50, 30)
        self.btn_home.setStyleSheet(btn_style_nav)
        self.btn_home.clicked.connect(lambda: self.load_url(config.DEFAULT_HOME_URL))
        
        nav_layout.addWidget(self.btn_back)
        nav_layout.addWidget(self.btn_forward)
        nav_layout.addWidget(self.btn_refresh)
        nav_layout.addWidget(self.btn_home)
        

        label_addr.setAlignment(Qt.AlignmentFlag.AlignCenter)
        label_addr.setFixedSize(120, 40)
        label_addr.setStyleSheet("""
            background-color: #2ECC71; 
            color: white; 
            border-radius: 4px; 
            font-size: 12px;
        """)
        
        # 地址输入框
        self.url_input = QLineEdit()
        self.url_input.setPlaceholderText("在此输入或粘贴视频网址...")
        self.url_input.setFixedHeight(40)
        self.url_input.setStyleSheet("""
            QLineEdit {
                border: 2px solid #2ECC71;
                border-radius: 4px;
                padding: 0 10px;
                font-size: 14px;
                background-color: white;
            }
        """)
        self.url_input.returnPressed.connect(self.load_from_input)
        
        # 线路选择 (稍微美化)
        self.combo_interface = QComboBox()
        self.combo_interface.setFixedHeight(40)
        self.combo_interface.setMinimumWidth(120)
        self.combo_interface.setStyleSheet("""
            QComboBox {
                border: 2px solid #2ECC71;
                border-radius: 4px;
                padding: 0 10px;
                background-color: white;
            }
            QComboBox::drop-down {
                border: none;
            }
        """)
        for item in config.v_i_p_INTERFACES:
            self.combo_interface.addItem(item["name"], item["url"])
 
        # 立即播放按钮
        self.btn_play = QPushButton("立即播放")
        self.btn_play.setFixedSize(100, 40)
        self.btn_play.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.btn_play.setStyleSheet("""
            QPushButton {
                background-color: #2ECC71; 
                color: white; 
                font-weight: bold; 
                font-size: 14px;
                border-radius: 4px; 
                border: none;
            }
            QPushButton:hover {
                background-color: #27ae60;
            }
        """)
        self.btn_play.clicked.connect(self.parse_video)
 
        # 用浏览器打开按钮
        self.btn_open_browser = QPushButton("浏览器播放")
        self.btn_open_browser.setFixedSize(100, 40)
        self.btn_open_browser.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.btn_open_browser.setStyleSheet("""
            QPushButton {
                background-color: #3498DB; 
                color: white; 
                font-weight: bold; 
                font-size: 14px;
                border-radius: 4px; 
                border: none;
            }
            QPushButton:hover {
                background-color: #2980B9;
            }
        """)
        self.btn_open_browser.clicked.connect(self.open_in_system_browser)
        
        # 组装控制栏
        control_layout.addLayout(nav_layout)
        control_layout.addSpacing(15)
        control_layout.addWidget(label_addr)
        control_layout.addWidget(self.url_input, 1) # 拉伸
        control_layout.addWidget(self.combo_interface)
        control_layout.addWidget(self.btn_play)
        control_layout.addWidget(self.btn_open_browser)
        
        main_layout.addWidget(control_container)
        
        # --- 3. 提示文字 ---
        tip_widget = QWidget()
        tip_widget.setStyleSheet("background-color: #4DB6AC;")
        tip_layout = QVBoxLayout()
        tip_layout.setContentsMargins(0, 5, 0, 10)
        tip_widget.setLayout(tip_layout)
        

        tip_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        tip_label.setStyleSheet("color: white; font-size: 12px;")
        tip_layout.addWidget(tip_label)
        
        main_layout.addWidget(tip_widget)
 

        web_container = QWidget()
        web_container.setStyleSheet("background-color: #4DB6AC; padding: 0px 20px;")
        web_layout = QVBoxLayout()
        web_layout.setContentsMargins(0, 0, 0, 0)
        web_container.setLayout(web_layout)
 
        self.browser = WebEngineView()
        self.browser.setStyleSheet("background-color: black;")
        if self.player_available:
            self.media_player = QMediaPlayer()
            self.audio_output = QAudioOutput()
            self.media_player.setAudioOutput(self.audio_output)
            self.video_widget = QVideoWidget()
            self.media_player.setVideoOutput(self.video_widget)
        

        try:
            data_path = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation)
            profile_path = os.path.join(data_path, "v_i_p_video_player_profile")
            cache_path = os.path.join(profile_path, "cache")
            
            if not os.path.exists(profile_path):
                os.makedirs(profile_path, exist_ok=True)
            if not os.path.exists(cache_path):
                os.makedirs(cache_path, exist_ok=True)
                
            profile = self.browser.page().profile()
            profile.setPersistentStoragePath(profile_path)
            profile.setCachePath(cache_path)
            self.video_interceptor = VideoUrlInterceptor(self.on_video_url_detected)
            profile.setUrlRequestInterceptor(self.video_interceptor)
            print(f"Profile storage path set to: {profile_path}")
        except Exception as e:
            print(f"Failed to set profile path: {e}")
 


        script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentCreation)
        script.setWorldId(QWebEngineScript.ScriptWorldId.MainWorld)
        script.setRunsOnSubFrames(True)
        self.browser.page().scripts().insert(script)
        
     
        settings = self.browser.settings()
        settings.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows, True)
        
    
        settings.setAttribute(QWebEngineSettings.WebAttribute.PlaybackRequiresUserGesture, False)
        settings.setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
        
        # 启用更多高级特性以提高兼容性
        settings.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.Accelerated2dCanvasEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False)
        settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
        settings.setAttribute(QWebEngineSettings.WebAttribute.HyperlinkAuditingEnabled, False)
        
        self.browser.urlChanged.connect(self.update_url_bar)
        self.browser.loadProgress.connect(self.update_progress)
        self.browser.titleChanged.connect(self.update_title)
        self.browser.loadFinished.connect(self.on_load_finished)
        
        if self.player_available and self.video_widget is not None:
            self.view_stack = QStackedWidget()
            self.view_stack.addWidget(self.browser)
            self.view_stack.addWidget(self.video_widget)
            web_layout.addWidget(self.view_stack)
        else:
            self.view_stack = None
            web_layout.addWidget(self.browser)
        main_layout.addWidget(web_container, 1) # 浏览器占据主要空间

        self.progress_bar = QProgressBar()
        self.progress_bar.setFixedHeight(2)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.setStyleSheet("QProgressBar { border: 0px; background-color: transparent; } QProgressBar::chunk { background-color: #2ECC71; }")
        main_layout.addWidget(self.progress_bar)
 
        # --- 5. 底部平台快捷栏 ---
        platform_widget = QWidget()
        platform_widget.setStyleSheet("background-color: #4DB6AC;")
        platform_layout = QVBoxLayout()
        platform_layout.setContentsMargins(20, 10, 20, 20)
        platform_widget.setLayout(platform_layout)
        

        bottom_tip.setAlignment(Qt.AlignmentFlag.AlignCenter)
        bottom_tip.setStyleSheet("color: white; font-size: 13px; margin-bottom: 10px;")
        platform_layout.addWidget(bottom_tip)
        
        # 图标布局 (单行显示)
        platforms_layout = QHBoxLayout()
        platforms_layout.setSpacing(15)
        
        # 生成按钮
        for platform in VIDEO_PLATFORMS:
            btn = QPushButton(platform["name"])
            btn.setFixedHeight(50)
            btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
            # 使用动态颜色样式
            btn.setStyleSheet(f"""
                QPushButton {{
                    background-color: white;
                    color: {platform['color']};
                    font-size: 16px;
                    font-weight: bold;
                    border-radius: 6px;
                    border: 1px solid #ddd;
                }}
                QPushButton:hover {{
                    background-color: #f9f9f9;
                    border: 2px solid {platform['color']};
                }}
            """)
            # 使用闭包绑定URL
            btn.clicked.connect(lambda checked, url=platform["url"]: self.load_url(url))
            
            platforms_layout.addWidget(btn)
        
        platform_layout.addLayout(platforms_layout)
        main_layout.addWidget(platform_widget)
 
 
    # --- 逻辑功能 ---
 
    def load_url(self, url_str):
        """加载URL"""
        if not url_str.startswith('http'):
            url_str = 'https://' + url_str
        self.browser.load(QUrl(url_str))
 
    def load_from_input(self):
        """从地址栏加载"""
        url = self.url_input.text().strip()
        if url:
            self.load_url(url)
 
    def parse_video(self):

        

        self.pending_parse = True
        self.last_video_url = ""
        if self.view_stack is not None:
            self.view_stack.setCurrentWidget(self.browser)
        self.browser.load(QUrl(final_url))
 
    def open_in_system_browser(self):
        """在系统浏览器中打开当前页面"""
        QDesktopServices.openUrl(self.browser.url())
 
    def open_in_system_browser_url(self, url):
        QDesktopServices.openUrl(QUrl(url))
 
    def on_video_url_detected(self, url):
        if not self.pending_parse:
            return
        if url == self.last_video_url:
            return
        self.last_video_url = url
        if self.player_available and self.media_player is not None:
            self.play_video_url(url)
            self.pending_parse = False
 
    def play_video_url(self, url):
        if self.view_stack is not None and self.video_widget is not None:
            self.view_stack.setCurrentWidget(self.video_widget)
        self.media_player.setSource(QUrl(url))
        self.media_player.play()
 
    def navigate_back(self):
        self.browser.back()
 
    def navigate_forward(self):
        self.browser.forward()
 
    def reload_page(self):
        self.browser.reload()
 
    def update_url_bar(self, qurl):
        """更新地址栏显示"""
        self.url_input.setText(qurl.toString())
        self.url_input.setCursorPosition(0)
 
    def update_progress(self, progress):
        """更新进度条"""
        self.progress_bar.setValue(progress)
        if progress == 100:
            self.progress_bar.hide()
        else:
            self.progress_bar.show()
 
    def update_title(self, title):
        """更新窗口标题"""

 
    
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = v_i_pVideoPlayer()
    window.show()
    sys.exit(app.exec())

3、项目开发说明文档


✨ 发生了什么?AI到底做了什么?
你可能会好奇:这几句话,AI真的能做出一个完整的软件吗?

答案是:能!而且做得很好!

DeepSeek V3.1在代码生成和推理方面的能力非常强,AIME 2025基准测试得分88.4%,接近GPT-5的水平。加上Trae的Agent模式,AI可以:

1、理解你的需求:知道你要做一个聚合视频的桌面应用

2、调用Skill规范:按照Electron开发的最佳实践来组织代码

3、生成完整项目:包括主进程、渲染进程、package.json、打包配置等

4、处理技术细节:比如webview配置、跨域问题、登录态保持

整个过程就像你请了一个24小时待命的程序员,你说需求,他写代码,你只管验收!

🎯 最终成果展示
代码生成后,在项目目录下运行:

bash
npm start
npm start

你就会看到——「剧#多#多」桌面应用打开了!

如果你想打包成exe/dmg发给朋友,只需要告诉AI:

AI会自动修改package.json,添加打包配置。运行 npm run dist,安装包就出来了!

💡 小白常见问题
Q1:我真的一点代码都不会,能搞定吗?

A:完全可以! 我本人就是例子。全程只需要打字,AI写代码,你负责确认就行。

Q2:DeepSeek要钱吗?
A:DeepSeek本身是开源免费的
,但如果通过API调用,服务商可能会收少量费用。不过一般注册都送额度,做个这种小应用绰绰有余。

Q3:Skills到底是什么?必须自己创建吗?
A:Skill就是AI的「操作手册」
,告诉AI“做这类事情要按照这个流程”。你可以自己创建,也可以用别人分享的。网上有很多现成的Skill可以直接导入。

Q4:生成的软件安全吗?会不会有病毒?
A:代码都在你的电脑上生成
,你可以查看每一行代码。 如果不放心,可以让AI解释每段代码的作用。

🚀 进阶玩法:还能做什么?
学会了这个套路,你可以让AI帮你做:


只要你敢想,AI就能做!

📢 最后说几句
以前想做个软件,得学编程、学框架、踩无数坑,没几个月出不来。

现在有了Trae + DeepSeek + Agent Skills,普通人也能3分钟拥有自己的软件

我的「剧多多」已经打包好,想体验的朋友可以私信我,也可以按上面的教程自己做一个——自己做的用起来更香!

如果你按教程做出来了,欢迎在评论区晒图!有任何问题随时问,我看到就回。👇

#AI编程 #DeepSeek #Trae #零代码开发 #追剧神器 #个人项目 #效率工具

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐