一. 前言
完蛋 ,一觉醒来发现自己穿越到了豆包新世界 ,成为了一个零基础的 Python 程序员
。
领导让写一个桌面应用
,用来看 股票 K 线图
,说对我而言小需求 ,明天就给他, 不然开了我。
可是 ,我不会啊!!!
好在电脑上环境已经配好了 ,还发现一个摸鱼用的 豆包
,离明天交付还有8小时 ,干了。
二. 儿郎们 ,赶紧操练起来
2.1 先确定项目选型
❓ 问 :Python 有什么好用的桌面端脚手架
❓ 问 : Git 上面有没有 PyQt5 的 Fluent Design 风格的组件库
❓ 问 : Python 有什么框架可以查询股票日线行情
总结 : 选型确定了!!!
很好 ,花了5分钟时间 ,了解了 Python 上有那些组件可以帮助我完成这个需求。
- 桌面框架 : PyQt-Fluent-Widgets
- 股票工具 : Tushare
- 图标工具 : Echats
题外话 : 个人感触
- 和之前一篇分析来比 ,
豆包返回的内容明显更丰富了 。回答的内容也更全面了
。 👍👍👍
2.2 还剩下 7小时55分钟 ,为了避免加班 ,赶紧写代码
❓ 问 :帮我用 PyQt-Fluent-Widgets + Tushare 实现一个查询日线行情的 Demo
👉 此时心态 : 白花了2分钟 ,回答不是很满意啊
这里虽然我给了明确的提示词 ,但是也由于引用了具体的项目 ,而项目并没有收录到模型中 ,所以回答的内容并不是我期望的内容。
或许 !!
是我用法不对 !!
2.3 摸了 1 小时鱼, 还剩下 6 小时 ,灵感来了
❓ PyQt-Fluent-Widgets 案例有吗 ?
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
from qfluentwidgets import (
NavigationInterface,
NavigationItemPosition,
NavigationWidget,
setTheme,
Theme,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt-Fluent-Widgets Example")
self.resize(800, 600)
# 创建导航界面
navigation_interface = NavigationInterface(self)
# 添加导航项
home_item = navigation_interface.addItem(
routeKey="home",
icon="home",
text="Home",
position=NavigationItemPosition.TOP,
)
about_item = navigation_interface.addItem(
routeKey="about",
icon="info",
text="About",
position=NavigationItemPosition.BOTTOM,
)
# 设置主题
setTheme(Theme.LIGHT)
# 将导航界面添加到主窗口
self.setCentralWidget(navigation_interface)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
- PS : 这里还问了要安装什么插件 :
- pip install PyQt6-Frameless-Window
- pip install PyQt6-Fluent-Widgets -i pypi.org/simple/
- pip install PyQt6
- pip install PyQt6-WebEngine
- pip install tushare
- 结论 :
- 当前进度 : 摸鱼一小时 ,工作10分钟 ,界面基本出来了
- 使用技巧 :
有的时候询问的题目很重要 ,如果加入太多了元素 ,答案反而给的不明朗
2.5 躲在会议室开了10把游戏 ,午休结束 ,还剩4小时,要好好工作啦
❓ 用 Tushare 帮我查询当天的日行情 ,单独放在一个类里面
- 结论 :
- 不容易啊 ,又工作了两分钟 , 开一把游戏奖励一下自己
- 可以看到 ,MarsCode 提供的类很好 ,还提供了调用案例
❓Python PyQT 里面如何使用 Echats 框架
- 结论 :
三. 开始聚合代码
3.1 开始进行组件的整合
❓ 问 : 帮我整合上面的 Echat ,Tushare , 和 qfluentwidgets ,让我可以在里面看到 K 线图
- 结论 :
- 这里 Python
3.2 省略一些小细节 ,我又花了半个小时和它聊了会天
❓问 : 我想要把 HTML 文本单独放在一个 .html 文件里面 ,再发起调用
- 结论 :
- 这里细节比较曲折 ,AI 的通病 ,有的时候比较笨,总的来说问了这些问题
-
- 我需要从 OS 里面读取 HTML 文件 ,然后在这里通过地址 load
- 这里是由于 Demo 没有正确从地址里面加载 ,所以需要明确的告诉他重写
-
- 我需要从 OS 里面读取 JSON 文件,并且进行占位符的替换
- 如果把操作抽象的更细 ,回答的结论就会更清晰
-
- 遍历替换 JSON 中得到占位符
-
- 我需要替换为 JSON 对象 ,而不是字符串
-
- 我需要... 这里陆陆续续改了很多版,达到了一个初步的效果
❓问 : 给我一个占位符替换代码 ,用于替换 JSON 文本里面的占位符
import json
class JsonPlaceholderReplacer:
def __init__(self, json_data):
# 将 JSON 字符串解析为 Python 字典
self.json_data = json.loads(json_data) if isinstance(json_data, str) else json_data
def replace_placeholders(self, replacements):
"""
使用一个包含占位符和对应值的字典批量替换 JSON 数据中的占位符。
:param replacements: dict, 包含 {占位符: 值} 的键值对
"""
for placeholder, value in replacements.items():
self._replace_recursive(self.json_data, placeholder, value)
def replace_placeholder(self, key, value):
self._replace_recursive(self.json_data, key, value)
def _replace_recursive(self, data, placeholder, value):
"""递归地遍历和替换 JSON 数据中的占位符"""
if isinstance(data, dict):
for key, val in list(data.items()):
if isinstance(val, (dict, list)):
self._replace_recursive(val, placeholder, value)
elif val == placeholder:
# 如果值完全匹配占位符,将其替换为对象或列表
data[key] = value
elif isinstance(data, list):
for index, item in enumerate(data):
if isinstance(item, (dict, list)):
self._replace_recursive(item, placeholder, value)
elif item == placeholder:
# 如果列表中的项完全匹配占位符,将其替换为对象或列表
data[index] = value
def get_modified_json(self):
"""返回替换后的 JSON 字符串"""
return json.dumps(self.json_data, ensure_ascii=False, indent=4)
其中还发生了很多追问行为 :
- 更复杂一点 ,传入的是一个JSON 对象
- 按照方法来 def replace_placeholders(self, replacements) + def get_modified_json(self)
- 继续优化 ,递归地遍历和替换 JSON 数据中的占位符
3.3 最后冲刺 : 马上就要发版了
问 : 帮我优化一下代码 ,让他可以运行
from datetime import datetime, timedelta
import json
import sys
import os
import traceback # 用于打印异常栈信息
from typing import List
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtCore import QUrl
import pandas as pd
from qfluentwidgets import (
NavigationInterface,
NavigationItemPosition,
setTheme,
Theme,
)
# 检查外部依赖是否导入成功
try:
from JsonReplace import JsonPlaceholderReplacer # 需确保本地存在该模块
from tushare_das import getDayStock # 需确保本地存在该模块
except ImportError:
print("缺少必要的模块 `JsonReplace` 或 `tushare_das`,请检查后重试。")
sys.exit(1)
class StockChartWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Stock Chart with PyQt-Fluent-Widgets and Echarts")
self.resize(1200, 800)
# 设置主题
setTheme(Theme.LIGHT)
# 创建导航界面
navigation_interface = NavigationInterface(self)
navigation_interface.addItem(
routeKey="home",
icon="home",
text="Home",
position=NavigationItemPosition.TOP,
)
navigation_interface.addItem(
routeKey="about",
icon="info",
text="About",
position=NavigationItemPosition.BOTTOM,
)
# 创建布局和控件
layout = QVBoxLayout()
self.web_view = QWebEngineView()
# 添加控件到布局
layout.addWidget(self.web_view)
# 创建中央控件并设置布局
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
# 加载HTML文件
self.do_load_chat()
def do_load_chat(self):
try:
current_dir = os.path.dirname(os.path.abspath(__file__))
chat_html_file = "stock_day.html"
chat_path = os.path.join(current_dir, chat_html_file)
if not os.path.exists(chat_path):
raise FileNotFoundError(f"{chat_html_file} 文件不存在")
self.web_view.load(QUrl.fromLocalFile(chat_path))
self.web_view.loadFinished.connect(self.do_load_data)
except Exception as e:
print(f"加载HTML失败: {e}")
def do_load_data(self):
try:
current_dir = os.path.dirname(os.path.abspath(__file__))
chat_json_file = "stock_day.json"
local_file_path = os.path.join(current_dir, chat_json_file)
if not os.path.exists(local_file_path):
raise FileNotFoundError(f"{chat_json_file} 文件不存在")
with open(local_file_path, "r", encoding="utf-8") as file:
json_data = json.load(file)
# 调用函数方法进行解析
chat_data = self.data_exchange(json_data)
js_code = f"setData({chat_data});"
print(f"最终写入参数: {js_code}")
self.web_view.page().runJavaScript(js_code, self.echat_callback)
except Exception as e:
print(f"加载数据失败: {e}")
def echat_callback(self, result):
print(f"JavaScript Result: {result}")
def data_exchange(self, json_data: dict) -> str:
try:
today = datetime.today()
end_time = today.strftime("%Y%m%d")
start_time = (today - timedelta(days=180)).strftime("%Y%m%d")
stock_data = getDayStock("600519.SH", start_time, end_time)
# 检查数据是否为空
if stock_data is None or stock_data.empty:
raise ValueError("股票数据为空,无法解析")
dates_data = self.get_dates(stock_data)
kline_data = self.get_kline_data(stock_data)
point_data = self.get_point_data(stock_data)
mark_data = self.get_markline_data(stock_data)
replacer = JsonPlaceholderReplacer(json_data)
replacer.replace_placeholder("__DATAS__", dates_data)
replacer.replace_placeholder("__STOCK_NAME__", "写死一个骗骗老板")
replacer.replace_placeholder("__STOCK_DATA__", kline_data)
replacer.replace_placeholder("__POINT_DATA__", point_data)
replacer.replace_placeholder("__MARK_DATA__", mark_data)
return replacer.get_modified_json()
except Exception as e:
# 打印异常栈信息
print(f"数据处理失败: {e}")
print("异常栈信息如下:")
print(traceback.format_exc())
return "{}"
def get_kline_data(self, stock_data: pd.DataFrame) -> List[List[float]]:
"""
从股票数据中提取 K 线图数据。
:param stock_data: Tushare 返回的 DataFrame,包含股票每日交易数据
:return: K 线图数据 [[open, close, low, high], ...]
"""
if stock_data.empty:
print("股票数据为空,无法生成 K 线数据")
return []
required_columns = ["open", "close", "low", "high"]
missing_columns = [
col for col in required_columns if col not in stock_data.columns
]
if missing_columns:
raise ValueError(f"缺少以下必要列,无法生成 K 线数据: {missing_columns}")
# 使用 DataFrame 的 values 提取多列数据为二维列表
kline_data = stock_data[["open", "close", "low", "high"]].values.tolist()
print(f"K 线数据生成成功,共 {len(kline_data)} 条记录")
return kline_data
def get_dates(self, stock_data: pd.DataFrame) -> List[str]:
"""
从股票数据中提取并格式化交易日期。
:param stock_data: Tushare 返回的 DataFrame,包含股票每日交易数据
:return: 格式化后的日期列表 ['2023-01-01', '2023-01-02', ...]
"""
if stock_data.empty:
print("股票数据为空,无法生成日期列表")
return []
if "trade_date" not in stock_data.columns:
raise ValueError("数据中缺少 'trade_date' 列,无法生成日期列表")
# 使用 apply 格式化日期列
dates = (
stock_data["trade_date"]
.apply(lambda x: datetime.strptime(str(x), "%Y%m%d").strftime("%Y-%m-%d"))
.tolist()
)
print(f"日期列表生成成功,共 {len(dates)} 条记录")
return dates
def get_point_data(self, stock_data) -> List[dict]:
"""
生成包含最高值和最低值的点数据。
参数:
stock_data (DataFrame): 包含股票数据的 DataFrame。
返回:
List[dict]: 包含最高值和最低值点数据的列表。
"""
point_data = [
{"name": "最高值", "type": "max", "valueDim": "highest"},
{"name": "最低值", "type": "min", "valueDim": "lowest"},
]
return point_data
def get_markline_data(self, stock_data: pd.DataFrame) -> dict:
"""
生成辅助线 (markLine) 数据,用于标注收盘价的平均值。
:param stock_data: Tushare 返回的 DataFrame,包含股票每日交易数据
:return: 包含辅助线数据的字典
"""
if stock_data.empty:
print("股票数据为空,无法生成平均线数据")
return {"data": []}
if "close" not in stock_data.columns:
raise ValueError("数据中缺少 'close' 列,无法生成平均线数据")
# 计算收盘价平均值
average_price = stock_data["close"].mean()
print(f"收盘价平均值计算成功: {average_price:.2f}")
# 构造辅助线数据
return {
"data": [
{
"yAxis": average_price,
"name": "平均值",
"lineStyle": {
"color": "#FF0000", # 虚线颜色
"type": "dashed", # 虚线样式
"width": 2, # 线条宽度
},
"label": {
"formatter": f"平均值: {average_price:.2f}", # 显示均值
"position": "end", # 文本位置
"color": "#FF0000", # 文本颜色
},
}
]
}
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StockChartWindow()
window.show()
sys.exit(app.exec())
3.5 终于写完了 ,还能打几把游戏下班
四. 我用 AI 写了几万行代码了 ,我有很多建议分享给大家
我最近一直在用 AI 代码工具帮我生成代码 ,我也已经实现了一个开源的框架 ,用来做股票风险评估。
可以说 ,其中 90% 的代码都是 AI 生成的 ,我完全相信 ,未来程序员的就业压力会越来越大
.
因为哪怕一个零基础的 Python 程序员 ,也能在一周时间开发出一个看得过去的桌面应用。 甚至于大多数情况下都是睡前写一个小时代码。 这你慌不慌???
但是 , 他并不会替代我们 ,它终究只是工具 ,你需要有一定技巧的和他沟通 ,它才能给你准确的答复。
- 关键一 : 避免问一些宽泛的东西 , 明确的说明你需要的功能点
- 关键二 : 避免提问给出太多的内容,拆分成一个个方法问
- 关键三 : 仿写代码时 ,要提供足够的前置条件 ,可以通过
"记住这个..."
这种语法实现 - 关键四 : 不满意进行追问 ,追问要指明明确的目的
我真的非常建议大家积极使用 AI 编程, 它就像一个结伴编程一样 ,可以帮我们解决大多数重复性的问题。
比如我正在做的开源项目 , 其中这里面的代码都是 AI 帮忙写的 ,花了30分钟不到 ,大概有十几个类 :
五. 未来的展望
当然和 ChatGPT 这些对比差距还是很明显的 ,我最喜欢的一个内容是让 AI 帮我仿写代码。比如 GPT 我会这么问 :
- 但是在使用豆包的时候效果就很不理想 :
- 甚至我要坦白 ,这个 Demo 里面最后的一些贯穿的环节 ,豆包并没有帮我完成
- 在此期间我
换着很多种方式进行了提问
,效果都不理想 - 不论是拆分还是合并问 ,最后变化的结果总是会有新的问题。所以最后还是让 GPT 完成了
- 在此期间我
总结
其实这个 Demo 在之前的文章里面就出现过一次 ,里面的东西都是 ChatGPT 提供的。
在这个整体思路下 ,我通过 豆包 再次尝试走了一遍
。 也通过指定的方式刻意的引导了回答的方向。
整个过程中 ,豆包帮我完成了 80% 的代码
,我个人还是改了 5%
左右(主要是把 Demo Copy 进来后 ,要改调用方式) ,剩下了 15%
是在串联运行的过程中一直不成功, 让 ChatGPT 来实现的。
整个过程实际耗时 30 - 40
分钟 ,其中大部分时间都在等结果. 实际写代码的时间应该不超过20
分钟。
豆包 和 我年中使用的时候 ,已经能基本满足我的业务需求了
。我相信未来会更好,加油!!
而我 ,确实是一个 Python 新人 ,这才是最可怕的!!!
拥抱 AI ,别被抛弃。 ❗❗❗❗❗
最后的最后 ❤️❤️❤️👇👇👇
- 👈 欢迎关注 ,超200篇优质文章,未来持续高质量输出 🎉🎉
- 🔥🔥🔥 系列文章集合,高并发,源码应有尽有 👍👍
- 走过路过不要错过 ,知识无价还不收钱 ❗❗