PyQt6实例_pyqtgraph散点图显示工具_代码分享

目录

描述:

效果:

代码: 

返回结果对象

字符型横坐标

通用散点图工具

工具主界面

使用举例


描述:

1 本例结合实际应用场景描述散点图的使用。在财报分析中,需要将数值放在同行业中进行比较,从而判断是否异常。

2 散点图显示部分可以当通用工具使用。

3 数据计算使用compile动态执行代码,返回固定格式的数据进行显示,尽最大可能实现工具的灵活性。

效果:

代码: 

返回结果对象

@dataclass
class MultiScatterObj:title:str=''df:pd.DataFrame=pd.DataFrame()col_dict: dict = field(default_factory=dict)type_dict: dict = field(default_factory=dict)focus_target: str = ''error_msg:str = ''status:str='ok'pass

1 title 标题

2 type_dict 行业分类标准

4 col_dict 计算的指标

5 focus_target 要关注的股票

字符型横坐标

class StrAxisItem(pg.AxisItem):def __init__(self,ticks,*args,**kwargs):pg.AxisItem.__init__(self,*args,**kwargs)self.x_values = [x[0] for x in ticks]self.x_strings = [x[1] for x in ticks]passdef tickStrings(self, values, scale, spacing):strings = []for v in values:vs = v*scaleif vs in self.x_values:vstr = self.x_strings[self.x_values.index(vs)]else:vstr = ''strings.append(vstr)return strings

通用散点图工具

class ScatterGraphWidget(pg.PlotWidget):def __init__(self):super().__init__()self.init_data()passdef init_data(self):self.whole_df = Noneself.cur_targetItem = Noneself.focus_targetItem = Noneself.color_point = (255, 255, 0)self.color_star = (220, 20, 60)self.color_focus = (255,140,0)self.color_mean = (255, 0, 255)self.color_median = (34, 139, 34)with open(DATA_DIR+'ticker_name.json','r',encoding='utf-8') as f:self.ticker_name_dict = json.load(f)passdef set_data(self, df: pd.DataFrame,focus_ticker:str=None):self.clear()if df.empty:returnself.whole_df = dfself.xTicks = self.whole_df.loc[:, ['x', 'ticker']].valuesself.x = self.whole_df['x'].to_list()self.y = self.whole_df['target'].to_list()horAxis = StrAxisItem(ticks=self.xTicks, orientation='bottom')self.setAxisItems({'bottom': horAxis})scatters = pg.ScatterPlotItem(hoverable=True,hoverPen=pg.mkPen('g'),tip=None)spots = []for x0, y0 in zip(self.x, self.y):spots.append({'pos': (x0, y0),'size': 10,'pen': {'color': self.color_point, 'width': 2},'brush': pg.mkBrush(color=self.color_point)})scatters.addPoints(spots)self.addItem(scatters)self.label = pg.TextItem()self.addItem(self.label, ignoreBounds=True)if focus_ticker:if self.focus_targetItem:self.removeItem(self.focus_targetItem)df00 = df.loc[df['ticker']==focus_ticker]if not df00.empty:index_x = df00.iloc[0]['x']self.focus_targetItem = pg.TargetItem(pos=(index_x, self.y[index_x]),movable=False,size=20,symbol='t1',pen=self.color_focus,brush=self.color_focus)self.addItem(self.focus_targetItem)# 添加中位数线 和 平均数线mean_line = pg.InfiniteLine(pos=(0,self.whole_df['target'].mean()),movable=False,angle=0,pen=pg.mkPen({'color':self.color_mean,'width':2}),label=f'{self.whole_df["target"].mean():,} 平均数',labelOpts={'position':0.05,'color': (255, 255, 255), 'movable': True, 'fill': (self.color_mean[0], self.color_mean[1], self.color_mean[2], 100)})median_line = pg.InfiniteLine(pos=(0,self.whole_df['target'].median()),movable=False,angle=0,pen=pg.mkPen({'color':self.color_median,'width':2}),label=f'{self.whole_df["target"].median():,} 中位数',labelOpts={'position':0.05,'color': (255, 255, 255), 'movable': True, 'fill': (self.color_median[0], self.color_median[1], self.color_median[2], 100)})self.addItem(mean_line)self.addItem(median_line)scatters.sigClicked.connect(self.scatters_sigClicked)scatters.sigHovered.connect(self.scatters_sigHovered)self.enableAutoRange()passdef set_content_empty(self):self.clear()passdef set_targetItem(self,ticker:str):df = self.whole_df.copy()df = df.loc[df['ticker'] == ticker]if df.empty:returnindex_x = df.iloc[0]['x']if self.cur_targetItem:self.removeItem(self.cur_targetItem)self.cur_targetItem = pg.TargetItem(pos=(index_x, self.y[index_x]),movable=False,size=20,symbol='star',pen=self.color_star,brush=self.color_star)self.addItem(self.cur_targetItem)passdef scatters_sigClicked(self, plot, points):# 将单击的股票代码发送给左侧图if len(points) <= 0:returnindex_x = points[0].index()if self.cur_targetItem:self.removeItem(self.cur_targetItem)self.cur_targetItem = pg.TargetItem(pos=(index_x, self.y[index_x]),movable=False,size=20,symbol='star',pen=self.color_star,brush=self.color_star)self.addItem(self.cur_targetItem)passdef scatters_sigHovered(self, plot, points):if len(points) <= 0:returnindex_x = points[0].index()x_str = self.xTicks[index_x][1]y_val = self.y[index_x]x_str00 = self.ticker_name_dict.get(x_str,x_str)html_str = '<p style="color:white;font-size:18px;font-weight:bold;">' + x_str00 + '&nbsp;' + f'{y_val:,}' + '</p>'self.label.setHtml(html_str)self.label.setPos(points[0].pos())passdef wheelEvent(self, ev):if len(self.whole_df) <= 0:super().wheelEvent(ev)else:delta = ev.angleDelta().x()if delta == 0:delta = ev.angleDelta().y()s = 1.001 ** deltabefore_xmin, before_xmax = self.viewRange()[0]val_x = self.getViewBox().mapSceneToView(ev.position()).x()after_xmin = int(val_x - (val_x - before_xmin) // s)after_xmax = int(val_x + (before_xmax - val_x) // s)if after_xmin < 1:after_xmin = 0if after_xmin >= len(self.whole_df):after_xmin = max(len(self.whole_df) - 3, len(self.whole_df) - 1)if after_xmax < 1:after_xmax = min(len(self.whole_df) - 1, 1)if after_xmax >= len(self.whole_df):after_xmax = len(self.whole_df) - 1# print(after_xmin,after_xmax)df00 = self.whole_df.loc[(self.whole_df['x'] >= after_xmin) & (self.whole_df['x'] <= after_xmax)].copy()after_ymin = df00['target'].min()after_ymax = df00['target'].max()self.setXRange(after_xmin, after_xmax)self.setYRange(after_ymin, after_ymax)passpass

1)本例中散点图增加了一些与实际业务相关的数据

2)set_data方法需要带入df、focus_ticker(可为空)

2.1)df必须要有x、ticker两个字段,x为递增整数,ticker为横坐标要显示的字符

2.2)df中target字段为y轴数值

工具主界面

class PyExcuteGraphShowWidgetII(QWidget):def __init__(self):super().__init__()self.setWindowTitle('py文件执行并显示结果(散点图)')self.setMinimumSize(QSize(1000,800))label00 = QLabel('选择py文件:')self.lineedit_file = QLineEdit()btn_choice = QPushButton('选择文件',clicked=self.btn_choice_clicked)self.btn_excute = QPushButton('执行',clicked=self.btn_excute_clicked)btn_download = QPushButton('下载数据',clicked=self.btn_download_clicked)self.label_title = QLabel('指标', alignment=Qt.AlignmentFlag.AlignHCenter)self.label_title.setStyleSheet("font-size:28px;color:#CC2EFA;")label01 = QLabel('行业标准')self.combo_type = QComboBox()self.combo_type.currentIndexChanged.connect(self.combo_type_currentIndexChanged)self.label_industry = QLabel('所属行业')self.label_industry.setStyleSheet("font-size:20px;color:#CC2EFA;")label02 = QLabel('日期')self.combo_date = QComboBox()self.combo_date.currentIndexChanged.connect(self.combo_date_currentIndexChanged)self.label_date = QLabel('日期')self.label_date.setStyleSheet("font-size:20px;color:#CC2EFA;")self.radioButtonGroup = QButtonGroup()self.radioButtonGroup.buttonClicked.connect(self.radioButtonGroup_buttonClicked)self.layout_radio = QHBoxLayout()groupBox = QGroupBox('指标')groupBox.setLayout(self.layout_radio)self.pw = ScatterGraphWidget()self.label_num = QLabel('几个')self.table = QTableWidget()self.table.setColumnCount(2)self.table.setHorizontalHeaderLabels(['代码','名'])self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)self.table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)self.table.itemClicked.connect(self.table_itemClicked)layout00 = QHBoxLayout()layout00.addWidget(label00)layout00.addWidget(self.lineedit_file)layout00.addWidget(btn_choice)layout00.addWidget(self.btn_excute)layout00.addWidget(btn_download)layout01 = QVBoxLayout()layout01.addWidget(label01)layout01.addWidget(self.combo_type)layout01.addWidget(self.label_industry)layout02 = QVBoxLayout()layout02.addWidget(label02)layout02.addWidget(self.combo_date)layout02.addWidget(self.label_date)layout03 = QHBoxLayout()layout03.addLayout(layout01)layout03.addLayout(layout02)layout04 = QVBoxLayout()layout04.addLayout(layout03)layout04.addWidget(groupBox)layout04.addWidget(self.pw)layout05 = QVBoxLayout()layout05.addWidget(self.label_num)layout05.addWidget(self.table)layout06 = QHBoxLayout()layout06.addLayout(layout04,5)layout06.addLayout(layout05,1)layout = QVBoxLayout()layout.addLayout(layout00)layout.addWidget(self.label_title)layout.addLayout(layout06)self.setLayout(layout)self.open_init()passdef open_init(self):self.whole_resObj:MultiScatterObj = Noneself.whole_current_df:pd.DataFrame = pd.DataFrame()self.whole_current_show_df:pd.DataFrame = pd.DataFrame()with open(DATA_DIR+'industry_type.json','r',encoding='utf-8') as f:self.industry_type = json.load(f)with open(DATA_DIR+'ticker_name.json','r',encoding='utf-8') as f:self.ticker_name_dict = json.load(f)self.radio_list = []self.whole_current_ticker_list = []passdef btn_choice_clicked(self):file_path,_ = QFileDialog.getOpenFileName(self,'选择文件')if file_path:self.lineedit_file.setText(file_path)passdef btn_excute_clicked(self):file_path = self.lineedit_file.text()if len(file_path) <= 0:QMessageBox.information(self,'提示','请选择要执行的py文件',QMessageBox.StandardButton.Ok)returnwith open(file_path,'r',encoding='utf-8') as fr:py_code = fr.read()namespace = {}fun_code = compile(py_code, '<string>', 'exec')exec(fun_code, namespace)res = namespace['execute_caculate']()if res.status == 'error':QMessageBox.information(self,'执行过程报错',res.error_msg,QMessageBox.StandardButton.Ok)returnself.label_title.setText(res.title)self.whole_resObj = resself.reset_content()QMessageBox.information(self,'提示','执行完毕',QMessageBox.StandardButton.Ok)passdef btn_download_clicked(self):if self.whole_resObj is None or self.whole_resObj.status == 'error':QMessageBox.information(self,'提示','数据为空',QMessageBox.StandardButton.Ok)returndir_name = QFileDialog.getExistingDirectory(self,'选择保存位置')if dir_name:df = self.whole_resObj.df.copy()df.rename(columns=self.whole_resObj.col_dict,inplace=True)df.to_csv(dir_name+os.path.sep + self.whole_resObj.title +'.csv',encoding='utf-8',index=False)QMessageBox.information(self,'提示','下载完毕',QMessageBox.StandardButton.Ok)passdef combo_type_currentIndexChanged(self,cur_i:int):cur_type = self.combo_type.currentText()self.label_industry.setText(';'.join(self.whole_resObj.type_dict[cur_type].keys()))df = self.whole_resObj.df.copy()key0 = list(self.whole_resObj.type_dict[cur_type].keys())[0]ticker_list = self.whole_resObj.type_dict[cur_type][key0]self.whole_current_df = df.loc[df['ticker'].isin(ticker_list)].copy()self.label_num.setText(f'共{len(ticker_list)}个')self.table.setRowCount(len(ticker_list))for i, item in enumerate(ticker_list):item_name = self.ticker_name_dict.get(item, item)self.table.setItem(i, 0, QTableWidgetItem(str(item)))self.table.setItem(i, 1, QTableWidgetItem(item_name))passself.table.resizeColumnsToContents()self.whole_current_ticker_list = ticker_listdate_list = list(set(self.whole_current_df['reportDate'].to_list()))date_list.sort()date_list.reverse()self.combo_date.addItems(date_list)passdef combo_date_currentIndexChanged(self,cur_i:int):cur_date = self.combo_date.currentText()self.label_date.setText(cur_date)a0 = self.radioButtonGroup.button(2)a0.setChecked(True)self.radioButtonGroup_buttonClicked(a0)passdef radioButtonGroup_buttonClicked(self,a0):indicator_name = a0.text()target_name = ''for k,v in self.whole_resObj.col_dict.items():if indicator_name == v:target_name = kbreakdf = self.whole_resObj.df.copy()df = df.loc[(df['ticker'].isin(self.whole_current_ticker_list)) & (df['reportDate']==self.combo_date.currentText())]df.rename(columns={target_name:'target'},inplace=True)df['x'] = range(len(df))self.whole_current_show_df = df.copy()self.pw.set_data(df.copy(),self.whole_resObj.focus_target)passdef table_itemClicked(self,cur_item):cur_row = cur_item.row()ticker = self.table.item(cur_row,0).text()df = self.whole_current_show_df.loc[self.whole_current_show_df['ticker']==ticker]if df.empty:QMessageBox.information(self,'提示',f'{ticker},在该日期没有数据',QMessageBox.StandardButton.Ok)returnself.pw.set_targetItem(ticker)passdef reset_content(self):for item in self.radio_list:self.layout_radio.removeWidget(item)self.radioButtonGroup.removeButton(item)self.radio_list.clear()i = 2for item in self.whole_resObj.col_dict.values():radio = QRadioButton(item)self.layout_radio.addWidget(radio)self.radioButtonGroup.addButton(radio,i)self.radio_list.append(radio)i += 1self.pw.set_content_empty()self.combo_type.clear()self.combo_date.clear()self.table.clearContents()self.label_num.setText('--')self.combo_type.addItems(list(self.whole_resObj.type_dict.keys()))self.label_title.setText(self.whole_resObj.title)passpass

使用举例

需要导入的包和运行代码

import os,sys,json
import pandas as pd
import numpy as np
from PyQt6.QtCore import (
QSize,
Qt
)
from PyQt6.QtWidgets import (QApplication, QButtonGroup, QRadioButton,QMainWindow, QAbstractItemView,QLabel,QPushButton,QComboBox,QTableWidget,QTableWidgetItem,QTextEdit,QWidget,QVBoxLayout,QHBoxLayout, QGridLayout,QFileDialog,QInputDialog,QMessageBox,QLineEdit,QGroupBox, QScrollArea, QCompleter
)
import pyqtgraph as pg
from objects import MultiScatterObj
from settings import DATA_DIRif __name__ == '__main__':app = QApplication(sys.argv)mw = PyExcuteGraphShowWidgetII()mw.show()app.exec()pass

1)一个py文件例子,内容如下,方法名固定为 excute_caculate

def execute_caculate():import traceback,json,osimport pandas as pdfrom utils import postgresql_utilsfrom objects import MultiScatterObjfrom settings import DATA_DIR,INDUSTRY_DIR'''灵活py文件执行营业利润,营业外支出,营业外收入'''conn = postgresql_utils.connect_db()cur = conn.cursor()try:ticker = '000638'file_list = os.listdir(INDUSTRY_DIR)type_dict = {}ticker_list = []for file_one in file_list:type_code = file_one[0:6]file_path = os.path.join(INDUSTRY_DIR, file_one)with open(file_path, 'r',encoding='utf-8') as f:j_obj = json.load(f)# type_dict[type_code] = {}for k,v in j_obj.items():if ticker in v:if type_dict.get(type_code,None) is None:type_dict[type_code] = {}type_dict[type_code][k] = vticker_list.extend(v)passpassticker_list = list(set(ticker_list))ticker_list_str = '\',\''.join(ticker_list)ticker_list_str = '\''+ticker_list_str+'\''sql_str = f'''select ticker,reportDate,iii_operateProfit,add_nonoperateIncome,less_nonoperateExpenses from t_profit where ticker in ({ticker_list_str}) and reportDate like \'%-12-31\';'''cur.execute(sql_str)res = cur.fetchall()col_list = ['ticker','reportDate','a0','a1','a2']col_dict = {'a0':'营业利润','a1':'营业外收入','a2':'营业外支出'}df = pd.DataFrame(columns=col_list, data=res)res_obj = MultiScatterObj(title=f'{ticker},营业利润、营业外收入、营业外支出',df=df,col_dict=col_dict,type_dict=type_dict,focus_target=ticker,status='ok')return res_objexcept:res_obj = MultiScatterObj(status='error',error_msg=traceback.format_exc())return res_objfinally:cur.close()conn.close()passpass

保存为test003.py

注意:例子中涉及到的postgreSQL和财报数据在往期博文中可以找到

2)点击“选择文件”,选择 test002.py文件

3)点击“执行”,选择行业、日期、指标,就能显示散点图

4)右侧股票列表,单击某个股票,就会在散点图中用红星标注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/79002.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

纯C协程框架NtyCo

原文是由写的&#xff0c;写的真的很好&#xff0c;原文链接&#xff1a;纯c协程框架NtyCo实现与原理-CSDN博客 1.为什么会有协程&#xff0c;协程解决了什么问题&#xff1f; 网络IO优化 在CS&#xff0c;BS的开发模式下&#xff0c;服务器的吞吐量是一个受关注的参数&#x…

信息系统项目管理师——第10章 项目进度管理 笔记

10项目进度管理 1.规划进度管理&#xff1a;项目章程、项目管理计划&#xff08;开发方法、范围管理计划&#xff09;、事业环境因素、组织过程资产——专家判断、数据分析&#xff08;备选方案分析&#xff09;、会议——进度管理计划 2.定义活动&#xff1a;WBS进一步分解&am…

通过门店销售明细表用SQL得到每月每个门店的销冠和按月的同比环比数据

假设我在Snowflake里有销售表&#xff0c;包含ID主键、门店ID、日期、销售员姓名和销售额&#xff0c;需要统计出每个月所有门店和各门店销售额最高的人&#xff0c;不一定是一个人&#xff0c;以及他所在的门店ID和月总销售额。 统计每个月份下&#xff0c;各门店内销售额最高…

移远通信LG69T赋能零跑B10:高精度定位护航,共赴汽车智联未来

当前&#xff0c;汽车行业正以前所未有的速度迈向智能化时代&#xff0c;组合辅助驾驶技术已然成为车厂突出重围的关键所在。高精度定位技术作为实现车辆精准感知与高效协同的基石&#xff0c;其重要性日益凸显。 作为全球领先的物联网及车联网整体解决方案供应商&#xff0c;移…

jmeter-Beashell获取http请求体json

在JMeter中&#xff0c;使用BeanShell处理器或BeanShell Sampler来获取HTTP请求体中的JSON数据是很常见的需求。这通常用于在测试计划中处理和修改请求体&#xff0c;或者在响应后进行验证。以下是一些步骤和示例代码&#xff0c;帮助你使用BeanShell来获取HTTP请求体中的JSON数…

若干查找算法

一、顺序查找 1.原理 2.代码 #if 0 const int FindBySeq(const vector<int>& ListSeq, const int KeyData) {int retrIdx -1;int size ListSeq.size();for(int i 0; i < size; i) {if (ListSeq.at(i) KeyData){retrIdx i;break;}}return retrIdx; } #else c…

Uniapp(vue):生命周期

目录 一、Vue生命周期二、Uniapp中页面的生命周期三、执行顺序比较一、Vue生命周期 setup():是在beforeCreate和created之前运行的,所以可以用setup代替这两个钩子函数。onBeforeMount():已经完成了模板的编译,但是组件还未挂载到DOM上的函数。onMounted():组件挂载到DOM完…

Prometheus监控

1、docker - prometheusgrafana监控与集成到spring boot 服务_grafana spring boot-CSDN博客 2、【IT运维】普罗米修斯基本介绍及监控平台部署&#xff08;PrometheusGrafana&#xff09;-CSDN博客 3、Prometheus监控SpringBoot-CSDN博客 4、springboot集成普罗米修斯-CSDN博客…

C#进阶学习(十四)反射的概念以及关键类Type

目录 本文末尾有相关类中的总结&#xff0c;如有需要直接跳到最后即可 前置知识&#xff1a; 1、程序集&#xff08;Assembly&#xff09; 2、元数据&#xff08;Metadata&#xff09; 3、中间语言&#xff08;IL, Intermediate Language&#xff09; 中间语言&#xff08;…

Kotlin中的also、apply、invoke用法详解

以下是 Kotlin 中作用域函数(let、run、with、also、apply)和 invoke 操作符的完整总结,结合代码示例和对比说明,帮助您理解它们的用法和区别。 一、作用域函数:简化对象操作 作用域函数用于在对象的上下文中执行代码块,并根据函数的不同返回对象本身或 lambda 的结果。…

Ubuntu实现远程文件传输

目录 安装 FileZillaUbuntu 配套设置实现文件传输 在Ubuntu系统中&#xff0c;实现远程文件传输的方法有多种&#xff0c;常见的包括使用SSH&#xff08;Secure Shell&#xff09;的SCP&#xff08;Secure Copy Protocol&#xff09;命令、SFTP&#xff08;SSH File Transfer P…

TEC制冷片详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 三、程序设计 main文件 jdq.h文件 jdq.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 半导体制冷片&#xff08;又称热电模块&#xff09;&#xff0c;是利用半导体材料的珀耳帖效应制造的一种新型制冷元件…

DotNet 入门:(一) 环境安装

一、前言 本想用 Go 语言实现一个通过小爱同学操作电脑的&#xff0c;比如我对着手机说打开音乐&#xff0c;或调小音乐&#xff0c;电脑能做相应的处理。奈何我一时间没看懂&#xff0c;就想着用.Net 来试一下&#xff0c;于是就有了下面这篇文章。 二、安装.Net 环境 1. 下…

人工智能数学基础(四):线性代数

线性代数是人工智能领域的核心数学工具之一&#xff0c;广泛应用于数据表示、模型训练和算法优化等多个环节。本文将系统梳理线性代数的关键知识点&#xff0c;并结合 Python 实例&#xff0c;助力读者轻松掌握这一重要学科。资源绑定附上完整资源供读者参考学习&#xff01; …

Github 2025-04-26 Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-04-26统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero General Public Li…

使用org.java_websocket库第三方库实现广播

可以使用org.java_websocket库来实现WebSocket服务器&#xff0c;并通过broadcast方法实现广播 java实现 import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; import java.net.…

【自然语言处理与大模型】LangChain大模型应用框架入门②

本文介绍LangChain的另一个重要组件——提示词模板&#xff08;Prompt Template&#xff09;组件&#xff0c;它主要用于将用户输入和参数转换为语言模型可理解的指令。有助于引导模型生成符合预期的响应&#xff0c;帮助其更好地理解上下文&#xff0c;从而输出相关且连贯的语…

单例设计模式之懒汉式以及线程安全问题

在单例设计模式中&#xff0c;懒汉式&#xff08;Lazy Initialization&#xff09; 通过延迟实例化来优化资源使用&#xff0c;但在多线程环境下存在线程安全问题。以下是其核心问题及解决方案的详细解析&#xff1a; 一、基础懒汉式代码&#xff08;线程不安全&#xff09; pu…

Windows7升级PowerShell到5.1

window7系统&#xff0c;安装了vs2019后&#xff0c;应用要用到PowerShell&#xff0c;Tool->Commond Line->Developer PowerShell时&#xff0c;提示版本需要3.0以上。还有编译新版本vcpkg&#xff08;2021前的版本&#xff09;&#xff0c;脚本报错。所以需要升级下Pow…

区块链:去中心化应用(DApp)开发全流程解析

一、DApp的核心概念与特点 去中心化应用&#xff08;DApp&#xff09;是一种基于区块链技术的应用程序&#xff0c;其核心逻辑通过智能合约在链上执行&#xff0c;数据存储和交互均不依赖中心化服务器。相比传统应用&#xff0c;DApp具备以下特点&#xff1a; 去中心化&#x…