wxPython DatePicker 及日期计算踩坑记录

背景

这一次是在做backtrader的回测的过程中,试图用窗口界面来快速进行 多股票+多策略+多时间段的三重循环回测。当前选用的是wxPython做窗口界面。

关于多时间段上,默认是当前日期为回测结束日期,可选择DatePicker控件进行日期设定;而开始日期,设置了近半年,近1年以及近2年等,即从结束日期往回减去180、365、730天得到。

  • python 版本为3.11
  • wxPython 版本为4.2.1

踩坑记录

或许是wxPython有些小众,使用的人不多,网上搜索到的资料也不多;另外wxPython最近的更新似乎与旧版本差别有点大,而搜索出来的很多资料都是旧版本的。所以,在实际的应用过程中几乎是一步一个坑,效率非常低下。

原来以为AI能快速帮我解决问题,结果却是发现AI能快速帮我制造问题,然后还要我一步一步地导引它来发现错误。从这个过程中,我不断感觉到,目前的一些AI还是通过搜索网上的资料并进行整理,所以如果最容易被搜索到的资源上面就是错误的,那AI怎么分辨呢?三人成虎,AI也会认为是老虎吧......

DatePicker控件库

首先,我问AI : wxpython DatePicker控件获取日期,并计算30天前的日期的python代码。

然后AI回答给了我一段代码,我复制到Spyder中一运行,先给我来了第一个错误

AttributeError: module 'wx' has no attribute 'DatePickerCtrl'

以前用过老版本wxPython的人疯了,因为旧版本上这个是可以运行的。不过还是有点滴线索可查,例如wxFormbuilder也已经升级了,在新版的wxFormbuilder中添加一个DatePickerCtrl,检查代码能看到它是wx.adv.DatePickerCtrl,也就是成了adv的一部分。

把这个错误直接复制回给AI,AI这点还不错,它知道问题了:

这可能是因为您使用的是wxPython Phoenix版本,
在该版本中DatePickerCtrl位于wx.adv模块中,而不是直接在wx模块中。

然后,重新给了一段更改过的代码,出现新的报错。

wx.DateTime的计算

先来看一下新的代码的片段

    def OnDateChanged(self, event):
        # 获取选择的日期
        selected_date = self.datePicker.GetValue()
        # 计算30天前的日期
        thirty_days_ago = selected_date - wx.DateTime.Days(30)
        print("30天前的日期是:", thirty_days_ago.Format())

这里使用了wx.DateTime.Days(),但运行是会报错的:

AttributeError: type object 'DateTime' has no attribute 'Days'. 
Did you mean: 'day'?

把这个错误回给AI,AI给出的回答如下

在wxPython中,要创建一个表示天数的wx.DateTime对象,
应该使用wx.DateTime的构造函数并传递相应的天数。
以下是修正后的示例代码,演示了如何计算30天前的日期:
    def OnDateChanged(self, event):
        # 获取选择的日期
        selected_date = self.datePicker.GetValue()
        # 创建一个表示30天的DateTime对象
        thirty_days = wx.DateTime(30, wx.DateTime_Month_Today, wx.DateTime_Day_Today)
        # 计算30天前的日期
        thirty_days_ago = selected_date - thirty_days
        print("30天前的日期是:", thirty_days_ago.FormatISODate())

再复制运行,还是报错

AttributeError: module 'wx' has no attribute 'DateTime_Month_Today'

到此,已经让我对AI有了深深的怨念。我尝试着问:是否有TimeSpan或DateSpan的函数来计算,于是AI又回答我

当然可以,wx.TimeSpan 类在 wxPython 中用于表示两个日期之间的时间差。以下是使用 wx.TimeSpan 来计算30天前的日期的示例代码:

    def OnDateChanged(self, event):
        # 获取选择的日期
        selected_date = self.datePicker.GetValue()
        # 创建一个表示30天的时间跨度
        time_span = wx.TimeSpan(30)
        # 计算30天前的日期
        thirty_days_ago = selected_date - time_span
        # 格式化输出
        print("30天前的日期是:", thirty_days_ago.Format("%Y-%m-%d"))

这次好歹是运行成功了,但是,请注意,结果仍然是错误的,比如我选择了2023-8-19后,程序给出的输出却是2023-8-17。好吧,又扔回一个问题给我,TimeSpan的计量单位是什么?

于是乎,自己再去试一下x24(一天24小时) , x24* 3600 (1小时3600秒),然后得到答案是TimeSpan的计量单位是小时,而使用TimeSpan计算天数的话需要乘上24。

既然有TimeSpan,那么应该有DateSpan才对,我都自己代码编写后运行成功了,然后问AI能用wx.DateSpan来计算30天前的日期吗?

回答是:wxPython 中并没有 wx.DateSpan 这个类。在 wxPython 中用于表示时间间隔的是 wx.TimeSpan 类。wx.TimeSpan 可以用来表示两个时间点之间的差异,包括天数、秒数、分钟数等。

关于wx.Add()的计算

接着,我在AI中又得到了一段代码

    def OnDateChanged(self, event):
        # 获取选择的日期
        selected_date = self.datePicker.GetValue()
        # 创建一个表示30天的时间跨度
        time_span = wx.TimeSpan(0, 0, 0, 30)  # 0年, 0月, 0周, 30天
        # 使用Add方法计算30天前的日期
        thirty_days_ago = selected_date.Add(time_span)
        # 格式化输出
        print("30天前的日期是:", thirty_days_ago.Format())

这里就出现2个问题,第1个是wx.TimeSpan()参数并不是年、月、周、天,而应该是时,分,秒,?,要得到30天前的日期,需要这样写

time_span = wx.TimeSpan(-30*24, 0, 0, 0)

第2个问题,使用.Add()方法的时候,变量值会被更改,导致当前日期和30天前日期变成了同一个值,这里就会导致我的窗口程序先获取结束日期后,用函数计算得到比如半年前的日期,但结果把结束日期也改成了半年前。

    def OnDateChanged(self, event):
        # 获取选择的日期
        selected_date = self.datePicker.GetValue()
        # 创建一个表示30天的时间跨度
        time_span = wx.TimeSpan(-30*24, 0, 0, 0)  # 0年, 0月, 0周, 30天
        # 使用Add方法计算30天前的日期
        thirty_days_ago = selected_date.Add(time_span)
        # 格式化输出
        print("30天前的日期是:", thirty_days_ago.Format())
        print("当前日期是:", selected_date.Format())

----------------
30天前的日期是: Thu Jul 20 00:00:00 2023
当前日期是: Thu Jul 20 00:00:00 2023

把wx.DateTime数据转换成backtrader接受的格式

这里问AI得到的结果是OK的:

    def OnDateChanged(self, event):
        # 从DatePickerCtrl获取wx.DateTime对象
        wx_date = event.GetDate()
        
        # 将wx.DateTime对象转换为Python的datetime对象
        py_datetime = datetime(wx_date.GetYear(), wx_date.GetMonth() + 1, wx_date.GetDay())
        
        # 将datetime对象转换为date对象
        bt_date = py_datetime.date()
        
        print("Selected Date for backtrader:", bt_date)
        print("Type of backtrader Date:", type(bt_date))

还有一种曲线的方式,即先把wx.DateTime转成str,再使用python的DateTime把它从字符串格式转回来

    def OnDateChanged(self, event):
        # 从DatePickerCtrl获取wx.DateTime对象
        wx_date = event.GetDate()
                
        s22 = wx_date.FormatISODate()
        s2 = datetime.strptime(s22, '%Y-%m-%d')
        
        print ('str format:', s22, type(s22))
        print('dateTime format',s2, type(s2))

------------------
str format: 2023-08-19 <class 'str'>
dateTime format 2023-08-19 00:00:00 <class 'datetime.datetime'>

小结

  1. wxPython 用AI得到的代码并不太靠谱
  2. 新版的wxPython的很多控件,包括DatePicker等在 wx.adv.里面
  3. wx.DateTime 要计算日期间隔 使用 current_date +/- wx.DateSpan(30)的方式
  4. 硬是要使用wx.TimeSpan则注意第1个参数是小时,换算成天要乘上24
  5. date2 = date1.Add()方法会更改date1的值与date2相同,慎用
  6. 把wx.DateTime格式转换成backtrader接受的格式可以用多种方式,可以直接datetime(wx.GetYear(),...)的方式,也可以曲线式的先转成str再得到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值