2 Star 4 Fork 3

(C)/获取基金持仓

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
View.py 147.95 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005
from Init import *
import tkinter as tk
from tkinter import messagebox
import time
# 网络初始化相关 =============================
NETTIME_STAMP = None # 网络时间戳
NETTIME_ARRAY = None # 网络时间数组
NETTIME = None # 网络时间字符串 '2021-07-11 15:14:07'
NETTIME_D = None # 网络日期('2021-07-11')
NETTIME_T = None # 网络时间('15:14:07')
# --【TIMESTART】--
# 为了不频繁访问获取时间的接口,软件打开时获取一次网络时间,同时开启计时
# 当前网络时间戳 = 当前计时时间戳 - 开始计时时间戳 + 网络时间戳
TIMESTART = None
def handle_time(time_stamp):
"""时间处理"""
global NETTIME_ARRAY, NETTIME, NETTIME_T, NETTIME_D
# tm_year 2008
# tm_mon 1 到 12
# tm_mday 1 到 31
# tm_hour 0 到 23
# tm_min 0 到 59
# tm_sec 0 到 61 (60或61 是闰秒)
# tm_wday 0到6 (0是周一)
# tm_yday 一年中的第几天,1 到 366
# tm_isdst 是否为夏令时,值有:1(夏令时)、0(不是夏令时)、-1(未知),默认 -1
# 时间数组 (tm_year=2021, tm_mon=7, tm_mday=11, tm_hour=15, tm_min=17, tm_sec=50, tm_wday=6, tm_yday=192, tm_isdst=0)
NETTIME_ARRAY = time.localtime(time_stamp)
# 将时间数组转换为字符串格式 '2021-07-11 15:14:07'
NETTIME = time.strftime('%Y-%m-%d %H:%M:%S', NETTIME_ARRAY) # 转换为时间字符串
NETTIME_D, NETTIME_T = NETTIME.split(' ') # 分割为日期('2021-07-11') 和 时间('15:14:07')
def calibration_time(often=False):
"""
校准网络时间,如果校准时间成功全局变量 NETTIME_STAMP 为 int 类型
其他说明,校准失败:
【'Time out'】 - 请求超时
【'Network error'】 - 网络连接错误
:param often: 是否开启无延时访问
"""
global NETTIME_STAMP, TIMESTART
# 访问东方财富官网
NETTIME_STAMP = Inet.connect_dfcf(often=often) # 返回如果成功,返回int类型秒级时间戳
if isinstance(NETTIME_STAMP, int):
TIMESTART = time.time()
handle_time(NETTIME_STAMP)
def calc_time():
"""
计算网络时间
:return:
【Ture】 - 计算时间成功
【False】 - 计算时间失败
"""
if isinstance(NETTIME_STAMP, int):
differ = time.time() - TIMESTART
handle_time(TIMESTART+differ)
return True
else:
return False
def isopen():
"""
判断当前交易状态
交易前:0:00 - 9:30 [0 - 570]
交易中场休息:11:31 - 13:00 [691 - 780]
交易中:9:31 - 11:30 or 13:01 - 15:00 [571 - 690] or [781 - 900]
交易结束:15:01 - 24:00 [901 - 1440]
获取当前时间戳,当前时间戳 = 获取的网络时间戳 + (开始计时cpu时间戳 - 当前cpu时间戳)
:return:
【int】 -
>0 处在交易前或者中场休息。数值为需要还有多久开始交易
<0 交易已结束
0 交易中
"""
# 将时间转换为分钟
nowtime_min = NETTIME_ARRAY.tm_hour * 60 + NETTIME_ARRAY.tm_min
# print(f"当前时间:{NETTIME_ARRAY.tm_hour}:{NETTIME_ARRAY.tm_min}. 分钟:{nowtime_min}")
# test
# nowtime_min = 570
sleep_s = -1
# 判断交易时间
if nowtime_min < 571:
sleep_s = 571 - nowtime_min
# print(f'交易前,交易倒计时(min): {sleep_s}')
elif nowtime_min > 900:
sleep_s = -1
# print(f'交易结束,交易倒计时(min): {sleep_s}')
elif 690 < nowtime_min < 781:
sleep_s = 781 - nowtime_min
# print(f'交易中场休息,交易倒计时(min): {sleep_s}')
elif 571 <= nowtime_min <= 690 or 781 <= nowtime_min <= 900:
sleep_s = 0
# print(f'交易中,交易倒计时(min): {sleep_s}')
return sleep_s * 60
def only_number(content):
"""
限制Entry只能输入数字
:param content:
:return:
"""
# 如果不加上==""的话,就会发现删不完。总会剩下一个数字 isdigit函数:isdigit函数方法检测字符串是否只由数字组成。
if content.isdigit() or content == "":
return True
else:
return False
def handle_paging(ready_list, page_num):
"""
处理分页
:param ready_list: 需要分页的数据列 [[],[],[]]
:param page_num: 每页的数量
:return:
处理后的分页列表
[[1,2],[1,2],]
"""
# print(f'需要处理的列表:{ready_list}')
# 判断分页数量
page_list = [] # 处理完分页的数据
total_num = len(ready_list) # 数据总条目
if total_num > page_num:
# 需要分页
aline_num = 0 # 如果 aline_num == page_num. 一页
apage = [] # 一页的数据
for cell in ready_list:
apage.append(cell)
aline_num += 1 # 一页数据标志位
if aline_num == page_num:
# 一页数据处理完成
page_list.append(apage)
apage = [] # 清空一页,准备下一页
aline_num = 0
# 判断是否整除,不整除,说明有溢出的数据
if total_num % page_num != 0:
page_list.append(tuple(apage))
else:
# 如果总数据条码 <= 分页数据条码。直接一页
page_list.append(tuple(ready_list))
return tuple(page_list)
def draw_tb_line(cvs, cvs_atts, tt=(1, 1), line_wight=SIZE_TBLINE_WIDTH, line_color=COLOR_TBLINE):
"""
绘制表格线条
:param cvs: 画布对象
:param cvs_atts: 表头画布属性
:param tt: 顶部线还是底部线
:param line_wight: 线条宽度
:param line_color: 线条颜色
:return:
"""
if tt[0]:
# 绘制上方线条
top_line = (0, 0, cvs_atts['width'], 0)
cvs.create_line(top_line, width=line_wight, fill=line_color)
if tt[1]:
# 绘制下方线条
bottom_line = (0, cvs_atts['height'] - 1, cvs_atts['width'], cvs_atts['height'] - 1)
cvs.create_line(bottom_line, width=line_wight, fill=line_color)
def gets_jjheld(jjinfo_list: list, year):
"""
遍历获取基金持仓
:param jjinfo_list: 基金信息列表 [[基金代码, 基金名称],......]
:param year: 获取哪一年的
:return:
【'Time out'】 - 请求超时
【'Network error'】 - 网络连接错误
【list】 - [
{
'jjInfo': ['519674', '银河创新成长混合'],
'jjHeld': [
[
'//quote.eastmoney.com/sz300661.html', '1', '300661', '圣邦股份', '', '', '变动详情股吧行情档案',
'10.45%', '1,164.74', '294,365.42'], ['//quote.eastmoney.com/sh603986.html', '2', '603986',
'兆易创新', '', '', '变动详情股吧行情档案', '10.22%', '1,532.52', '287,960.56'
], ......
]
},
......
]
"""
jjheld_list = [] # 存储基金持仓数据
# [
# {
# 'jjInfo': ['519674', '银河创新成长混合'],
# 'jjHeld': [
# [
# '//quote.eastmoney.com/sz300661.html', '1', '300661', '圣邦股份', '', '', '变动详情股吧行情档案',
# '10.45%', '1,164.74', '294,365.42'], ['//quote.eastmoney.com/sh603986.html', '2', '603986',
# '兆易创新', '', '', '变动详情股吧行情档案', '10.22%', '1,532.52', '287,960.56'
# ], ......
# ]
# }, ......
# ]
# 遍历基金,获取基金持仓
for jjinfo in jjinfo_list:
print(f'正在获取:{jjinfo} 持仓信息!')
# 将获取到的基金信息格式统一
# 'jjInfo': [基金代码, 基金名称]
noe_jj = {
'jjInfo': jjinfo,
'jjHeld': None
}
# 按照基金代码查询历史持仓
response = Inet.get_jjheld(jjinfo[0], year)
if not isinstance(response, dict):
if response is None:
# 该基金暂无最新持仓
print(f'{jjinfo[1]} {year} 暂无持仓!')
response = {
'持仓': {
'数据': []
}
}
else:
# 其他情况, 即('Time out','Network error')直接结束
return response
# 存储一只基金的持仓数据
noe_jj['jjHeld'] = response['持仓']['数据']
# 汇总基金信息
jjheld_list.append(noe_jj)
return jjheld_list
def td_byjjrank(ft: str, sc: str, pn: int):
"""
按照基金排名,获取基金持仓,并生成推荐指数
:param ft: 类型(全部:all 股票:gp 混合:hh 指数:zs)
:param sc: 排行时间段(近一周:zzf 近1月:1yzf 近3月:3yzf 近6月:6yzf 近1年:1nzf 近3年:3nzf)
:param pn: 显示排行数前多少名
:return:
【dict】 - {
'TabelName': '按基金排行(全部,近1月,前50)',
'data': -
【'Time out'】 - 请求超时
【'Network error'】 - 网络连接错误
【'Response error'】 - 按基金名称查询接口,响应内容可能变更
【dict】 - 数据分析成功
}
"""
# 获取前端选择参数
option = {
'ft': {
'all': '全部',
'gp': '股票',
'hh': '混合',
'zs': '指数',
},
'sc': {
'zzf': '近1周',
'1yzf': '近1月',
'3yzf': '近3月',
'6yzf': '近6月',
'nzf': '近1年',
'3nzf': '近3年',
},
}
tdResult = {
'TabelName': '按基金排行(' + option['ft'][ft] + ',' + option['sc'][sc] + ',前' + str(pn) + ')',
'data': None
}
# 获取基金排名
jjrank = Inet.get_jjrank(ft, sc, pn)
if not isinstance(jjrank, list):
# 访问出错
tdResult['data'] = jjrank
return tdResult
print(f'按基金排行,获取到的基金排名: {jjrank}')
# [
# [
# 0_"基金代码", 1_"基金简称", 2_"首字母", 3_"最新日期", 4_"最新净值", 5_"累计净值", 6_"日增长率", 7_"近1周收益率",
# 8_"近1月收益率", 9_"近3月收益率", 10_"近6月收益率", 11_"近1年收益率", 12_"近2年收益率", 13_"近3年收益率",
# 14_"今年来收益率", 15_"成立以来收益率", 16_"成立日期", 17_"Unknown1", 18_"自定义收益率", 19_"Unknown2",
# 20_"Unknown3", 21_"Unknown4", 22_"手续费", 23_"Unknown5"
# ], ......
# ]
# 校准时间
calibration_time()
if not isinstance(NETTIME_STAMP, int):
# 校准网络时间失败
tdResult['data'] = NETTIME_STAMP
return tdResult
year = NETTIME_ARRAY.tm_year
# print(f'当前年份:{year}')
# 处理获取到的基金列表
jjinfo_list = []
for noe in jjrank:
jjinfo_list.append([noe[0], noe[1]])
# 获取基金持仓
jjheld_list = gets_jjheld(jjinfo_list, year)
if not isinstance(jjheld_list, list):
tdResult['data'] = jjheld_list
return tdResult
# 基金持仓获取成功
print(f'按照基金排名,汇总完的基金持仓信息: {jjheld_list}')
# 统计基金持仓,并生成个股推荐指数
rslt = Anls.stock_rating(jjheld_list, NETTIME_D)
if not isinstance(rslt, dict):
# 返回不是字典,访问错误
tdResult['data'] = rslt
return tdResult
# print(f'按股票排行分析的最终结果:{rslt}')
# 返回线程结果
tdResult['data'] = rslt
return tdResult
def td_byjjname(key: str):
"""
按照基金名称,获取基金持仓,并生成推荐指数
:param key:
:return:
【dict】 - {
'TabelName': '按基金排行(全部,近1月,前50)',
'data': -
【'Time out'】 - 请求超时
【'Network error'】 - 网络连接错误
【'Response error'】 - 按基金名称查询接口,响应内容可能变更
【dict】 - 数据分析成功
}
"""
# print(f'按照 {key} 搜索基金...')
tdResult = {
'TabelName': '按基金名称(' + str(key) + ')',
'data': None
}
# 按键名称搜索基金
byjjname_list = Inet.get_jjbyname(key)
if not isinstance(byjjname_list, list):
# 访问出错
tdResult['data'] = byjjname_list
return tdResult
print(f'按基金名称获取到的基金列表:{byjjname_list}')
# [
# {'_id': '159766', 'CODE': '159766', 'NAME': '富国中证旅游主题ETF', 'STOCKMARKET': ''},
# ......
# ]
# 校准时间
calibration_time()
if not isinstance(NETTIME_STAMP, int):
# 校准网络时间失败
tdResult['data'] = NETTIME_STAMP
return tdResult
year = NETTIME_ARRAY.tm_year
# print(f'当前年份:{year}')
# 处理获取到的基金列表
jjinfo_list = []
for noe in byjjname_list:
jjinfo_list.append([noe['CODE'], noe['NAME']])
# 获取基金持仓
jjheld_list = gets_jjheld(jjinfo_list, year)
if not isinstance(jjheld_list, list):
tdResult['data'] = jjheld_list
return tdResult
# 基金持仓获取成功
print(f'按照基金名称,汇总完的基金持仓信息: {jjheld_list}')
# 统计基金持仓,并生成个股推荐指数
rslt = Anls.stock_rating(jjheld_list, NETTIME_D)
if not isinstance(rslt, dict):
# 返回不是字典,访问错误
return rslt
tdResult['data'] = rslt
# 返回线程结果
return tdResult
def init_ready():
"""
判断所有初始化是否准备完毕
"""
# 判断表格初始化是否完成,失败直接退出
if TABELINIT != 'success':
# 表格初始化失败
messagebox.showerror(title='代码错误', message=TABELINIT)
exit() # 退出程序
# 检测网络状况
calibration_time(often=True) # 校准时间, 开启无延时访问
# 循环检测网络连接情况,直到用户取消或连接成功!
while not isinstance(NETTIME_STAMP, int):
if NETTIME_STAMP == 'Time out':
# 超时,询问是否重新连接
asktry = messagebox.askretrycancel(
title='连接超时',
message='连接超时,是否重新连接?如果取消,部分功能可能无法使用!'
)
if asktry:
# 重新检查
calibration_time()
else:
# 用户取消
break
elif NETTIME_STAMP == 'Network error':
# 如果是网络错误,直接不再检查!
messagebox.showerror(
title='网络连接错误',
message='网络连接错误,部分功能可能无法使用!!'
)
break
# 判断需要图片压缩包是否存在
if not SKINZIP:
print(f'Skin.zip准备失败!')
messagebox.showerror(
title='必要文件缺失',
message='必要文件缺失,请尝试重新安装!!'
)
exit() # 退出程序
class WINDOW:
def __init__(self):
# 创建主窗口
self.ROOT = tk.Tk()
# 获取屏幕信息
SYS_W = self.ROOT.winfo_screenwidth() # 屏幕宽度
SYS_H = self.ROOT.winfo_screenheight() # 屏幕高度
# 计算居中的位置
WIN_X = (SYS_W - SIZE_WIN_W) / 2
WIN_Y = (SYS_H - SIZE_WIN_H) / 2
self.ROOT.geometry("%dx%d+%d+%d" % (SIZE_WIN_W, SIZE_WIN_H, WIN_X, WIN_Y)) # 主窗口居中显示
self.ROOT.resizable(width=False, height=False) # 禁止改变窗体大小
self.ROOT.title("ChoiceStock") # 窗口标题
self.ROOT.configure(bg=COLOR_BACKGROUND) # 窗口背景颜色
self.ROOT.iconbitmap('icon.ico') # 设置窗口图标
# 限制Entry只能输入数字
self.OnlyNumber = self.ROOT.register(only_number) # 将函数包装一下
# --No.1--↓↓↓↓↓ 绘制window基本框架-----------------
self.CNTL_Menu_BTN = [] # 存储菜单按钮控件对象,用于点击后改变按钮状态
self.CNTL_NowFace = None
self.window_layout()
# --No.1--↑↑↑↑↑ 绘制window基本框架-----------------
# --No.2--↓↓↓↓↓ 必要初始化判断-----------------
init_ready()
# --No.2--↑↑↑↑↑ 必要初始化判断-----------------
# --No.3--↓↓↓↓↓ 通用图片初始化-----------------
# --页面所需图片变量,所有需要的图片变量放至 window 下,防止不显示
self.tkImage = {
'currency': []
}
# --初始化所有页面都用到的图片
self.NeedImgPath = [
'skin/Currency/Internet/networkerror.png',
'skin/Currency/Internet/timeout.png'
]
self.imginit('currency') # 初始化所需通用图片
# --No.3--↑↑↑↑↑ 通用图片初始化-----------------
# -----↓↓↓↓↓↓↓↓↓↓ 初始化变量 ↓↓↓↓↓↓↓↓↓↓-----
self.NowFaceCode = 0 # 当前处在什么页面, 更改该值,即由此进入程序时的页面
# self.NowFaceCode可选值: int:0 选股;1 持有;2 历史
# 选股分析线程
self.tdResult_Choice = None # 存储选股分析的线程结果
self.tdObject_Choice = None # 存储选股分析的线程对象
# 持有页面定时刷新
self.monitorState_Hold = False # Hold的监听器开启标志位
self.tdResult_Hold = None # 存储持有数据的线程结果
self.tdObject_Hold = None # 存储刷新持有数据线程对象
# -----↑↑↑↑↑↑↑↑↑↑ 初始化变量 ↑↑↑↑↑↑↑↑↑↑-----
# --No4--↓↓↓↓↓ 更新持有的最新价格-----------------
self.td_run_hold()
# --No4--↓↓↓↓↓ 更新持有的最新价格-----------------
# --No.Last--↓↓↓↓↓ 绘制初始页面----------------
self.event_switch_face(self.NowFaceCode)
# --No.Last--↑↑↑↑↑ 绘制初始页面----------------
def td_run_hold(self):
"""执行更新持有最新价线程"""
self.tdObject_Hold = MyThread(Ldb.update_hold)
self.tdObject_Hold.setDaemon(True) # 设为主线程的守护线程,主线程结束,该线程结束!
self.tdObject_Hold.start() # 开始线程
def window_layout(self):
# --绘制导航---------------
# 导航菜单框架
# --master self.ROOT 主窗口
frame_menu_attrs = {
'bg': COLOR_THEME,
}
frame_menu_place = {
'width': SIZE_MENU_W,
'height': SIZE_MENU_H,
}
frame_menu = tk.Frame(
self.ROOT,
frame_menu_attrs
)
frame_menu.place(frame_menu_place)
# 导航按钮
# --master frame_menu 导航菜单框架
button_menu_text = ("选股", "持有", "历史")
button_menu_attrs = {
'bg': frame_menu_attrs['bg'], # 'red',
'bd': 0, # 去掉边框
'fg': COLOR_BTNFG,
'font': FONT_MENU,
'activebackground': frame_menu_attrs['bg'], # 点击时的背景色与菜单框架一致
'activeforeground': 'white',
}
for index in range(len(button_menu_text)):
button_menu_attrs['text'] = button_menu_text[index]
button_menu = tk.Button(
frame_menu,
button_menu_attrs
)
button_menu.pack(side='left', padx=10)
# 绑定导航菜单按钮事件
button_menu.configure(command=lambda pre=index: self.event_switch_face(pre))
self.CNTL_Menu_BTN.append(button_menu)
# --绘制导航---------------
# --绘制桌面框架------------
# --master self.ROOT 主窗口
frame_face_attrs = FRAME_ATTRS.copy()
frame_face_place = {
'width': SIZE_FACE_W,
'height': SIZE_FACE_H,
'y': frame_menu_place['height']
}
self.CNTL_NowFace = tk.Frame(
self.ROOT,
frame_face_attrs
)
self.CNTL_NowFace.place(frame_face_place)
# --绘制桌面框架------------
def imginit(self, key: str):
"""初始化页面需要的图片"""
lackRoute = '' # 存储缺少的图片信息
for route in self.NeedImgPath:
try:
content = SKINZIP.read(route)
# 临时存储图片
tempimg = open('tempImg.png', 'wb+')
tempimg.write(content) # 将图片写入临时图片
tempimg.close()
# 设置tk可用img
self.tkImage[key].append(tk.PhotoImage(file='tempImg.png'))
# 删除临时图片
os.remove('tempImg.png')
except KeyError:
print(f'{route} 在Skin.zip中不存在!')
lackRoute += route + '\n'
if lackRoute:
messagebox.showerror(
title='文件损坏',
message=f'必要文件损坏,请检查文件 ‘Skin.zip’:\n{lackRoute}'
)
exit()
# 清空路径列表,释放内存
self.NeedImgPath = []
def draw_errors(self, master, state):
"""
绘制访问超时页面
:param master: 父级框架
:param state: 错误状态,'Time out'-超时 'Network error'-网络错误 'None data'
:return:
"""
# 信息显示框架
frame_errorinfo_attrs = FRAME_ATTRS.copy()
# frame_errorinfo_attrs['bg'] = 'green'
frame_errorinfo_place = {
'width': SIZE_TBODY_W,
'height': SIZE_TBODY_W,
}
frame_errorinfo = tk.Frame(
master,
frame_errorinfo_attrs
)
frame_errorinfo.place(frame_errorinfo_place)
# 图片显示框架
# --master frame_errorinfo 信息显示框架
label_errorimg_attrs = {
'bg': COLOR_BACKGROUND,
}
# 判断显示的图片和文字
if state == 'Time out':
label_errorimg_attrs['image'] = self.tkImage['currency'][1] # 超时图片
text = '网络连接超时!'
elif state == 'Network error':
label_errorimg_attrs['image'] = self.tkImage['currency'][0] # 网络错误图片
text = '网络连接错误!'
elif state == 'None data':
tk.Label(
master,
bd=0,
bg=COLOR_BACKGROUND,
fg=COLOR_TIPS_TALL,
font=FONT_NONEDATA_ABNORMAL,
text='暂无数据!',
anchor='n',
).pack(expand='yes', fill='both')
return None
else:
text = '未知错误!'
label_errorimg = tk.Label(
frame_errorinfo,
label_errorimg_attrs,
)
label_errorimg.pack()
# 文字显示框架
# --master frame_errorinfo 信息显示框架
label_errorfont_attrs = {
'bg': COLOR_BACKGROUND,
'font': FONT_NONEDATA_ABNORMAL,
'fg': COLOR_TIPS_TALL,
'text': text
}
label_errorfont = tk.Label(
frame_errorinfo,
label_errorfont_attrs
)
label_errorfont.pack()
def destory_face(self):
"""清空桌面,方便重新绘制"""
for widget in self.CNTL_NowFace.winfo_children():
widget.destroy()
def event_switch_face(self, face_code):
"""
持有、历史、选股页面的切换
:param face_code: int 页面代码:0 选股;1 持有;2 历史
"""
# 变换导航按钮样式
for i in range(len(self.CNTL_Menu_BTN)):
if i == face_code:
self.CNTL_Menu_BTN[i]["fg"] = COLOR_BTNFG_ACTIVE
else:
self.CNTL_Menu_BTN[i]["fg"] = COLOR_BTNFG
if face_code == 0:
self.NowFaceCode = 0 # 当前处在持有页面
# print('选股页面')
ChoiceFace(self)
elif face_code == 1:
self.NowFaceCode = 1 # 当前处在历史页面
# print('持有页面')
HoldFace(self)
elif face_code == 2:
self.NowFaceCode = 2 # 当前处在选股页面
# print('持有历史页面')
HoldHistoryFace(self)
def event_business(code_oder):
"""
模拟买卖事件
:param code_oder: 买卖参数。
str - 模拟买入
int - 模拟卖出
:return:
【True】 - 卖出成功
【False】 - 卖出失败
"""
title = '错误'
# 校准时间
calibration_time()
if not isinstance(NETTIME_STAMP, int):
# 校准网络时间失败
info = NETTIME_STAMP
else:
if isinstance(code_oder, str):
info = Ldb.buy(code_oder, NETTIME)
title = '模拟买入'
elif isinstance(code_oder, int):
info = Ldb.sale(code_oder, NETTIME)
title = '模拟卖出'
else:
print(f'event_business()参数错误:{code_oder}')
return False
if not isinstance(info, dict):
if info == 'Held':
message = f'{code_oder} 已持有!'
elif info == 'TryAll error':
message = f'{code_oder} 股票代码错误!'
elif info == 'Time out':
message = '请求超时,请稍后重试!'
elif info == 'Network error':
message = '网络错误,请检查网络后重试!'
elif info == 'None data':
message = '数据不存在!'
elif info == 'Not Held':
message = f'卖出失败,Oder:{code_oder} 已模拟卖出!'
else:
message = '未知错误!'
messagebox.showerror(
title='错误',
message=message
)
return False
# 确定询问
userchoose = messagebox.askokcancel(title=title,
message=info['Tips']
)
# 判断用户是否确定
if userchoose:
# 执行更新语句
s = Ldb.myexecute(info['SQL'])
if s:
messagebox.showerror(
title='错误',
message=s
)
return False
else:
return True
class FACE:
def __init__(self, window):
self.window = window # 主窗口类
window.destory_face() # 清空桌面
# --↓↓↓↓↓ 初始化变量 ↓↓↓↓↓--
# face页面所需图片
self.window.tkImage['face'] = []
# --↑↑↑↑↑ 初始化变量 ↑↑↑↑↑--
def event_business(self, code_oder):
"""
模拟买卖事件
:return:
"""
if event_business(code_oder):
print('购买成功')
# 刷新整个页面
self.refresh()
else:
print('购买失败')
return None
class ChoiceFace(FACE):
def __init__(self, window):
FACE.__init__(self, window)
# --初始化本页面需要的图片
self.window.tkImage['ChoiceFace'] = []
# --页面都用到的图片
self.window.NeedImgPath = [
'skin/Choices/ImgButton/search.png',
]
self.window.imginit('ChoiceFace') # 初始化所需通用图片
# --加载动画相关的变量
self.LoadingTextIndex = 0 # 高亮字体计数
self.LoadingText = ('数', '据', '分', '析', '中', '.', '.', '.') # 高亮字体
self.LoadingText_LEN = len(self.LoadingText)
self.LoadingSpeed = 200 # 动画的速度(ms)
# --模式选择相关变量
self.NowMode = 0 # 在此设置,即初始绘制的模式选择页面
# self.NowMode可选值:0 按照基金排行;1 按照基金名称;2 按照股票代码
self.Text_Type = ('全部', '股票', '混合', '指数') # '基金类型'
self.Text_TimeSlot = ('近1周', '近1月', '近3月', '近6月', '近1年', '近3年') # '时间段'
self.Text_Rank = ('前30', '前50', '前100', '前200') # '排名'
self.PREs_byjjrank = {
'基金类型': ('all', 'gp', 'hh', 'zs'),
'时间段': ('zzf', '1yzf', '3yzf', '6yzf', '1nzf', '3nzf'),
'排名': ('30', '50', '100', '200'),
}
self.PRE_byjjrank = {
'基金类型': '',
'时间段': '',
'排名': '',
} # 存放按基金排名的参数,如果未 '' 说明该参数未选择
self.CNTL_RecomModeBTN = [] # 按钮控件对象,用于改变按钮状态
self.CNTL_FrameOption = None # 存放模式选项控件的框架对象
self.CNTL_OptionBTN = {
'基金类型': [], # 基金类型,如:全部、股票、指数等
'时间段': [], # 哪个时间段,如 近1周
'排名': [], # 排名,如 前50
} # 存放“按基金排名”选项的控件对象
self.StringVar_SearchEntry = tk.StringVar() # 存储搜索输入框框的值
# 监听结果
self.monitor_choice_tdresult()
def monitor_choice_tdresult(self):
"""
监听线程结果,根据线程结果,给出不同页面
:return:
"""
# 线程结果变量存储在 window 中,为了达到分析一次后,将分许结果存储在内存,
# 只要不关闭 window 页面或用户不主动清除,分析的数据就不会被清除。
# self.window.tdResult_Choice 可分为:
# None - 无线程
# 'Not started' - 线程未启动
# 'Started' - 线程已启动,未结束
# dict - 字典,表示数据分析完成
# 只有页面处于选股页面才开启监听
if self.window.NowFaceCode == 0:
print(f'处于选股页面!')
# 判断是否有结果
if isinstance(self.window.tdResult_Choice, dict):
# 有分析数据。绘制结果
print(f'分析完成,绘制结果!')
ChoiceResult(self.window)
else:
# 没有结果,判断是否有线程
if not self.window.tdObject_Choice:
print('无分析结果,并且无 Choice 线程对象,绘制选股选项页面!')
# 绘制选股选项页面
self.draw_choiceface()
else:
# 有线程,获取线程结果
self.window.tdResult_Choice = self.window.tdObject_Choice.get_result()
if not isinstance(self.window.tdResult_Choice, dict):
print(f'当前 Choice 线程状态:{self.window.tdResult_Choice}')
# 如果返回结果不是字典,可能线程未启动或已启动未结束!绘制loading
self.analysis_loading_animation()
# 继续监听
self.window.ROOT.after(self.LoadingSpeed, self.monitor_choice_tdresult)
def analysis_loading_animation(self):
"""
分析中动画效果
:return:
"""
frame_loadcenter_attrs = FRAME_ATTRS.copy()
# frame_loadcenter_attrs['bg'] = 'blue'
frame_loadcenter_attrs['width'] = SIZE_FACE_W
frame_loadcenter_attrs['height'] = SIZE_FACE_H
frame_loadcenter_place = {
'width': SIZE_FACE_W,
'height': SIZE_FACE_H,
}
frame_loadcenter = tk.Frame(
self.window.CNTL_NowFace, # 整个动画基于整个FACE
frame_loadcenter_attrs
)
frame_loadcenter.place(frame_loadcenter_place)
# 整合居中框架
# --master self.window.NowFace_cntl 桌面框架
frame_allloadcenter_attrs = FRAME_ATTRS.copy()
# frame_allloadcenter_attrs['bg'] = 'red'
# frame_allloadcenter_attrs['width'] = 100
# frame_allloadcenter_attrs['height'] = 50
frame_allloadcenter = tk.Frame(
frame_loadcenter,
frame_allloadcenter_attrs
)
frame_allloadcenter.pack(side='top', expand='yes')
# label
# --master frame_allloadcenter 居中框架
label_load_attrs = {
'bg': COLOR_BACKGROUND,
'font': FONT_LOADING,
}
for index in range(self.LoadingText_LEN):
if index == self.LoadingTextIndex:
# 高亮的字
label_load_attrs['fg'] = COLOR_THEME
else:
label_load_attrs['fg'] = COLOR_TIPS_LOW
label_load_attrs['text'] = self.LoadingText[index]
label_load = tk.Label(
frame_allloadcenter,
label_load_attrs
)
label_load.pack(side='left', expand='yes', padx=5)
if self.LoadingTextIndex == self.LoadingText_LEN - 1:
# 重置
self.LoadingTextIndex = 0
else:
# 下一个高亮字体
self.LoadingTextIndex += 1
def draw_choiceface(self):
"""
绘制选股选项页面
:return:
"""
# 选股标题
# --master self.window.CNTL_NowFace 桌面框架
label_chiocetitle_attrs = {
'text': 'Choices',
'anchor': 's', # 显示在底部
'font': FONT_CHOICETITLE,
'fg': COLOR_THEME,
'bg': COLOR_BACKGROUND,
}
label_chiocetitle_place = {
'width': SIZE_CHOICETITLE_W,
'height': SIZE_CHOICETITLE_H,
}
label_chiocetitle = tk.Label(
self.window.CNTL_NowFace,
label_chiocetitle_attrs
)
label_chiocetitle.place(label_chiocetitle_place)
# 模式选择按钮框架--------------------------------------
# --master self.window.CNTL_NowFace 桌面框架
frame_recommode_attrs = FRAME_ATTRS.copy()
# frame_recommode_attrs['bg'] = 'pink'
frame_recommode_place = {
'width': SIZE_CHOICE_RECOMMODE_W,
'height': SIZE_CHOICE_RECOMMODE_H,
'x': 0,
'y': SIZE_CHOICETITLE_H,
}
frame_recommode = tk.Frame(
self.window.CNTL_NowFace,
frame_recommode_attrs
)
frame_recommode.place(frame_recommode_place)
# 为了控件能再一行居中显示,再套一层居中的框架
# 模式选择居中框架
# --master frame_recommode 模式选择按钮框架
frame_rcmodecenter_attrs = FRAME_ATTRS.copy()
# frame_rcmodecenter_attrs['bg'] = 'yellow'
frame_rcmodecenter = tk.Frame(
frame_recommode,
frame_rcmodecenter_attrs
)
frame_rcmodecenter.pack()
# 模式选择按钮
# --master frame_rcmodecenter 模式选择居中框架
mode_text = ('按基金排名', '按基金名称', '按股票代码')
# 推荐模式选择按钮
button_recommode_attrs = {
'bg': COLOR_BACKGROUND, # 'green',
'font': FONT_RECOMMODE,
'fg': COLOR_THEME,
# 'width': 10,
"relief": "solid",
'bd': 1,
"activebackground": COLOR_BACKGROUND,
"activeforeground": COLOR_THEME,
'disabledforeground': 'white', # 禁用时的按钮文本颜色
}
for index in range(len(mode_text)):
button_recommode_attrs['text'] = mode_text[index]
button_recommode = tk.Button(
frame_rcmodecenter,
button_recommode_attrs
)
button_recommode.pack(side='left', expand='yes', padx=20, ipadx=10)
self.CNTL_RecomModeBTN.append(button_recommode) # 存储推荐模式按钮对象
# 绑定按钮事件
button_recommode.configure(command=lambda pre=index: self.event_switch_mode(pre))
# 模式选择按钮框架--------------------------------------
# 推荐模式选项框架--------------------------------------
# --master self.window.CNTL_NowFace 桌面框架
frame_option_attrs = FRAME_ATTRS.copy()
# frame_option_attrs['bg'] = 'blue'
frame_option_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': SIZE_CHOICE_RECOMMODE_FACE_H,
'y': SIZE_CHOICETITLE_H + SIZE_CHOICE_RECOMMODE_H
}
self.CNTL_FrameOption = tk.Frame(
self.window.CNTL_NowFace,
frame_option_attrs
)
self.CNTL_FrameOption.place(frame_option_place)
# 推荐模式选项框架--------------------------------------
# 绘制初始模式
self.event_switch_mode(self.NowMode)
def draw_byjjrank(self):
"""
绘制按基金排名选股的选项
:return:
"""
# 选项的一行高度
alineheight = SIZE_CHOICE_RECOMMODE_FACE_H * 0.15
pady = 20
# 按钮属性
button_attrs = {
'bg': COLOR_BACKGROUND, # 'green',
'font': FONT_RECOMMODE_OPTION,
'fg': COLOR_THEME,
"relief": "solid",
'bd': 1,
"activebackground": COLOR_BACKGROUND,
"activeforeground": COLOR_THEME,
'disabledforeground': 'white', # 禁用时的按钮文本颜色
'cursor': 'dotbox',
}
# 基金类型选项框架
# --master self.CNTL_Option 选股选项框架
frame_jjtype_attrs = FRAME_ATTRS.copy()
# frame_jjtype_attrs['bg'] = 'yellow'
frame_jjtype_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': alineheight,
}
frame_jjtype = tk.Frame(
self.CNTL_FrameOption,
frame_jjtype_attrs
)
frame_jjtype.place(frame_jjtype_place)
# 居中框架
# --master frame_recommode 模式选择按钮框架
frame_center_attrs = FRAME_ATTRS.copy()
frame_center = tk.Frame(
frame_jjtype,
frame_center_attrs
)
frame_center.pack()
# 类型选项-----------------------------------------------------
# --master frame_center 居中框架
button_type_attrs = button_attrs.copy()
self.CNTL_OptionBTN['基金类型'] = [] # 先重置,防止反复添加
for index in range(len(self.Text_Type)):
button_type_attrs['text'] = self.Text_Type[index]
button_type = tk.Button(
frame_center,
button_type_attrs
)
button_type.pack(side='left', expand='yes', padx=20, pady=pady, ipadx=10)
self.CNTL_OptionBTN['基金类型'].append(button_type)
# 绑定事件
button_type.configure(
command=lambda pre='基金类型', pre2=index: self.event_clickrankoption(pre, pre2)
)
# 类型选项-----------------------------------------------------
# 时间段选项框架
# --master self.CNTL_Option 选股选项框架
frame_jjtimeslot_attrs = FRAME_ATTRS.copy()
# frame_jjtimeslot_attrs['bg'] = 'pink'
frame_jjtimeslot_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': alineheight,
'y': frame_jjtype_place['height']
}
frame_jjtimeslot = tk.Frame(
self.CNTL_FrameOption,
frame_jjtimeslot_attrs
)
frame_jjtimeslot.place(frame_jjtimeslot_place)
# 居中框架
# --master frame_jjtimeslot 时间段选项框架
frame_center_attrs = FRAME_ATTRS.copy()
frame_center = tk.Frame(
frame_jjtimeslot,
frame_center_attrs
)
frame_center.pack()
# 时间段选项-----------------------------------------------------
# --master frame_center 居中框架
button_timeslot_attrs = button_attrs.copy()
self.CNTL_OptionBTN['时间段'] = [] # 先重置,防止反复添加
for index in range(len(self.Text_TimeSlot)):
button_timeslot_attrs['text'] = self.Text_TimeSlot[index]
button_timeslot = tk.Button(
frame_center,
button_timeslot_attrs
)
button_timeslot.pack(side='left', expand='yes', padx=20, pady=pady, ipadx=10)
self.CNTL_OptionBTN['时间段'].append(button_timeslot)
# 绑定事件
button_timeslot.configure(
command=lambda pre='时间段', pre2=index: self.event_clickrankoption(pre, pre2)
)
# 时间段选项-----------------------------------------------------
# 排名前多少名选项框架
# --master self.CNTL_Option 选股选项框架
frame_jjrank_attrs = FRAME_ATTRS.copy()
# frame_jjrank_attrs['bg'] = 'green'
frame_jjrank_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': alineheight,
'y': frame_jjtimeslot_place['y'] + frame_jjtimeslot_place['height']
}
frame_jjrank = tk.Frame(
self.CNTL_FrameOption,
frame_jjrank_attrs
)
frame_jjrank.place(frame_jjrank_place)
# 居中框架
# --master frame_jjrank 排名前多少名选项框架
frame_center_attrs = FRAME_ATTRS.copy()
frame_center = tk.Frame(
frame_jjrank,
frame_center_attrs
)
frame_center.pack()
# 排行选项-----------------------------------------------------
# --master frame_center 居中框架
button_rank_attrs = button_attrs.copy()
self.CNTL_OptionBTN['排名'] = [] # 先重置,防止反复添加
for index in range(len(self.Text_Rank)):
button_rank_attrs['text'] = self.Text_Rank[index]
button_rank = tk.Button(
frame_center,
button_rank_attrs
)
button_rank.pack(side='left', expand='yes', padx=20, pady=pady, ipadx=10)
self.CNTL_OptionBTN['排名'].append(button_rank)
# 绑定事件
button_rank.configure(
command=lambda pre='排名', pre2=index: self.event_clickrankoption(pre, pre2)
)
# 排行选项-----------------------------------------------------
# 确定按钮框架
# --master self.CNTL_Option 选股选项框架
frame_det_attrs = FRAME_ATTRS.copy()
# frame_det_attrs['bg'] = 'red'
frame_det_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': alineheight,
'y': frame_jjrank_place['y'] + (frame_jjrank_place['height'] * 1.5)
}
frame_jjrank = tk.Frame(
self.CNTL_FrameOption,
frame_det_attrs
)
frame_jjrank.place(frame_det_place)
# 居中框架
# --master frame_jjrank 排名前多少名选项框架
frame_center_attrs = FRAME_ATTRS.copy()
frame_center = tk.Frame(
frame_jjrank,
frame_center_attrs
)
frame_center.pack()
# 确定按钮-----------------------------------------------------
# --master frame_center 居中框架
button_dat_attrs = BUTTON_ATTRS.copy()
button_dat_attrs['bd'] = 0
button_dat_attrs['font'] += ' underline'
button_dat_attrs['text'] = '确定'
button_dat = tk.Button(
frame_center,
button_dat_attrs
)
button_dat.pack(side='bottom', expand='yes')
# 绑定事件
button_dat.configure(
command=lambda: self.event_ok_byjjrank()
)
# 确定按钮-----------------------------------------------------
def draw_byjjname(self):
"""
绘制按基名称选股的选项
:return:
"""
# 名称搜索输入框按钮框架
# --master self.CNTL_FrameOption 选项输入框
frame_jjtype_attrs = FRAME_ATTRS.copy()
# frame_jjtype_attrs['bg'] = 'green'
frame_jjtype_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': SIZE_CHOICE_RECOMMODE_FACE_H * 0.3,
'x': 0,
'y': 0,
}
frame_jjtype = tk.Frame(
self.CNTL_FrameOption,
frame_jjtype_attrs
)
frame_jjtype.place(frame_jjtype_place)
# 居中框架
# --master frame_jjtype 输入框框架
frame_jjtypecenter_attrs = FRAME_ATTRS.copy()
# frame_jjtypecenter_attrs['bg'] = 'pink'
frame_jjtypecenter = tk.Frame(
frame_jjtype,
frame_jjtypecenter_attrs
)
frame_jjtypecenter.pack(side='top', expand='yes')
# 输入框画布
# --master frame_jjtypecenter
line_widght = 2 # 输入框线条宽度
line_color = 'black' # 输入框线条颜色
canvas_entry_attrs = CANVAS_ATTRS.copy()
# canvas_entry_attrs['bg'] = 'blue'
canvas_entry_attrs['width'] = frame_jjtype_place['width'] * 0.4 # frame_jjtype_attrs['width'] * 0.3
canvas_entry_attrs['height'] = frame_jjtype_place['height'] * 0.4 # frame_jjtype_attrs['height'] * 0.4
canvas_entry = tk.Canvas(
frame_jjtypecenter,
canvas_entry_attrs
)
# 绘制输入框下的横线,
draw_tb_line(canvas_entry, canvas_entry_attrs, tt=(0, 1), line_wight=line_widght, line_color=line_color)
canvas_entry.pack(side='left', expand='no', padx=3)
# 输入框
# --master canvas_entry 输入框画布,画布用来绘制横线
entry_name_attrs = {
'bg': COLOR_BACKGROUND,
'bd': 0,
'font': FONT_ENTRY,
'fg': COLOR_TIPS_TALL,
'justify': 'center',
'textvariable': self.StringVar_SearchEntry,
}
entry_name_place = {
'width': canvas_entry_attrs['width'],
'height': canvas_entry_attrs['height'] - line_widght,
}
entry_name = tk.Entry(
canvas_entry,
entry_name_attrs
)
entry_name.place(entry_name_place)
# 搜索按钮
# --master frame_jjtype
# button_entry_attrs = BUTTON_ATTRS.copy()
self.StringVar_SearchEntry.set("") # 重置值
button_entry_attrs = {
'bd': 0,
'bg': COLOR_BACKGROUND,
"activebackground": COLOR_BACKGROUND,
'image': self.window.tkImage['ChoiceFace'][0],
}
button_entry = tk.Button(
frame_jjtypecenter,
button_entry_attrs
)
button_entry.pack(side='left', expand='no', fill='both', padx=5)
# 绑定事件
button_entry.configure(
command=lambda pre='byjjname': self.event_search_btn(pre)
)
def draw_bystockcode(self):
"""
按照股票代码
:return:
"""
# 名称搜索输入框按钮框架
# --master self.CNTL_FrameOption 选项输入框
frame_jjtype_attrs = FRAME_ATTRS.copy()
# frame_jjtype_attrs['bg'] = 'green'
frame_jjtype_place = {
'width': SIZE_CHOICE_RECOMMODE_FACE_W,
'height': SIZE_CHOICE_RECOMMODE_FACE_H * 0.3,
}
frame_jjtype = tk.Frame(
self.CNTL_FrameOption,
frame_jjtype_attrs
)
frame_jjtype.place(frame_jjtype_place)
# 居中框架
# --master frame_jjtype 输入框框架
frame_jjtypecenter_attrs = FRAME_ATTRS.copy()
# frame_jjtypecenter_attrs['bg'] = 'pink'
frame_jjtypecenter = tk.Frame(
frame_jjtype,
frame_jjtypecenter_attrs
)
frame_jjtypecenter.pack(side='top', expand='yes')
# 输入框画布
# --master frame_jjtypecenter
line_widght = 2 # 输入框线条宽度
line_color = 'black' # 输入框线条颜色
canvas_entry_attrs = CANVAS_ATTRS.copy()
# canvas_entry_attrs['bg'] = 'blue'
canvas_entry_attrs['width'] = frame_jjtype_place['width'] * 0.4 # frame_jjtype_attrs['width'] * 0.3
canvas_entry_attrs['height'] = frame_jjtype_place['height'] * 0.4 # frame_jjtype_attrs['height'] * 0.4
canvas_entry = tk.Canvas(
frame_jjtypecenter,
canvas_entry_attrs
)
# 绘制输入框下的横线,
draw_tb_line(canvas_entry, canvas_entry_attrs, tt=(0, 1), line_wight=line_widght, line_color=line_color)
canvas_entry.pack(side='left', expand='no', padx=3)
# 输入框
# --master canvas_entry 输入框画布,画布用来绘制横线
entry_name_attrs = {
'bg': COLOR_BACKGROUND,
'bd': 0,
'font': FONT_ENTRY,
'fg': COLOR_TIPS_TALL,
'justify': 'center',
'textvariable': self.StringVar_SearchEntry,
'validate': "key",
'validatecommand': (self.window.OnlyNumber, '%P'), # 显示entry只能输入数字
}
entry_name_place = {
'width': canvas_entry_attrs['width'],
'height': canvas_entry_attrs['height'] - line_widght,
}
entry_name = tk.Entry(
canvas_entry,
entry_name_attrs
)
entry_name.place(entry_name_place)
# 搜索按钮
# --master frame_jjtype
# button_entry_attrs = BUTTON_ATTRS.copy()
self.StringVar_SearchEntry.set("") # 重置值
button_entry_attrs = {
'bd': 0,
'bg': COLOR_BACKGROUND,
"activebackground": COLOR_BACKGROUND,
'image': self.window.tkImage['ChoiceFace'][0],
}
button_entry = tk.Button(
frame_jjtypecenter,
button_entry_attrs
)
button_entry.pack(side='left', expand='no', fill='both', padx=5)
# 绑定事件
button_entry.configure(
command=lambda pre='bystockcode': self.event_search_btn(pre)
)
def td_run(self, function, param):
"""
开启分析线程
:param function: 线程函数
:param param: 函数的参数
:return:
"""
self.window.tdObject_Choice = MyThread(function, param)
self.window.tdObject_Choice.setDaemon(True) # 设为主线程的守护线程,主线程结束,该线程结束!
self.window.tdObject_Choice.start() # 开始线程
# 开启监听
self.monitor_choice_tdresult()
def event_ok_byjjrank(self):
"""
按基金排名,确定按钮事件
:return:
"""
# print(f'选择的参数:{self.PRE_byjjrank}')
# 判断哪个参数未选择
message = '请选择:'
for par, value in self.PRE_byjjrank.items():
if not value:
message += f'”{par}“,'
if message != '请选择:':
# 有未选选项
message = message[:-1] + '。' # 去掉多余逗号加上句号
messagebox.showwarning(title='请选择', message=message)
else:
# 处理参数
byjjrank_param = self.PRE_byjjrank.values()
# 开始进行数据分析
self.td_run(td_byjjrank, byjjrank_param)
def event_search_btn(self, mode):
"""
按基金名称,股票代码搜索按钮事件
:return:
"""
# 获取输入值
input_content = self.StringVar_SearchEntry.get()
if not input_content:
# 无输入
return None
# 判断模式
if mode == 'byjjname':
self.td_run(td_byjjname, (input_content,))
if mode == 'bystockcode':
if len(input_content) > 6:
messagebox.showerror(
title='输入错误', message='请输入 6 位以内的数字!'
)
self.StringVar_SearchEntry.set("")
return None
# 绘制个股
StockInfo(self.window, input_content)
def event_clickrankoption(self, key: str, index):
"""
按基金排名,选项按钮点击事件
:param key: 哪一项
:param index: 点击的哪个按钮
:return:
"""
try:
terms = self.CNTL_OptionBTN[key]
except KeyError:
print('按钮绑定事件,传参错误!')
return None
# 先重置所有控件状态
for indv in terms:
indv['bg'] = COLOR_BACKGROUND
indv['fg'] = COLOR_THEME
indv['state'] = 'normal'
# 改变点击控件状态
terms[index]['bg'] = COLOR_THEME
terms[index]['fg'] = 'white'
terms[index]['state'] = 'disable'
# 更改参数下标
self.PRE_byjjrank[key] = self.PREs_byjjrank[key][index]
def event_switch_mode(self, mode):
"""
模式选择按钮绑定事件
:return:
"""
# 先清空选项框架内容
for widget in self.CNTL_FrameOption.winfo_children():
widget.destroy()
# 重置按钮状态
for cntl in self.CNTL_RecomModeBTN:
cntl['bg'] = COLOR_BACKGROUND
cntl['fg'] = COLOR_THEME
cntl['state'] = 'normal'
# 改变按钮状态
self.CNTL_RecomModeBTN[mode]['bg'] = COLOR_THEME
# normal
# disabled
# active
self.CNTL_RecomModeBTN[mode]['state'] = 'disabled' # 禁用按钮
# self.CNTL_RecomModeBTN[self.NowMode]['fg'] = 'white'
self.NowMode = mode
if self.NowMode == 0:
# print(f'按基金排行')
self.draw_byjjrank() # 按基金排行
elif self.NowMode == 1:
# print(f'按基金名称')
self.draw_byjjname()
elif self.NowMode == 2:
# print(f'按股票代码')
self.draw_bystockcode()
class StockInfo(FACE):
def __init__(self, window, code, sortcol='bcynum', sortmode='desc', nowpagenum=0):
"""
绘制个股信息
:param window:
:param code: 股票代码
:param sortcol: 非直接进入此页面,上级页面的排序列
:param sortmode: 非直接进入此页面,上级页面的排序方式
:param nowpagenum: 非直接进入此页面,上级页面的页数
"""
FACE.__init__(self, window)
# --初始化本页面需要的图片
self.window.tkImage['StockInfo'] = []
print(f'{code} 个股信息页面')
self.Code = code # 股票代码
# 如果是从非选股页面进入个股详情,以下参数会用到。达到返回原来页面
self.SortColumn = sortcol
self.SortMode = sortmode
self.NowPageNum = nowpagenum
# --本页面框架结构--------
self.CNTL_Stock = {}
# {
# 'title': # 标题
# 'body': # 主体
# }
# --本页面框架结构--------
# 动态显示个股信息
self.StringVar_DetailedInfo = {}
# 绘制初始框架
self.stockinfo_layout()
# 刷新页面
self.refresh()
def stockinfo_layout(self):
"""
个股信息页面初始布局
:return:
"""
# 标题框架
# --master self.window.CNTL_NowFace 桌面框架
frame_title_attrs = CANVAS_ATTRS.copy()
# frame_title_attrs['bg'] = 'red'
frame_title_place = {
'width': SIZE_STOCKFACE_TITLE_W,
'height': SIZE_STOCKFACE_TITLE_H,
'x': SIZE_STOCKFACE_MARGIN
}
self.CNTL_Stock['title'] = tk.Canvas(
self.window.CNTL_NowFace,
frame_title_attrs
)
self.CNTL_Stock['title'].place(frame_title_place)
draw_tb_line(self.CNTL_Stock['title'], frame_title_place, tt=(0, 1))
# 页面内容主体
# --master self.window.CNTL_NowFace 桌面框架
frame_content_attrs = FRAME_ATTRS.copy()
frame_content_attrs['bg'] = 'pink'
frame_content_place = {
'width': SIZE_STOCKFACE_CONTENT_W,
'height': SIZE_STOCKFACE_CONTENT_H,
'x': frame_title_place['x'],
'y': frame_title_place['height']
}
self.CNTL_Stock['content'] = tk.Frame(
self.window.CNTL_NowFace,
frame_content_attrs
)
self.CNTL_Stock['content'].place(frame_content_place)
def destroy_content(self):
# 清空内容框架
for widget in self.CNTL_Stock['content'].winfo_children():
widget.destroy()
def refresh(self):
"""
刷新当前页面
:return:
"""
# 绘制标题内容
self.draw_title()
# 获取最新数据
self.handle_data()
def handle_data(self):
"""
获取个股信息最新数据
:return:
"""
stockinfo = Inet.get_stockinfo(self.Code)
if not isinstance(stockinfo, dict):
# 如果不是字典,说明访问出错
if stockinfo == 'Time out' or stockinfo == 'Network error':
# 超时, 网络错误
self.window.draw_error(stockinfo)
elif stockinfo == 'TryAll error':
# 股票代码错误,或secid错误
messagebox.showerror(
title='错误',
message=f'{self.Code} 股票代码错误,或secid错误!'
)
else:
messagebox.showerror(
title='错误',
message='未知错误!'
)
return None
# 获取个股数据成功
print(f'个股最新数据:{stockinfo}')
# 遍历设置动态变量
for key, value in stockinfo.items():
self.StringVar_DetailedInfo[key] = tk.StringVar()
self.StringVar_DetailedInfo[key].set(value)
# 数据处理成功,内容框架。绘制内容
self.draw_content()
def draw_title(self):
"""
绘制页面标题
:return:
"""
# 字体框架
# --master self.CNTL_Stock['title'] 标题框架
frame_text_attrs = FRAME_ATTRS.copy()
# frame_text_attrs['bg'] = 'blue'
frame_text_place = {
'width': SIZE_STOCKFACE_TITLE_W * 0.5,
'height': SIZE_STOCKFACE_TITLE_H - SIZE_TBLINE_WIDTH,
}
frame_text = tk.Frame(
self.CNTL_Stock['title'],
frame_text_attrs,
)
frame_text.place(frame_text_place)
# 详情
# --master frame_text 字体框架
label_xq_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_TALL,
'font': FONT_TBNAME,
'text': '详 情'
}
label_xq = tk.Label(
frame_text,
label_xq_attrs
)
label_xq.pack(side='left')
# 数据提供
# --master frame_text 字体框架
label_provider_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_LOW,
'font': FONT_WARNING,
'text': '(以下数据由东方财富提供)'
}
label_provider = tk.Label(
frame_text,
label_provider_attrs
)
label_provider.pack(side='left')
# 按钮框架
# --master self.CNTL_Stock['title'] 标题框架
frame_btn_attrs = FRAME_ATTRS.copy()
# frame_btn_attrs['bg'] = 'green'
frame_btn_place = {
'width': SIZE_STOCKFACE_TITLE_W * 0.5,
'height': SIZE_STOCKFACE_TITLE_H - SIZE_TBLINE_WIDTH,
'x': frame_text_place['width']
}
frame_btn = tk.Frame(
self.CNTL_Stock['title'],
frame_btn_attrs,
)
frame_btn.place(frame_btn_place)
# 返回按钮
# --master frame_btn 按钮框架
button_back_attrs = BUTTON_ATTRS.copy()
button_back_attrs['text'] = '返回'
button_back_attrs['command'] = self.event_back
button_back = tk.Button(
frame_btn,
button_back_attrs
)
button_back.pack(side='right')
# 加持有按钮
# --master frame_btn 按钮框架
button_back_attrs = BUTTON_ATTRS.copy()
# 判断是否已经持有
if self.Code in Ldb.read_holdlist():
# 已持有
button_back_attrs['text'] = '已持有'
button_back_attrs['state'] = 'disable' # 禁用按钮
else:
button_back_attrs['text'] = '加持有'
button_back = tk.Button(
frame_btn,
button_back_attrs
)
button_back.pack(side='right', padx=10)
# 绑定事件
button_back.configure(
command=lambda
code=self.Code:
self.event_business(code)
)
def draw_content(self):
"""
绘制主体内容框架
:return:
"""
# 左边内容
# --master self.CNTL_Stock 内容框架
frame_leftcontent_attrs = FRAME_ATTRS.copy()
# frame_leftcontent_attrs['bg'] = 'red'
frame_leftcontent_place = {
'width': SIZE_STOCKTREND_PIC_W,
'height': SIZE_STOCKFACE_CONTENT_H
}
frame_leftcontent = tk.Frame(
self.CNTL_Stock['content'],
frame_leftcontent_attrs
)
frame_leftcontent.place(frame_leftcontent_place)
# 右边内容
# --master self.CNTL_Stock 内容框架
frame_rightcontent_attrs = FRAME_ATTRS.copy()
frame_rightcontent_attrs['bg'] = 'blue'
frame_rightcontent_place = {
'width': SIZE_STOCKFACE_CONTENT_W - SIZE_STOCKTREND_PIC_W,
'height': SIZE_STOCKFACE_CONTENT_H,
'x': SIZE_STOCKTREND_PIC_W
}
frame_rightcontent = tk.Frame(
self.CNTL_Stock['content'],
frame_rightcontent_attrs
)
frame_rightcontent.place(frame_rightcontent_place)
# 个股详基本信息和细信息框架
# --master frame_leftcontent 左边内容
frame_detailed_basic_info_attrs = FRAME_ATTRS.copy()
frame_detailed_basic_info_place = {
'width': frame_leftcontent_place['width'],
'height': frame_leftcontent_place['height'] - SIZE_STOCKTREND_PIC_H,
}
frame_detailed_basic_info = tk.Frame(
frame_leftcontent,
frame_detailed_basic_info_attrs
)
frame_detailed_basic_info.place(frame_detailed_basic_info_place)
# --【基本信息框架】--
# --master frame_detailedinfo 个股详基本信息和细信息框架
frame_basicinfo_attrs = FRAME_ATTRS.copy()
# frame_basicinfo_attrs['bg'] = 'red'
frame_basicinfo_place = {
'width': frame_detailed_basic_info_place['width'],
'height': frame_detailed_basic_info_place['height'] * 0.3
}
frame_basicinfo = tk.Frame(
frame_detailed_basic_info,
frame_basicinfo_attrs
)
frame_basicinfo.place(frame_basicinfo_place)
# --【详细信息框架】--
# --master frame_detailedinfo 个股详基本信息和细信息框架
frame_detailedinfo_attrs = FRAME_ATTRS.copy()
# frame_detailedinfo_attrs['bg'] = 'red'
frame_detailedinfo_place = {
'width': frame_detailed_basic_info_place['width'],
'height': frame_detailed_basic_info_place['height'] - frame_basicinfo_place['height'],
'y': frame_basicinfo_place['height'],
}
frame_detailedinfo = tk.Frame(
frame_detailed_basic_info,
frame_detailedinfo_attrs
)
frame_detailedinfo.place(frame_detailedinfo_place)
# -*-*-基本信息(股票名称、代码、涨跌幅、涨跌额、最高、最低、今开、昨收、最新价)
# 分为5等分
# --master frame_basicinfo 基本信息框架
frame_cellinfo_place = {
'width': frame_basicinfo_place['width'] / 5,
'height': frame_basicinfo_place['height'],
}
# 根据涨跌幅设置字体颜色
if float(self.StringVar_DetailedInfo['f3'].get()) < 0:
font_color = COLOR_FALL # 下跌
else:
font_color = COLOR_RISE # 涨
for i in range(5):
frame_cellinfo_place['x'] = i * frame_cellinfo_place['width']
frame_cellinfo = tk.Frame(
frame_basicinfo,
frame_basicinfo_attrs
)
frame_cellinfo.place(frame_cellinfo_place)
# 股票名称、股票代码
if i == 0:
# 股票名称
label_gpmc_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_STOCKINFO_DATA1,
'anchor': 's',
'textvariable': self.StringVar_DetailedInfo['f14']
}
label_gpmc = tk.Label(
frame_cellinfo,
label_gpmc_attrs
)
label_gpmc.pack(side='top', fill='y', expand='y')
# 股票代码
label_gpdm_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_TALL,
'font': FONT_STOCKINFO_CODE,
'anchor': 'n',
'textvariable': self.StringVar_DetailedInfo['f12']
}
label_gpdm = tk.Label(
frame_cellinfo,
label_gpdm_attrs
)
label_gpdm.pack(side='top', fill='y', expand='y')
# 最新价
elif i == 1:
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': font_color,
'font': FONT_STOCKINFO_DATA1,
'textvariable': self.StringVar_DetailedInfo['f2']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(expand='y', fill='both')
# 涨跌幅、涨跌额
elif i == 2:
# 涨跌幅
# 设置单位
self.StringVar_DetailedInfo['f3'].set(self.StringVar_DetailedInfo['f3'].get() + '%')
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': font_color,
'font': FONT_STOCKINFO_DATA2,
'anchor': 'w', # 文字显示在左边
'textvariable': self.StringVar_DetailedInfo['f3']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(side='top', fill='x', expand='y')
# 涨跌额
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': font_color,
'font': FONT_STOCKINFO_DATA2,
'anchor': 'w', # 文字显示在左边
'textvariable': self.StringVar_DetailedInfo['f4']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(side='top', fill='x', expand='y')
# 今开、昨收
elif i == 3:
# 今开
self.StringVar_DetailedInfo['f17'].set('今开:' + self.StringVar_DetailedInfo['f17'].get())
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_STOCKINFO_DATA2,
'anchor': 'w', # 文字显示在左边
'textvariable': self.StringVar_DetailedInfo['f17']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(side='top', fill='x', expand='y')
# 昨收
self.StringVar_DetailedInfo['f18'].set('昨收:' + self.StringVar_DetailedInfo['f18'].get())
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_STOCKINFO_DATA2,
'anchor': 'w', # 文字显示在左边
'textvariable': self.StringVar_DetailedInfo['f18']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(side='top', fill='x', expand='y')
# 最高、最低
elif i == 4:
# 最高
self.StringVar_DetailedInfo['f15'].set('最高:' + self.StringVar_DetailedInfo['f15'].get())
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_STOCKINFO_DATA2,
'anchor': 'w', # 文字显示在左边
'textvariable': self.StringVar_DetailedInfo['f15']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(side='top', fill='x', expand='y')
# 最低
self.StringVar_DetailedInfo['f16'].set('最低:' + self.StringVar_DetailedInfo['f16'].get())
label_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_STOCKINFO_DATA2,
'anchor': 'w', # 文字显示在左边
'textvariable': self.StringVar_DetailedInfo['f16']
}
label = tk.Label(
frame_cellinfo,
label_attrs
)
label.pack(side='top', fill='x', expand='y')
# -*-*-详细信息()
# 处理字段
self.StringVar_DetailedInfo['f20'].set('总市值(元):'+self.StringVar_DetailedInfo['f20'].get())
self.StringVar_DetailedInfo['f21'].set('流通市值:' + self.StringVar_DetailedInfo['f21'].get())
self.StringVar_DetailedInfo['f23'].set('市净:' + self.StringVar_DetailedInfo['f23'].get())
self.StringVar_DetailedInfo['f9'].set('动态市盈率:' + self.StringVar_DetailedInfo['f9'].get())
self.StringVar_DetailedInfo['f5'].set('成交量(手):' + self.StringVar_DetailedInfo['f5'].get())
self.StringVar_DetailedInfo['f6'].set('成交额(元):' + self.StringVar_DetailedInfo['f6'].get())
self.StringVar_DetailedInfo['f8'].set('换手率:' + self.StringVar_DetailedInfo['f8'].get())
self.StringVar_DetailedInfo['f10'].set('量比:' + self.StringVar_DetailedInfo['f10'].get())
tempLsit = [
[
self.StringVar_DetailedInfo['f20'],
self.StringVar_DetailedInfo['f21'],
self.StringVar_DetailedInfo['f23'],
self.StringVar_DetailedInfo['f9'],
],
[
self.StringVar_DetailedInfo['f5'],
self.StringVar_DetailedInfo['f6'],
self.StringVar_DetailedInfo['f8'],
self.StringVar_DetailedInfo['f10'],
],
]
# --master frame_detailedinfo 详细信息框架
# 框架属性
frame_detailed2_attrs = FRAME_ATTRS.copy()
# frame_detailed2_attrs['bg'] = 'pink'
frame_detailed2_place = {
'width': frame_detailedinfo_place['width'] / 2,
'height': frame_detailedinfo_place['height'],
}
# 标签属性
label_cell_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_STOCKINFO_DATA2,
# 'anchor': 'w', # 文字显示在左边
}
for i in range(2):
# 绘制框架
frame_detailed2 = tk.Frame(
frame_detailedinfo,
frame_detailed2_attrs
)
frame_detailed2_place['x'] = frame_detailed2_place['width'] * i
frame_detailed2.place(frame_detailed2_place)
for j in range(4):
label_cell_attrs['textvariable'] = tempLsit[i][j]
label_cell = tk.Label(
frame_detailed2,
label_cell_attrs
)
label_cell.pack(side='top', fill='y', expand='y')
# ------------------------------------------------------------------------------
# 个股日K图片
# --master frame_leftcontent 左边内容
label_trendpic_attrs = {
'bg': COLOR_BACKGROUND
}
label_trendpic_place = {
'width': SIZE_STOCKTREND_PIC_W,
'height': SIZE_STOCKTREND_PIC_H,
'y': frame_detailed_basic_info_place['height']
}
# 下载图片
if Inet.download_stocktrend(self.Code):
self.window.tkImage['tempStockTrend'] = tk.PhotoImage(file='Cache/tempStockTrend.png')
# 删除图片
os.remove('Cache/tempStockTrend.png')
# 图片下载成功
label_trendpic_attrs['image'] = self.window.tkImage['tempStockTrend']
else:
# 图片下载失败
label_trendpic_attrs['text'] = '暂无数据!'
label_trendpic_attrs['font'] = FONT_NONEDATA_ABNORMAL
label_trendpic_attrs['fg'] = COLOR_TIPS_LOW
# 绘制
label_trendpic = tk.Label(
frame_leftcontent,
label_trendpic_attrs
)
label_trendpic.place(label_trendpic_place)
# =================================================================================
# 统计框架
# --master frame_rightcontent 右边内容框架
frame_total_attrs = FRAME_ATTRS.copy()
frame_total_place = {
'width': frame_rightcontent_place['width'],
'height': frame_rightcontent_place['height'] * 0.15,
}
frame_total = tk.Frame(
frame_rightcontent,
frame_total_attrs
)
frame_total.place(frame_total_place)
# 历史总收益框架
# --master frame_total 统计框架
frame_profit_attrs = FRAME_ATTRS.copy()
# frame_profit_attrs['bg'] = 'pink'
frame_profit_place = {
'width': frame_total_place['width'] / 2,
'height': frame_total_place['height'],
'x': 0,
}
total_tips = ['近15条历史总收益', '近15条历史总收益率']
stringvar_profitTotal = [tk.StringVar(), tk.StringVar()]
cntl_total = []
# stringvar_profitTotal[0]总收益动态变量
# stringvar_profitTotal[1]总收益率动态变量
for i in range(2):
frame_profit = tk.Label(
frame_total,
frame_profit_attrs
)
frame_profit_place['x'] += frame_profit_place['width'] * i
frame_profit.place(frame_profit_place)
# 统计标题
# --master frame_profit 历史总收益tips
label_totalprofit_attrs = {
'fg': COLOR_TIPS_LOW,
'bg': COLOR_BACKGROUND,
'font': FONT_OPTIONRECORD_TOTAL,
'text': total_tips[i],
'anchor': 's'
}
label_totalprofit = tk.Label(
frame_profit,
label_totalprofit_attrs
)
label_totalprofit.pack(side='top', fill='y', expand='y')
# 统计数据
# --master frame_profit 历史总收益tips
label_totaldata_attrs = {
'fg': COLOR_TIPS_LOW,
'bg': COLOR_BACKGROUND,
'font': FONT_OPTIONRECORD_TOTAL,
'textvariable': stringvar_profitTotal[i],
'anchor': 'n'
}
label_totaldata = tk.Label(
frame_profit,
label_totaldata_attrs
)
label_totaldata.pack(side='top', fill='y', expand='y')
cntl_total.append(label_totaldata)
# ----------------------------------------------------
# 列表框架
# --master frame_rightcontent 右边内容框架
frame_list_attrs = FRAME_ATTRS.copy()
# frame_list_attrs['bg'] = 'red'
frame_list_place = {
'width': frame_rightcontent_place['width'],
'height': frame_rightcontent_place['height'] - frame_total_place['height'],
'y': frame_total_place['height'],
}
frame_list = tk.Frame(
frame_rightcontent,
frame_list_attrs
)
frame_list.place(frame_list_place)
# -----------
# 表格标题
# --master frame_list 列表框架
label_tname_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_LOW,
'font': FONT_OPTIONRECORD_TNAME,
'text': '操作记录(仅展示最近15条记录!)',
'anchor': 'w'
}
label_tname_place = {
'width': frame_list_place['width'],
'height': frame_list_place['height'] * 0.05,
}
label_tname = tk.Label(
frame_list,
label_tname_attrs
)
label_tname.place(label_tname_place)
# 表格表头框架
# --master frame_list 列表框架
frame_thead_attrs = CANVAS_ATTRS.copy()
frame_thead_place = {
'width': frame_list_place['width'],
'height': frame_list_place['height'] * 0.06,
'y': label_tname_place['height'],
}
frame_thead = tk.Canvas(
frame_list,
frame_thead_attrs
)
frame_thead.place(frame_thead_place)
# 绘制表头表格线条
draw_tb_line(frame_thead, frame_thead_place, tt=(1, 1))
# 表身框架
# --master frame_list 列表框架
frame_tbody_attrs = FRAME_ATTRS.copy()
# frame_tbody_attrs['bg'] = 'pink'
frame_tbody_place = {
'width': frame_list_place['width'],
'height': frame_list_place['height'] - frame_thead_place['height'],
'y': frame_thead_place['height'] + frame_thead_place['y'],
}
frame_tbody = tk.Frame(
frame_list,
frame_tbody_attrs
)
frame_tbody.place(frame_tbody_place)
# -----------------------------
# 表头字段
# --master frame_thead 表头框架
thead_text = ('持有时间段', '天数', '收益(元)', '收益率')
thead_width = (0.40, 0.10, 0.30, 0.20)
label_thead_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_LOW,
'font': FONT_OPTIONRECORD_THEAD,
}
label_thead_place = {
'width': 0,
'height': frame_thead_place['height'] - SIZE_TBLINE_WIDTH * 2,
'x': 0,
'y': SIZE_TBLINE_WIDTH
}
# 遍历绘制表头字段
for index in range(len(thead_text)):
label_thead_attrs['text'] = thead_text[index]
label_thead = tk.Label(
frame_thead,
label_thead_attrs
)
label_thead_place['x'] += label_thead_place['width']
label_thead_place['width'] = frame_thead_place['width'] * thead_width[index]
label_thead.place(label_thead_place)
# -*-*-判断是否有数据
historyOption_list = Ldb.read_optionhistory(self.Code)
if not isinstance(historyOption_list, tuple):
# 无数据
self.window.draw_errors(frame_tbody, 'None data')
else:
# 有记录
print(f'读取的操作记录列表:{historyOption_list}')
# 设置统计数据,根据值给出不同颜色
if historyOption_list[1][0] > 0:
cntl_total[0]['fg'] = COLOR_RISE
cntl_total[1]['fg'] = COLOR_RISE
else:
cntl_total[0]['fg'] = COLOR_FALL
cntl_total[1]['fg'] = COLOR_FALL
stringvar_profitTotal[0].set(historyOption_list[1][0])
stringvar_profitTotal[1].set(historyOption_list[1][1])
# 按行绘制数据
for aline in historyOption_list[0]:
# 一行框架
# --master frame_tbody 表身框架
frame_aline_attrs = CANVAS_ATTRS.copy()
frame_aline_attrs['width'] = frame_tbody_place['width']
frame_aline_attrs['height'] = SIZE_STOCKINFO_ALINE_H
frame_aline = tk.Canvas(
frame_tbody,
frame_aline_attrs
)
frame_aline.pack()
draw_tb_line(frame_aline, frame_aline_attrs, tt=(0, 1))
# 单元格数据
# --master frame_aline 一行框架
label_data_attrs = {
'bg': COLOR_BACKGROUND
}
label_data_place = {
'width': 0,
'height': frame_aline_attrs['height'] - SIZE_TBLINE_WIDTH,
'x': 0
}
for cell_index in range(len(thead_width)):
# print(aline[cell_index])
# 根据收益设置字体颜色
if aline[2] > 0:
fg = COLOR_RISE
elif aline[2] < 0:
fg = COLOR_FALL
else:
fg = COLOR_TIPS_TALL
label_data_attrs['fg'] = fg
label_data_attrs['text'] = aline[cell_index]
label_data = tk.Label(
frame_aline,
label_data_attrs
)
label_data_place['x'] += label_data_place['width']
label_data_place['width'] = frame_aline_attrs['width'] * thead_width[cell_index]
label_data.place(label_data_place)
def event_back(self):
"""
返回事件
:return:
"""
if self.window.tdResult_Choice:
# 返回荐股结果页面
ChoiceResult(self.window, self.SortColumn, self.SortMode, self.NowPageNum)
else:
ChoiceFace(self.window)
class Tabel(FACE):
"""表格形式的布局类,即所有表格形式的页面继承该类"""
def __init__(self, window):
FACE.__init__(self, window)
self.CNTL_Tabel = {} # 存储表格基础框架控件对象
# key:
# 'name' 表格名称框架
# 'thead' 表头框架
# 'tbody' 表身框架
# 'page' 表格分页框架
# -----TName表格统计相关变量 ↓↓↓↓↓↓↓↓↓↓-----
self.TotalText = () # 存储统计的字段
self.CNTL_TotalData = None # 存储统计数据Label控件对象,方便改变状态
# -----TName表格标题相关变量 ↓↓↓↓↓↓↓↓↓↓-----
# -----Thead表头相关变量 ↓↓↓↓↓↓↓↓↓↓-----
self.TheadText = {}
"""
Example:
# 子类赋值
self.TheadText = {
'序号': {
'param': None,
'width': 0.1, # 该列的宽度占比,总宽度不超过 1
'CNTL': , # 表头控件对象,用于改变状态
},
......
}
# self.TheadText的keys内容就是表头的内容,数量就是数据列数
self.draw_thead() # 调用绘制表头,即可绘制表头
"""
# -----Thead表头相关变量 ↑↑↑↑↑↑↑↑↑↑-----
self.Mode = None # 存储当前模式
# self.Mode = 1 持有
# self.Mode = 2 历史
# -----Tbody表身相关变量 ↓↓↓↓↓↓↓↓↓↓-----
self.CNTL_TabelTbody = None
# 如果有滚动条, self.CNTL_TabelTbody 就是滚动条视图
# 如果没有滚动条, self.CNTL_TabelTbody 就指向 self.CNTL_Tabel['tbody']。
self.Tbody_DataLIST = None # 存储表身数据
self.Total_Data = None # 存储统计数据
self.SortColumn = None # 当前排序列
self.SortMode = None # 当前排序方式
# -----Tbody表身相关变量 ↑↑↑↑↑↑↑↑↑↑-----
# -----分页相关变量 ↓↓↓↓↓↓↓↓↓↓-----
self.PageNum = 10 # 每页的数据条目
self.NowPageNum = 0 # 当前页数
self.PageTotalNum = 0 # 总页数
self.LENDataLIST = 0 # 总数据条目
self.StringVar_PageInfo = tk.StringVar() # 页数动态显示
self.StringVar_JumpEntry = tk.StringVar()
# -----分页相关变量 ↑↑↑↑↑↑↑↑↑↑-----
# 绘制表格形式的布局
self.table_layout()
def table_layout(self):
"""
绘制表格形式的布局
"""
# --表格名称框架-----------------------
# -- master: self.window.CNTL_NowFace 桌面
frame_tabelname_attrs = FRAME_ATTRS.copy()
# frame_tabelname_attrs['bg'] = 'red'
frame_tabelname_palce = {
'width': SIZE_TBNAME_W,
'height': SIZE_TBNAME_H,
'x': SIZE_TABEL_MARGIN,
'y': 0,
}
self.CNTL_Tabel['name'] = tk.Frame(
self.window.CNTL_NowFace, # 基于当前的桌面
frame_tabelname_attrs
)
self.CNTL_Tabel['name'].place(frame_tabelname_palce)
# --表格名称框架-----------------------
# --表格框架-----------------------
# -- master: self.window.CNTL_NowFace 桌面
frame_tabel_attrs = FRAME_ATTRS.copy()
# frame_tabel_attrs['bg'] = 'blue'
frame_tabel_place = {
'width': SIZE_TB_W,
'height': SIZE_TB_H,
'x': SIZE_TABEL_MARGIN,
'y': frame_tabelname_palce['y'] + frame_tabelname_palce['height']
}
frame_tabel = tk.Frame(
self.window.CNTL_NowFace, # 基于当前的桌面
frame_tabel_attrs
)
frame_tabel.place(frame_tabel_place)
# 表头框架
# --master frame_tabel 表格框架
canvas_thead_atrrs = CANVAS_ATTRS.copy()
# canvas_thead_atrrs['bg'] = 'yellow'
canvas_thead_place = {
'width': SIZE_THEAD_W,
'height': SIZE_THEAD_H,
}
self.CNTL_Tabel['thead'] = tk.Canvas(
frame_tabel,
canvas_thead_atrrs
)
self.CNTL_Tabel['thead'].place(canvas_thead_place)
# 绘制表头线条
draw_tb_line(self.CNTL_Tabel['thead'], canvas_thead_place)
# 表身框架
# --master frame_tabel 表格框架
frame_tbody_attrs = FRAME_ATTRS.copy()
# frame_tbody_attrs['bg'] = 'pink'
frame_tbody_place = {
'width': SIZE_TBODY_W,
'height': SIZE_TBODY_H,
'y': SIZE_THEAD_H,
}
self.CNTL_Tabel['tbody'] = tk.Frame(
frame_tabel,
frame_tbody_attrs
)
self.CNTL_Tabel['tbody'].place(frame_tbody_place)
# --表格框架-----------------------
# --分页画布-----------------------
# -- master: self.window.CNTL_NowFace 桌面
canvas_paging_attrs = CANVAS_ATTRS.copy()
# canvas_paging_attrs['bg'] = 'green'
canvas_paging_place = {
'width': SIZE_TBPAGE_W,
'height': SIZE_TBPAGE_H,
'x': SIZE_TABEL_MARGIN,
'y': frame_tabel_place['y'] + frame_tabel_place['height']
}
self.CNTL_Tabel['page'] = tk.Canvas(
self.window.CNTL_NowFace, # 基于当前的桌面
canvas_paging_attrs
)
self.CNTL_Tabel['page'].place(canvas_paging_place)
# 绘制分页分割线
draw_tb_line(self.CNTL_Tabel['page'], canvas_paging_place, tt=(1, 0))
# --分页画布-----------------------
def destory_tname(self):
"""清空表格标题,方便重新绘制"""
for widget in self.CNTL_Tabel['name'].winfo_children():
widget.destroy()
def destroy_tbody(self):
"""清空表身,方便重新绘制"""
for widget in self.CNTL_Tabel['tbody'].winfo_children():
widget.destroy()
self.CNTL_TabelTbody = None
def destroy_tpage(self):
"""清空分页框架,方便重新绘制"""
for widget in self.CNTL_Tabel['page'].winfo_children():
widget.destroy()
def refresh(self):
"""
刷新整个表格页面
:return:
"""
# 绘制表格标题
self.draw_tname()
# 绘制表头
self.draw_thead()
# 读取数据
self.read_tbdata()
# 判断是否有数据
if self.Tbody_DataLIST:
if isinstance(self.Tbody_DataLIST, str):
# 禁用表头排序
self.disabel_theadbtn()
# 绘制异常
self.window.draw_errors(self.CNTL_Tabel['tbody'], self.Tbody_DataLIST)
else:
# print(f'读取到了数据:{self.Tbody_DataLIST}')
if self.TotalText:
# 设置统计数据
self.stringvar_set()
# 绘制表身
self.draw_tbody()
# 绘制分页
self.draw_tpage()
else:
# 禁用表头排序
self.disabel_theadbtn()
# 无数据
self.window.draw_errors(self.CNTL_Tabel['tbody'], 'None data')
def stringvar_set(self):
"""
设置统计数据
:return:
"""
fg = COLOR_THEME
# 根据收益改变字体颜色
if self.Total_Data[2] > 0.0:
fg = COLOR_RISE
elif self.Total_Data[2] < 0.0:
fg = COLOR_FALL
# 遍历设置统计数据
for index in range(len(self.CNTL_TotalData)):
if index > 1:
self.CNTL_TotalData[index]['fg'] = fg
if index == 3:
# 给收益率加上%
self.CNTL_TotalData[index]['text'] = str(round(self.Total_Data[index], 2)) + '%'
else:
self.CNTL_TotalData[index]['text'] = round(self.Total_Data[index], 2)
def read_tbdata(self):
"""
读取并处理表格数据
:return:
"""
self.LENDataLIST = 0 # 重置总数
self.Tbody_DataLIST, self.Total_Data = Ldb.read_localdata(self.Mode, self.SortColumn, self.SortMode)
print(f'读取到的本地数据:{self.Tbody_DataLIST}')
if self.Tbody_DataLIST:
self.Tbody_DataLIST = handle_paging(self.Tbody_DataLIST, self.PageNum)
for page in self.Tbody_DataLIST:
self.LENDataLIST += len(page) # 总数据条目
self.PageTotalNum = len(self.Tbody_DataLIST) # 总页数
def draw_tname(self):
"""
绘制表格标题
:return:
"""
self.destory_tname()
self.CNTL_TotalData = [] # 清空存储的控件
lenTotal = len(self.TotalText)
# 统计框架
# --master self.Tabel['name'] 表格标题框架
frame_totalterm_attrs = FRAME_ATTRS.copy()
frame_totalterm_attrs['bg'] = COLOR_BACKGROUND
frame_totalterm_attrs['width'] = SIZE_TBNAME_W / lenTotal
frame_totalterm_attrs['height'] = SIZE_TBNAME_H
for index in range(lenTotal):
# 统计框架
frame_totalterm = tk.Frame(
self.CNTL_Tabel['name'],
frame_totalterm_attrs
)
frame_totalterm.pack(side='left', fill='x', expand='y')
# 合计项tip
# -- master: frame_totalterm 合计项框架
label_totaltip_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_LOW,
'font': FONT_TBTOTAL,
'text': self.TotalText[index],
}
label_totaltip = tk.Label(
frame_totalterm,
label_totaltip_attrs
)
label_totaltip.pack(side='top', fill='both', expand='y')
# 合计数据
# -- master: frame_totalterm 合计项框架
label_total_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_TBTOTAL2,
}
# 位置处在合计的下方
label_total = tk.Label(
frame_totalterm,
label_total_attrs
)
label_total.pack(side='top', fill='x', expand='y')
self.CNTL_TotalData.append(label_total)
def draw_thead(self):
"""
Example:
# 子类赋值
self.TheadText = {
'序号': {
'param': None,
'width': 0.1, # 该列的宽度占比,总宽度不超过 1
},
......
}
# self.TheadText的keys内容就是表头的内容,数量就是数据列数
self.draw_thead() # 调用绘制表头,即可绘制表头
:return:
"""
# print(f'需要绘制的表头信息:{self.TheadText}')
# 判断总占比是否超过 1
percentage = 0
for t in self.TheadText.values():
percentage += t['width']
if percentage > 1.000000:
print(f'\n\n框架占比错误:{percentage}')
exit() # 退出程序
# --通用属性
all_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEAD_FONT,
'font': FONT_THEAD,
}
# --无排序操作的表头基本信息,即Label
label_thead_attrs = all_attrs.copy()
# --有排序操作的表头,即Button
button_thead_attrs = all_attrs.copy()
button_thead_attrs['activebackground'] = COLOR_BACKGROUND
button_thead_attrs['activeforeground'] = COLOR_THEAD_FONT
button_thead_attrs['bd'] = 0
button_thead_attrs['cursor'] = 'mouse' # 鼠标移动到控件的鼠标状态
button_thead_attrs['disabledforeground'] = COLOR_THEAD_FONT, # 禁用时的按钮文本颜色
# --表头的place布局
all_thead_place = {
'height': SIZE_THEAD_H - (SIZE_TBLINE_WIDTH * 2), # 防止挡住表格线条
'y': SIZE_TBLINE_WIDTH,
}
# 设置上一个表头布局
last_thead_place = all_thead_place.copy()
last_thead_place['x'] = 0
last_thead_place['width'] = 0
# 遍历绘制表头
for key in self.TheadText.keys():
# print(f'正在绘制“{key}”表头')
# 判断有无排序功能, 绘制不同类型的不同
# --master self.CNTL_Tabel['thead'] 表头框架
# label_thead_attrs['bg'] = test[randint(0, 3)]
sort_ = False
if not self.TheadText[key]['param']:
# 如果有参数,说明该按钮可排序
# print('不可排序!')
thead = tk.Label(
self.CNTL_Tabel['thead'],
label_thead_attrs
)
else:
# print('可排序!')
# 判断当前绘制的是否排序表头
if self.SortColumn in self.TheadText[key]['param']:
# print(f'当前排序的列:{key}')
# 得到当前排序字段的下标,后面需要根据下标设置 排序标识
colindex = self.TheadText[key]['param'].index(self.SortColumn)
sort_ = True
else:
sort_ = False
thead = tk.Button(
self.CNTL_Tabel['thead'],
button_thead_attrs
)
# 绑定排序事件
thead.configure(
command=lambda
param=self.TheadText[key]['param']: # 排序需要的参数
self.event_sort(param)
)
# 存储控件对象
self.TheadText[key]['CNTL'] = thead
# 根据当前排序状态, 重新设置表头信息
if sort_:
# 设置当前排序列的背景
thead['bg'] = COLOR_TBSORT_BG
thead['fg'] = COLOR_TIPS_TALL
# 设置当前排序标识
texts = key.split('/')
if self.SortMode == 'desc':
texts[colindex] = texts[colindex] + ' ↓'
elif self.SortMode == 'asc':
texts[colindex] = texts[colindex] + ' ↑'
# 设置排序标识
theadtext = ''
for ttext in texts:
theadtext += ttext + '/'
theadtext = theadtext[:-1] # 去掉最后的 /
thead['text'] = theadtext
else:
thead['text'] = key
# 设置表头布局
thead_place = all_thead_place.copy()
thead_place['x'] = last_thead_place['x'] + last_thead_place['width']
thead_place['width'] = SIZE_THEAD_W * self.TheadText[key]['width']
# 记录上一个表头布局
last_thead_place = thead_place.copy()
# 打包控件
thead.place(thead_place)
def draw_tbody_scroll(self):
"""
判断是否需要滚动条
:return:
"""
# 清空表身
self.destroy_tbody()
# !*!*!*-- 判断是否需要滚动条 --!*!*!*
# 如果本页数据的总高度 > 表身的高度。 说明需要滚动条
# SIZE_TLINE_H 一行的高度
# SIZE_TBODY_H 表身的高度
if len(self.Tbody_DataLIST[self.NowPageNum]) * SIZE_TLINE_H > SIZE_TBODY_H:
# print('需要滚动条')
mysf = MyScrollFrame(self.CNTL_Tabel['tbody']) # 滚动条类
mysf.pack(side="top", fill="both", expand=True) # 打包滚动条
self.CNTL_TabelTbody = mysf.get_container() # 返回可滚动视图,即表身数据容器
else:
self.CNTL_TabelTbody = self.CNTL_Tabel['tbody']
# !*!*!*-- 判断是否需要滚动条 --!*!*!*
def draw_tbody(self):
"""
绘制表身
:return:
"""
# 判断是否需要滚动条
self.draw_tbody_scroll()
pageData = self.Tbody_DataLIST[self.NowPageNum]
print(f'第 {self.NowPageNum} 页数据:{pageData}')
# 获取持有列表
holdList = Ldb.read_holdlist()
# 遍历每行数据
for index in range(len(pageData)):
self.draw_aline(index, holdList)
def draw_frame_aline(self, place=None):
"""
根据表头,绘制一行和数据单元格框架
:param place: 是否开启palce布局方式, 传参palce需要的参数{}
:return:
【dict】 - {
'序号': <tkinter.Frame object .!frame2.!frame2.!frame.!canvas.!frame>, # 对应的控件对象
......
}
"""
# 返回字典
rtrn = {}
# 判断表身框架
if not self.CNTL_TabelTbody:
cntl_ = self.CNTL_Tabel['tbody']
else:
cntl_ = self.CNTL_TabelTbody
# 一行画布
# --master self.CNTL_TabelTbody 表身框架
canvas_aline_attrs = CANVAS_ATTRS.copy()
# canvas_aline_attrs['bg'] = 'red'
canvas_aline_attrs['width'] = SIZE_TLINE_W
canvas_aline_attrs['height'] = SIZE_TLINE_H
aline = tk.Canvas(
cntl_,
canvas_aline_attrs
)
# 判断布局方式
if not place:
aline.pack()
else:
aline.place(place)
# 绘制表行的线条
draw_tb_line(aline, canvas_aline_attrs, tt=(0, 1))
# 单元格
# --master aline 一行画布
frame_cell_attrs = FRAME_ATTRS.copy()
# frame_cell_attrs['bg'] = 'blue'
frame_cell_place = {
'height': SIZE_TLINE_H - SIZE_TBLINE_WIDTH
}
# 初始位置
last_place = frame_cell_place.copy()
last_place['x'] = 0
last_place['width'] = 0
# 根据表头,遍历绘制单元格框架
for k in self.TheadText.keys():
# print(f'宽度{self.TheadText[k]["width"]}')
frame_cell_place['x'] = last_place['x'] + last_place['width']
frame_cell_place['width'] = self.TheadText[k]["width"] * SIZE_TLINE_W
cell = tk.Frame(
aline,
frame_cell_attrs
)
cell.place(frame_cell_place)
# 记录上一个控件位置
last_place = frame_cell_place.copy()
# 存储控件对象
rtrn[k] = {'CNTL': cell}
rtrn[k]['place'] = frame_cell_place.copy()
return rtrn
def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
"""
一行一行绘制数据,字类继承重写
:param rowsindex: 行数下标
:param holdlist: 持有列表
:param btn3state: 展开按钮状态
:param place: 是否开启place布局
:return:
"""
def draw_tpage(self):
"""
绘制分页
:return:
"""
frame_paging_attrs = FRAME_ATTRS.copy()
# frame_paging_attrs['bg'] = 'red'
frame_paging_place = {
'width': SIZE_TBPAGE_W,
'height': SIZE_TBPAGE_H - SIZE_TBLINE_WIDTH,
'x': 0,
'y': SIZE_TBLINE_WIDTH,
}
frame_paging = tk.Frame(
self.CNTL_Tabel['page'],
frame_paging_attrs
)
frame_paging.place(frame_paging_place)
# 页数和数据条目
# -- master: frame_paging 页数信息框架
label_pageinfo_attrs = LABEL_PAGE_ATTRS.copy()
# label_pageinfo_attrs['bg'] = 'blue'
label_pageinfo_attrs['textvariable'] = self.StringVar_PageInfo
label_pageinfo = tk.Label(
frame_paging,
label_pageinfo_attrs
)
label_pageinfo.pack(side='right', fill='y')
# 设置页数信息
if self.PageTotalNum > 1:
# 页数大于1时才绘制按钮
self.StringVar_PageInfo.set(
f'第 {self.NowPageNum + 1} 页,共 {self.PageTotalNum} 页,每页 {self.PageNum} 条,共 {self.LENDataLIST} 条'
)
# 判断翻页按钮属性
# ========判断分页按钮状态======
# 'normal' : 启用
# 'disabled' : 禁用
next_state = 'normal' # 下一页
previous_state = 'normal' # 上一页
first_state = 'normal' # 首页
last_state = 'normal' # 末页
if self.NowPageNum == 0: # 在首页
first_state = 'disabled'
previous_state = 'disabled'
elif self.NowPageNum + 1 == self.PageTotalNum: # 如果当前页 + 1 == 页数,说明在尾页
next_state = 'disabled'
last_state = 'disabled'
# **按钮属性**
button_page_attrs = {
'fg': COLOR_BTNFG,
'bg': COLOR_BACKGROUND,
'font': FONT_TBPAGE,
"relief": "solid",
"bd": 1,
"activebackground": COLOR_BACKGROUND,
"activeforeground": COLOR_BTNFG,
}
# 末页
# -- master frame_paging 分页框架
button_lastpage_attrs = button_page_attrs.copy()
button_lastpage_attrs['text'] = ' 末页 '
button_lastpage_attrs['state'] = last_state
button_lastpage = tk.Button(
frame_paging,
button_lastpage_attrs
)
button_lastpage.pack(side='right', padx=5)
# self.CNTL_Page.append(button_lastpage)
button_lastpage.configure(command=lambda: self.turn_page('last'))
# 下一页按钮
# -- master frame_paging 分页框架
button_nextpage_attrs = button_page_attrs.copy()
button_nextpage_attrs['text'] = ' 下一页 '
button_nextpage_attrs['state'] = next_state
button_nextpage = tk.Button(
frame_paging,
button_nextpage_attrs
)
button_nextpage.pack(side='right', padx=5)
# self.CNTL_Page.append(button_nextpage)
button_nextpage.configure(command=lambda: self.turn_page('next'))
# 上一页按钮
# -- master frame_paging 分页框架
button_previouspage_attrs = button_page_attrs.copy()
button_previouspage_attrs['text'] = ' 上一页 '
button_previouspage_attrs['state'] = previous_state
button_previouspage = tk.Button(
frame_paging,
button_previouspage_attrs
)
button_previouspage.pack(side='right', padx=5)
# self.CNTL_Page.append(button_previouspage)
button_previouspage.configure(command=lambda: self.turn_page('previous'))
# 首页按钮
# -- master self.CNTL_Paging 分页框架
button_firstpage_attrs = button_page_attrs.copy()
button_firstpage_attrs['text'] = ' 首页 '
button_firstpage_attrs['state'] = first_state
button_firstpage = tk.Button(
frame_paging,
button_firstpage_attrs
)
button_firstpage.pack(side='right', padx=5)
# self.CNTL_Page.append(button_firstpage)
button_firstpage.configure(command=lambda: self.turn_page('first'))
# 页 Label
# -- master self.CNTL_Paging 分页框架
# 属性在前面
label_ye = tk.Label(
frame_paging,
LABEL_PAGE_ATTRS.copy()
)
label_ye.pack(side='right', fill='y')
# 输入框架
# -- master self.CNTL_Paging 分页框架
# entry_line_attrs = FRAME_ATTRS.copy()
frame_entry_attrs = {
'bg': COLOR_BACKGROUND,
'width': 55,
'height': 30,
}
frame_entry = tk.Frame(
frame_paging,
frame_entry_attrs
)
frame_entry.pack(side='right', padx=5)
# 页数输入框
# -- master frame_entry 输入框架
# 设置跳转页显示
self.StringVar_JumpEntry.set("")
entry_page_attrs = {
'bg': COLOR_BACKGROUND,
'bd': 0,
'font': FONT_TBPAGE,
'fg': COLOR_TIPS_TALL,
# 'relief': 'solid',
'justify': 'center',
# 'state': entry_state,
'textvariable': self.StringVar_JumpEntry,
'validate': "key",
'validatecommand': (self.window.OnlyNumber, '%P'), # 显示entry只能输入数字
# 'width': 5,
}
entry_page_place = {
'width': frame_entry_attrs['width'],
'height': frame_entry_attrs['height'] - 2,
'x': 0,
'y': 0
}
entry_page = tk.Entry(
frame_entry,
entry_page_attrs
)
entry_page.place(entry_page_place)
# 输入框横线
# --master frame_entry 输入框架
entryline_attrs = {
'bg': 'black'
}
entryline_place = {
'width': frame_entry_attrs['width'],
'height': 2,
'y': entry_page_place['height']
}
entryline = tk.Frame(
frame_entry,
entryline_attrs
)
entryline.place(entryline_place)
# 跳转按钮
# -- master self.CNTL_Paging 分页框架
button_jumppage_attrs = button_page_attrs.copy()
button_jumppage_attrs['text'] = ' 跳转至 '
button_jumppage = tk.Button(
frame_paging,
button_jumppage_attrs
)
button_jumppage.pack(side='right', padx=5)
button_jumppage.configure(command=lambda: self.jump_page())
else:
self.StringVar_PageInfo.set(f'合计 {self.LENDataLIST} 条')
def event_sort(self, params: list):
"""
排序事件
:param params: 参数列表 - 0 排序的列key; 1 排序的字段; 2 排序的方式
:return:
"""
# print(f'参数:{params}')
# print(f'当前排序状态:{self.SortColumn, self.SortMode}')
# 判断当前点击的列是否当前排序的列
if self.SortColumn in params:
# print('点击了当前正在排序的列')
if self.SortMode == 'asc':
# 如果当前排序方式是'asc',需要更换字段
self.SortMode = 'desc' # 设置排序方式
if self.SortColumn == params[-1]:
# 如果当前排序字段是最后一个,直接跳转第一个
self.SortColumn = params[0]
else:
# 如果不是最后一个,跳转下一个
i = params.index(self.SortColumn) + 1
self.SortColumn = params[i]
elif self.SortMode == 'desc':
# 如果是'desc',直接改变排序方式即可
self.SortMode = 'asc'
else:
# print('点击了非当前正在排序的列')
self.SortColumn = params[0]
self.SortMode = 'desc'
# print(f'更改后的排序状态:{self.SortColumn, self.SortMode}')
# 重新绘制页面
self.refresh()
def turn_page(self, operation):
"""
翻页事件
:param operation: list [按钮对象, 操作]
'last' : 末页
'next' : 下一页
'previous' : 上一页
'first' : 首页
:return:
"""
if operation == 'last':
self.NowPageNum = self.PageTotalNum - 1
elif operation == 'next':
self.NowPageNum += 1
elif operation == 'previous':
self.NowPageNum -= 1
elif operation == 'first':
self.NowPageNum = 0
# 翻页
self.draw_tbody()
# 重绘分页
self.draw_tpage()
def jump_page(self):
"""
跳转指定页
:return:
"""
# print(f'输入的值:{self.StringVar_JumpEntry.get()}')
try:
input_page = int(self.StringVar_JumpEntry.get()) # 得到当前输入的页
except ValueError:
# 输入为空,不做任何操作
return None
if input_page >= self.PageTotalNum: # 如果输入的页数 > 总页数 直接翻页到最后
jump = self.PageTotalNum - 1
elif input_page <= 1: # 如果输入的页数 <= 1 直接翻页到首页
jump = 0
else: # 其他页面直接翻到指定页
jump = input_page
# 如果输入的与当前一致,不做任何操作
if jump == self.NowPageNum:
return None
self.NowPageNum = jump # 改变当前页数
self.draw_tbody()
# 重绘分页
self.draw_tpage()
def disabel_theadbtn(self):
"""禁用表头排序"""
for value in self.TheadText.values():
if value['param']:
# 只有有排序功能的按钮才能禁用
value['CNTL']['state'] = 'disable' # 禁用按钮
class HoldFace(Tabel):
def __init__(self, window):
Tabel.__init__(self, window)
# --No.1--初始化排序----------
self.Mode = 1 # 持有
self.SortColumn = 'UpAndDownRange'
self.SortMode = 'desc'
# --No.2--初始化统计----------
self.TotalText = ('持有总成本', '持有总市值', '持有总收益', '持有总收益率')
# --No.3--初始化表头----------
self.TheadText = {
'名称/代码': {'param': ('Name', 'Code'), 'width': 0.18},
'加入价格/时间': {'param': ('BuyPrice', 'BuyDate'), 'width': 0.22},
'最新价格/时间': {'param': ('Price', 'Date'), 'width': 0.22},
'涨跌': {'param': ('UpAndDownPrice',), 'width': 0.12},
'涨跌幅': {'param': ('UpAndDownRange',), 'width': 0.12},
'操作': {'param': None, 'width': 0.14},
} # param不会空表示,该表头可排序。内容即,sort需要的参数。使用关键字参数
# 存储定时更新最新结果的线程
self.tdResult = None
# 绘制页面
self.refresh()
# 开启定时刷新持有
if not self.window.monitorState_Hold:
print('开启监听!')
self.window.monitorState_Hold = True
self.monitor_hold_tdresult()
def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
"""
重写父类,方法。绘制一行数据
:param rowsindex:
:param holdlist:
:param btn3state:
:param place:
:return:
"""
aline = self.Tbody_DataLIST[self.NowPageNum][rowsindex]
# 通用属性
attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_TBDATA1,
}
# 绘制行框架
cntl_dict = self.draw_frame_aline(place=place)
# 遍历绘制单元格数据
for key, cntl in cntl_dict.items():
if key in ('涨跌', '涨跌幅'):
# 根据涨跌设置字体颜色
fg = COLOR_THEME
if aline[7] > 0:
fg = COLOR_RISE
elif aline[7] < 0:
fg = COLOR_FALL
# 判断是哪一列
if key == '涨跌':
text = round(aline[7], 2)
elif key == '涨跌幅':
text = str(round(aline[8], 2)) + '%' # 保留两位小数并加上单位
else:
text = 'ERROR'
label_attrs = attrs.copy()
label_attrs['text'] = text
label_attrs['fg'] = fg
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_attrs
).pack(expand='yes', fill='both')
elif key == '名称/代码':
# 一行存在两个数据
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[1]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[2]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '加入价格/时间':
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[3]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[4]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '最新价格/时间':
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[5]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[6]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '操作':
# 操作按钮居中框架
# --master cntl_dict[key]['CNTL'] 单元格框架
frame_optionbtncneter_attrs = FRAME_ATTRS.copy()
frame_optionbtncneter = tk.Frame(
cntl_dict[key]['CNTL'],
frame_optionbtncneter_attrs
)
frame_optionbtncneter.pack(side='top', expand='yes') # 水平垂直居中
# 详情按钮
# --master frame_optionbtncneter 单元格居中框架
button_details_attrs = BUTTON_ATTRS.copy()
button_details_attrs['text'] = '卖出'
button_details = tk.Button(
frame_optionbtncneter,
button_details_attrs
)
button_details.pack(side='left', padx=5)
# 绑定详情事件
button_details.configure(
command=lambda
oder=aline[0], # 库中id
: self.event_business(oder)
)
def monitor_hold_tdresult(self):
"""
监听更新持有结果
:return:
"""
# 只有当前处于持有页面才继续监听
if self.window.NowFaceCode == 1:
print('当前处于持有页面')
# 计算时间
if calc_time():
# 判断当前是否交易日
open = isopen()
if open > 0:
# 监听延时到开盘
print(f'延时 {open}s 监听!')
self.window.ROOT.after(open*1000, self.monitor_hold_tdresult)
elif open < 0:
print('交易已结束或不是交易日!')
# 停用监听
self.window.monitorState_Hold = 'Not open'
else:
print('正在交易中...')
# 判断是否有线程在执行
if self.window.tdObject_Hold:
print('当前有线程执行')
# 当前有线程在执行,获取线程结果
tdResult = self.window.tdObject_Hold.get_result()
if tdResult == 'success':
print('后台更新完成!')
# 更新完成,刷新页面
self.refresh()
# 开启新的更新最新价线程
self.window.td_run_hold()
self.window.ROOT.after(2000, self.monitor_hold_tdresult)
elif tdResult == 'Network error':
messagebox.showerror(title='网络错误', message='网络连接错误,实时刷新持有股票最新价格将失效!!!')
self.window.monitorState_Hold = False
self.window.tdObject_Hold = None # 清空线程
elif tdResult == 'None data':
print('无持有,延时监听15s')
# 如果没有持有数据,继续监听
self.window.ROOT.after(15000, self.monitor_hold_tdresult)
elif tdResult == 'Time out':
# 询问是否重试
asktry = messagebox.askretrycancel(
title='实时刷新超时',
message='实时更新持有最新市价数据超时,是否重试?如果取消,将无法实时更新持有最新数据!'
)
if asktry:
# 开启新的更新最新价线程
self.window.td_run_hold()
self.window.ROOT.after(2000, self.monitor_hold_tdresult)
else:
self.window.monitorState_Hold = False
self.window.tdObject_Hold = None # 清空线程
else:
print('线程未结束!')
self.window.ROOT.after(1000, self.monitor_hold_tdresult)
else:
print('当前无线程')
# 开启新的更新最新价线程
self.window.td_run_hold()
self.window.ROOT.after(2000, self.monitor_hold_tdresult)
else:
messagebox.showerror(title='时间错误', message='计算时间错误!!!')
# 停用监听
self.window.monitorState_Hold = 'CalcTime error'
else:
print('当前不在持有页面!')
self.window.monitorState_Hold = False
self.window.tdObject_Hold = None # 清空线程
class HoldHistoryFace(Tabel):
def __init__(self, window):
Tabel.__init__(self, window)
# --No.1--初始化排序----------
self.Mode = 2 # 持有历史
self.SortColumn = 'UpAndDownRange'
self.SortMode = 'desc'
# --No.2--初始化统计----------
self.TotalText = ('历史总成本', '卖出总市值', '历史总收益', '历史总收益率')
# --No.3--初始化表头----------
self.TheadText = {
'名称/代码': {'param': ('Name', 'Code'), 'width': 0.22},
'加入价格/时间': {'param': ('BuyPrice', 'BuyDate'), 'width': 0.23},
'卖出价格/时间': {'param': ('SalePrice', 'SaleDate'), 'width': 0.23},
'涨跌': {'param': ('UpAndDownPrice',), 'width': 0.16},
'涨跌幅': {'param': ('UpAndDownRange',), 'width': 0.16},
} # param不会空表示,该表头可排序。内容即,sort需要的参数。使用关键字参数
# 绘制页面
self.refresh()
def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
"""
重写父类,方法。绘制一行数据
:param rowsindex:
:param holdlist:
:param btn3state:
:param place:
:return:
"""
aline = self.Tbody_DataLIST[self.NowPageNum][rowsindex]
# 通用属性
attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_TBDATA1,
}
# 绘制行框架
cntl_dict = self.draw_frame_aline(place=place)
# 遍历绘制单元格数据
for key, cntl in cntl_dict.items():
if key in ('涨跌', '涨跌幅'):
# 根据涨跌设置字体颜色
fg = COLOR_THEME
if aline[7] > 0:
fg = COLOR_RISE
elif aline[7] < 0:
fg = COLOR_FALL
# 判断是哪一列
if key == '涨跌':
text = round(aline[7], 2)
elif key == '涨跌幅':
text = str(round(aline[8], 2)) + '%'
else:
text = 'ERROR'
label_attrs = attrs.copy()
label_attrs['text'] = text
label_attrs['fg'] = fg
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_attrs
).pack(expand='yes', fill='both')
elif key == '名称/代码':
# 一行存在两个数据
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[1]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[2]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '加入价格/时间':
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[3]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[4]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '卖出价格/时间':
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[5]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[6]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '操作':
# 操作按钮居中框架
# --master cntl_dict[key]['CNTL'] 单元格框架
frame_optionbtncneter_attrs = FRAME_ATTRS.copy()
frame_optionbtncneter = tk.Frame(
cntl_dict[key]['CNTL'],
frame_optionbtncneter_attrs
)
frame_optionbtncneter.pack(side='top', expand='yes') # 水平垂直居中
# 详情按钮
# --master frame_optionbtncneter 单元格居中框架
button_details_attrs = BUTTON_ATTRS.copy()
button_details_attrs['text'] = '卖出'
button_details = tk.Button(
frame_optionbtncneter,
button_details_attrs
)
button_details.pack(side='left', padx=5)
# 绑定详情事件
button_details.configure(
command=lambda
oder=aline[0], # 库中id
: self.event_business(oder)
)
class ChoiceResult(Tabel):
def __init__(self, window, sortcol='bcynum', sortmode='desc', nowpagenum=0):
Tabel.__init__(self, window)
# --初始化排序
self.SortColumn = sortcol
self.SortMode = sortmode
# --初始化页数
self.NowPageNum = nowpagenum
# --初始化本页面需要的图片
self.window.tkImage['ChoiceResult'] = []
# --页面都用到的图片
self.window.NeedImgPath = [
'skin/Choices/AnalysisStatus/thumb.png',
]
self.window.imginit('ChoiceResult') # 初始化所需通用图片
# --初始化表头----------
self.TheadText = {
'序号': {'param': None, 'width': 0.08},
'名称/代码': {'param': None, 'width': 0.18},
'被持数': {'param': ('bcynum',), 'width': 0.1},
'被持股数(万股)': {'param': None, 'width': 0.13},
'评级': {'param': ('pj',), 'width': 0.2},
'操作': {'param': None, 'width': 0.31},
} # param不会空表示,该表头可排序。内容即,sort需要的参数。使用关键字参数
# 绘制页面
self.refresh()
def read_tbdata(self):
"""
重写数据处理函数
:return:
"""
# 如果分析结果为空
if not self.window.tdResult_Choice['data']:
self.Tbody_DataLIST = None
else:
if isinstance(self.window.tdResult_Choice['data'], str):
self.Tbody_DataLIST = self.window.tdResult_Choice['data']
else:
# 排序
if self.SortMode == 'desc':
reverse = True
elif self.SortMode == 'asc':
reverse = False
else:
print(f'排序方式 参数错误!')
return None
if self.SortColumn == 'pj':
# 按照评级排序
data = sorted(self.window.tdResult_Choice['data'].items(), key=lambda kv: (kv[1]['recommendIndex'], kv[0]),
reverse=reverse)
elif self.SortColumn == 'bcynum':
# 按照被持有数排序
data = sorted(self.window.tdResult_Choice['data'].items(),
key=lambda kv: (kv[1]['recomStockInfo'][2], kv[0]),
reverse=reverse)
else:
print(f'排序列 参数错误!')
return None
print(f'排序结果:{data}')
# 处理数据
oder = 1
temp_Tbody_DataLIST = []
for v in data:
temp_Tbody_DataLIST.append(
(
oder, # 序号
v[1]['recomStockInfo'][0], # 股票名称
v[1]['recomStockInfo'][1], # 股票代码
v[1]['recomStockInfo'][2], # 被持数
v[1]['recomStockInfo'][3], # 被持股数(万股)
v[1]['recommendIndex'], # 评级
v[1]['beHoldJJ'] # 被持基金信息
)
) # 一行数据
oder += 1 # 序号自增
# 将数据处理成分页数据
self.Tbody_DataLIST = handle_paging(temp_Tbody_DataLIST, self.PageNum)
self.LENDataLIST = 0 # 重置总数
# 统计数据
if self.Tbody_DataLIST:
for page in self.Tbody_DataLIST:
self.LENDataLIST += len(page) # 总数据条目
self.PageTotalNum = len(self.Tbody_DataLIST) # 总页数
def draw_tname(self):
"""
:return:
"""
# --绘制基本信息--------------------------------
# 返回按钮框架属性
frame_back_attrs = FRAME_ATTRS.copy()
# frame_back_attrs['bg'] = 'yellow'
frame_back_place = {
'width': SIZE_TBNAME_W * 0.15,
'height': SIZE_TBNAME_H,
}
# 表格标题框架属性
frame_tbtitle_attrs = FRAME_ATTRS.copy()
# frame_tbtitle_attrs['bg'] = 'pink'
frame_tbtitle_place = {
'width': SIZE_TBNAME_W - frame_back_place['width'],
'height': SIZE_TBNAME_H,
}
# 表格标题框架
# --master self.CNTL_Tabel['name'] 表格名称框架
frame_tbtitle = tk.Frame(
self.CNTL_Tabel['name'],
frame_tbtitle_attrs
)
frame_tbtitle.place(frame_tbtitle_place)
# --通用属性--
label_currency_attrs = {
'bg': COLOR_BACKGROUND,
}
# 风险提示
# --master frame_tbtitle 表格标题框架
label_warning_attrs = label_currency_attrs.copy()
label_warning_attrs['anchor'] = 'w',
label_warning_attrs['fg'] = COLOR_TIPS_LOW
label_warning_attrs['font'] = FONT_WARNING
label_warning_attrs['text'] = '以下数据仅供参考,不构成任何投资建议。股市有风险,入市须谨慎!'
label_warning = tk.Label(
frame_tbtitle,
label_warning_attrs
)
label_warning.pack(side='bottom', fill='x')
# 表格标题
# --master frame_tbtitle 表格标题框架
label_title_attrs = label_currency_attrs.copy()
label_title_attrs['anchor'] = 'w'
label_title_attrs['fg'] = COLOR_TIPS_TALL
label_title_attrs['font'] = FONT_TBNAME
label_title_attrs['text'] = self.window.tdResult_Choice['TabelName']
# 绘制结果信息头
label_title = tk.Label(
frame_tbtitle,
label_title_attrs
)
label_title.pack(side='bottom', fill='x')
# 返回按钮框架
# --master self.CNTL_Tabel['name'] 表格名称框架
frame_back_place['x'] = frame_tbtitle_place['width']
frame_back = tk.Frame(
self.CNTL_Tabel['name'],
frame_back_attrs
)
frame_back.place(frame_back_place)
# 返回按钮
# --master frame_back 返回按钮框架
button_back_attrs = BUTTON_ATTRS.copy()
button_back_attrs['bd'] = 0
button_back_attrs['font'] += ' underline'
button_back_attrs['text'] = '返回'
button_back = tk.Button(
frame_back,
button_back_attrs
)
button_back.pack(side='top', expand='yes')
button_back.configure(command=lambda: self.event_back())
def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
"""
:param rowsindex:
:param holdlist:
:param btn3state:
:param place:
:return:
"""
aline = self.Tbody_DataLIST[self.NowPageNum][rowsindex]
# 通用属性
attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_THEME,
'font': FONT_TBDATA1,
}
# 绘制行框架
cntl_dict = self.draw_frame_aline(place=place)
# 遍历绘制单元格数据
for key, cntl in cntl_dict.items():
if key in ('序号', '被持数', '被持股数(万股)'):
# 判断是哪一列
if key == '序号':
text = aline[0]
elif key == '被持数':
text = aline[3]
elif key == '被持股数(万股)':
text = '%.2f' % aline[4]
else:
text = 'ERROR'
label_attrs = attrs.copy()
label_attrs['text'] = text
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_attrs
).pack(expand='yes', fill='both')
elif key == '名称/代码':
# 一行存在两个数据
# --top
label_top_attrs = attrs.copy()
label_top_attrs['text'] = aline[1]
label_top_attrs['anchor'] = 's'
label_top_place = {
'width': cntl_dict[key]['place']['width'],
'height': cntl_dict[key]['place']['height'] / 2,
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_top_attrs
).place(label_top_place)
# --bottom
label_bottom_attrs = attrs.copy()
label_bottom_attrs['fg'] = COLOR_TIPS_TALL
label_bottom_attrs['font'] = FONT_TBDATA2
label_bottom_attrs['text'] = aline[2]
label_bottom_attrs['anchor'] = 'n'
label_bottom_place = {
'width': label_top_place['width'],
'height': label_top_place['height'],
'y': label_top_place['height']
}
# 绘制控件
tk.Label(
cntl_dict[key]['CNTL'],
label_bottom_attrs
).place(label_bottom_place)
elif key == '评级':
# 评级原理:底图30*30图片*3.利用遮挡达到评级的效果
# 最低显示一个,最高显示全部
canvas_grade_attrs = CANVAS_ATTRS.copy()
# canvas_grade_attrs['bg'] = 'red'
canvas_grade_attrs['width'] = 30 * 3 # 3张30*30的评级图片
canvas_grade_attrs['height'] = 30
canvas_grade = tk.Canvas(
cntl_dict[key]['CNTL'],
canvas_grade_attrs
)
canvas_grade.pack(side='top', expand='yes')
# 绘制图片
canvas_grade.create_image(45, 15, image=self.window.tkImage['ChoiceResult'][0])
# 根据推荐指数,确定遮挡部分大小
# 如 推荐指数:0.5. width = 60 * 0.5
# 判断评级是否超过 1
if aline[5] > 1.0000000:
print(f'ERROR: recommendIndex > 1')
time.sleep(10)
exit()
# --master canvas_grade 挡板
frame_baffle_attrst = FRAME_ATTRS.copy()
frame_baffle_place = {
'width': 60 - 60 * aline[5],
'height': 30,
'x': 30,
}
frame_baffle_place['x'] += 60 - frame_baffle_place['width']
frame_baffle = tk.Frame(
canvas_grade,
frame_baffle_attrst
)
frame_baffle.place(frame_baffle_place)
elif key == '操作':
# 操作按钮居中框架
# --master cntl_dict[key]['CNTL'] 单元格框架
frame_optionbtncneter_attrs = FRAME_ATTRS.copy()
frame_optionbtncneter = tk.Frame(
cntl_dict[key]['CNTL'],
frame_optionbtncneter_attrs
)
frame_optionbtncneter.pack(side='top', expand='yes') # 水平垂直居中
# 详情按钮
# --master frame_optionbtncneter 单元格居中框架
button_details_attrs = BUTTON_ATTRS.copy()
button_details_attrs['text'] = '详情'
button_details = tk.Button(
frame_optionbtncneter,
button_details_attrs
)
button_details.pack(side='left', padx=5)
# 绑定详情事件
button_details.configure(
command=lambda
pre1=self.window,
pre2=aline[2], # 股票代码
pre3=self.SortColumn,
pre4=self.SortMode,
pre5=self.NowPageNum,
: StockInfo(
pre1, pre2, pre3, pre4, pre5
)
)
# 加持有按钮
# --master frame_optionbtncneter 单元格居中框架
button_details_attrs = BUTTON_ATTRS.copy()
button_details = tk.Button(
frame_optionbtncneter,
button_details_attrs
)
button_details.pack(side='left', padx=5)
# 判断是否已持有
if not aline[2] in holdlist:
button_details['text'] = '加持有'
# 绑定加持有事件
button_details.configure(command=lambda par=aline[2]: self.event_business(par))
else:
button_details['text'] = '已持有'
button_details['state'] = 'disable' # 禁用按钮
# 不绑定事件
# 展开按钮
# --master frame_optionbtncneter 单元格居中框架
button_details_attrs = BUTTON_ATTRS.copy()
button_details = tk.Button(
frame_optionbtncneter,
button_details_attrs
)
button_details.pack(side='left', padx=5)
# 绑定展开按钮事件
if not btn3state:
button_details['text'] = '展开'
button_details.configure(command=lambda pre1=rowsindex: self.draw_heldjjinfo(pre1))
else:
button_details['text'] = '收起'
# 返回
button_details.configure(command=lambda: self.refresh())
def draw_heldjjinfo(self, index: int):
"""
绘制股票被持有基金信息
:param index: 被持有数据的下标
:return:
"""
# 禁用表头所有按钮
self.disabel_theadbtn()
aline = self.Tbody_DataLIST[self.NowPageNum][index]
print(f'绘制被持有 {aline}')
# 清空表身框架
self.destroy_tbody()
# 清空分页框架
self.destroy_tpage()
# 读取已持有列表
holdlist = Ldb.read_holdlist()
# 股票信息
aline_place = {
'width': SIZE_TLINE_W,
'height': SIZE_TLINE_H
}
self.draw_aline(index, holdlist, btn3state=True, place=aline_place)
# 被持有框架
# --master self.CNTL_Tabel['tbody'] # 表身框架
frame_heldjj_attrs = FRAME_ATTRS.copy()
# frame_heldjj_attrs['bg'] = 'red'
frame_heldjj_palce = {
'width': SIZE_TBODY_W - SIZE_TBHELDJJ_MARGIN * 2,
'height': SIZE_TBODY_H - SIZE_TLINE_H,
'y': SIZE_TLINE_H,
'x': SIZE_TBHELDJJ_MARGIN
}
frame_heldjj = tk.Frame(
self.CNTL_Tabel['tbody'],
frame_heldjj_attrs
)
frame_heldjj.place(frame_heldjj_palce)
# 被持基金基本属性
alienheldjj_attrs = CANVAS_ATTRS.copy()
# alienheldjj_attrs['bg'] = 'pink'
alienheldjj_attrs['width'] = frame_heldjj_palce['width']
alienheldjj_attrs['height'] = SIZE_HELDJJLINE_H # 被持有基金信息框架的高度
# 被持有基金信息列表
datalist_totalnum = len(aline[6])
# 判断是否需要滚动条
if datalist_totalnum * SIZE_HELDJJLINE_H > frame_heldjj_palce['height']:
# 需要滚动条
mysf = MyScrollFrame(frame_heldjj)
mysf.pack(side="top", fill="both", expand=True) # 打包滚动条
cntl_frame_heldjj = mysf.get_container() # 返回可滚动视图,即表身数据容器
else:
# 不需要滚动条
cntl_frame_heldjj = frame_heldjj
# 单元格基本属性
label_cell_attrs = {
'bg': COLOR_BACKGROUND,
'fg': COLOR_TIPS_TALL,
'font': FONT_HELDJJINFO
}
label_cell_place = {
'height': alienheldjj_attrs['height'] - SIZE_TBLINE_WIDTH
}
# 遍历绘制被持基金信息
for jjinfos in aline[6]:
# 被持有基金一行框架
# --master frame_heldjj 被持有框架
frame_alineheldjj = tk.Canvas(
cntl_frame_heldjj,
alienheldjj_attrs
)
frame_alineheldjj.pack()
# 绘制表格线条
draw_tb_line(frame_alineheldjj, alienheldjj_attrs, tt=(0, 1))
# 单元格宽度平分一行框架
width = alienheldjj_attrs['width'] / len(jjinfos)
mul = 0
for jjinfo in jjinfos:
# 单元格
# --master frame_alineheldjj 一行框架
if mul == 1:
# 给持有股份加上单位
label_cell_attrs['text'] = jjinfo + "(万股)"
else:
label_cell_attrs['text'] = jjinfo
label_cell_place['width'] = width
label_cell_place['x'] = mul * width
label_cell = tk.Label(
frame_alineheldjj,
label_cell_attrs
)
label_cell.place(label_cell_place)
mul += 1
def event_back(self):
"""
返回按钮事件,返回到选股页面
:return:
"""
asktry = messagebox.askokcancel(title='确认', message='如果返回,本次结果将被清空!是否确定返回?')
if asktry:
# 清空线程结果
self.window.tdObject_Choice = None
self.window.tdResult_Choice = None
# 重新绘制选股页面
ChoiceFace(self.window)
if __name__ == "__main__":
run = WINDOW()
run.ROOT.mainloop()
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/first-cg/obtain-fund-positions.git
[email protected]:first-cg/obtain-fund-positions.git
first-cg
obtain-fund-positions
获取基金持仓
master

搜索帮助