基于Qt实现的自定义树结构容器:设计与应用
在Qt框架中,尽管其提供了许多强大的容器类(如 QList
, QMap
, QTreeWidget
等),但缺少一个通用的、灵活的树结构容器,直接支持多层级数据管理。为了满足这些需求,本文设计并实现了一个可复用的自定义树结构容器,并讨论其在不同项目中的应用。
1. 背景与动机
树结构在软件开发中是常见的数据组织形式,常用于以下场景:
- 多层级文件管理器:文件夹与文件的树形展示。
- 层次化关系管理:如公司组织结构、任务依赖关系。
- 数据处理与分类:如属性分类、规则树等。
然而,Qt 中缺少直接的树结构容器(QTreeWidget
是 UI 组件,QAbstractItemModel
偏向于数据视图)。因此,我们实现了一个灵活、可扩展的 通用树结构容器,支持:
- 动态添加和删除节点。
- 为节点附加数据。
- 数据筛选与查找。
- 清晰的树形结构打印与调试。
2. 核心设计与实现
2.1 类设计概览
该树容器包含两个核心类:
-
TreeNode:
- 表示树的单个节点。
- 包括节点名称、父节点指针、子节点列表、节点数据。
- 支持节点添加、删除、数据设置与清除等基本操作。
-
Tree:
- 管理整个树的逻辑。
- 提供全局的节点操作接口,如添加、删除节点,筛选节点数据,打印树结构等。
2.2 TreeNode 类实现
TreeNode 是树结构的核心,负责管理节点的层次关系和数据存储。以下是其关键代码逻辑:
class TreeNode {
public:explicit TreeNode(const QString& name, TreeNode* parent = nullptr);~TreeNode();// 添加子节点TreeNode* addChild(const QString& name);// 移除子节点bool removeChild(TreeNode* child);// 设置与清除节点数据void setData(const QVariant& data);void clearData();// 获取节点信息QVariant getData() const;const QList<TreeNode*>& getChildren() const;QString getName() const;TreeNode* getParent() const;
};
主要功能:
addChild
和removeChild
实现树的动态结构调整。setData
和clearData
支持灵活的节点数据管理。- 提供对父子关系和数据的访问接口。
2.3 Tree 类实现
Tree 是一个树容器的管理类。其设计目标是:
- 提供用户友好的接口,隐藏树节点的内部操作。
- 支持全局的增删改查功能。
以下是 Tree
类的部分接口说明:
class Tree {
public:Tree();~Tree();// 节点操作TreeNode* addNode(TreeNode* parent, const QString& name);bool removeNode(TreeNode* node);// 数据操作void setNodeData(TreeNode* node, const QVariant& data);QVariant getNodeData(TreeNode* node) const;void clearNodeData(TreeNode* node);// 数据筛选与树形打印QList<TreeNode*> filterNodes(const QString& keyword) const;void printTree() const;
};
主要功能:
addNode
:动态添加节点,支持将节点默认添加到根节点。filterNodes
:通过关键字查找包含特定数据的节点。printTree
:以层级缩进格式打印树的结构,便于调试。
2.4 调用示例
以下是使用 Tree
和 TreeNode
的示例代码:
int main(int argc, char* argv[]) {QCoreApplication a(argc, argv);// 创建树容器Tree tree;// 添加节点TreeNode* root = tree.addNode(nullptr, tc("根节点"));TreeNode* nodeA = tree.addNode(root, tc("节点A"));TreeNode* nodeB = tree.addNode(root, tc("节点B"));TreeNode* nodeC = tree.addNode(nodeA, tc("节点C"));// 设置节点数据tree.setNodeData(nodeA, tc("温度过高"));tree.setNodeData(nodeB, tc("正常"));tree.setNodeData(nodeC, tc("压力过低"));// 打印树结构tree.printTree();// 筛选包含 "温度" 的节点QList<TreeNode*> filteredNodes = tree.filterNodes(tc("温度"));qDebug() << tc("筛选结果:");for (TreeNode* node : filteredNodes) {qDebug() << node->getName() << ":" << node->getData().toString();}return a.exec();
}
运行结果:
根节点 ()节点A (温度过高)节点C (压力过低)节点B (正常)筛选结果:
"节点A" : "温度过高"
3. 适用场景分析
该树容器的灵活性使其适用于多种场景,包括但不限于以下项目:
-
文件管理器:
- 以层次结构管理文件夹和文件。
- 节点数据可存储文件的元信息(如路径、大小)。
-
组织结构管理:
- 用于显示公司组织架构(如部门、员工)。
- 节点数据可附加员工信息。
-
规则引擎或决策树:
- 用于实现条件匹配规则。
- 节点存储规则条件与结果。
-
动态数据分类:
- 实现类似标签分类的功能。
- 支持实时增删节点。
-
调试工具:
- 用于显示复杂系统中的内部数据关系。
4. 优势与改进方向
4.1 优势
-
简单易用:
- 接口友好,隐藏复杂的内部操作。
- 提供清晰的错误提示和默认行为。
-
高扩展性:
- 可以轻松添加新功能,如节点排序、自定义过滤条件等。
-
灵活性:
- 节点的数据类型为
QVariant
,支持多种数据类型存储。
- 节点的数据类型为
-
跨平台支持:
- 依赖 Qt 框架,具备良好的跨平台能力。
4.2 改进方向
-
线程安全:
- 增加对并发操作的支持,例如通过
QMutex
实现线程同步。
- 增加对并发操作的支持,例如通过
-
持久化:
- 增加树结构的序列化和反序列化功能,用于存储和加载数据。
-
性能优化:
- 对大规模树操作(如深度遍历)进行优化。
-
模型绑定:
- 将树容器与
QAbstractItemModel
绑定,支持直接用于 Qt 的视图类(如QTreeView
)。
- 将树容器与
5. 结语
本文介绍了一个基于 Qt 实现的自定义树结构容器,其功能涵盖了节点管理、数据存储、筛选与打印等操作,适用于多种项目场景。通过该容器,开发者可以更加灵活地管理复杂的层次化数据,同时其清晰的接口设计也便于扩展与维护。
6. 源码
以下是修正后的完整代码实现,包含 TreeNode.h
、TreeNode.cpp
、Tree.h
、Tree.cpp
和 main.cpp
文件。代码修复了根节点初始化问题,并增强了错误处理和默认逻辑。
TreeNode.h
#ifndef TREENODE_H
#define TREENODE_H#include <QString>
#include <QList>
#include <QVariant>#define tc(a) QString::fromLocal8Bit(a)class TreeNode {
public:explicit TreeNode(const QString& name, TreeNode* parent = nullptr);~TreeNode();// 添加子节点TreeNode* addChild(const QString& name);// 移除子节点bool removeChild(TreeNode* child);// 设置节点数据void setData(const QVariant& data);// 获取节点数据QVariant getData() const;// 移除节点数据void clearData();// 获取所有子节点const QList<TreeNode*>& getChildren() const;// 获取节点名称QString getName() const;// 获取父节点TreeNode* getParent() const;// 检查是否为叶子节点bool isLeaf() const;private:QString nodeName; // 节点名称QVariant nodeData; // 节点数据TreeNode* parentNode; // 父节点QList<TreeNode*> childNodes; // 子节点列表
};#endif // TREENODE_H
TreeNode.cpp
#include "TreeNode.h"
#include <QDebug>TreeNode::TreeNode(const QString& name, TreeNode* parent): nodeName(name), parentNode(parent) {}TreeNode::~TreeNode() {qDeleteAll(childNodes); // 删除所有子节点
}TreeNode* TreeNode::addChild(const QString& name) {TreeNode* child = new TreeNode(name, this);childNodes.append(child);return child;
}bool TreeNode::removeChild(TreeNode* child) {if (!child || !childNodes.contains(child)) {qWarning() << tc("移除失败:节点不存在!");return false;}childNodes.removeAll(child);delete child; // 删除子节点及其数据return true;
}void TreeNode::setData(const QVariant& data) {nodeData = data;
}QVariant TreeNode::getData() const {return nodeData;
}void TreeNode::clearData() {nodeData.clear();
}const QList<TreeNode*>& TreeNode::getChildren() const {return childNodes;
}QString TreeNode::getName() const {return nodeName;
}TreeNode* TreeNode::getParent() const {return parentNode;
}bool TreeNode::isLeaf() const {return childNodes.isEmpty();
}
Tree.h
#ifndef TREE_H
#define TREE_H#include "TreeNode.h"class Tree {
public:Tree();~Tree();// 添加节点TreeNode* addNode(TreeNode* parent, const QString& name);// 移除节点bool removeNode(TreeNode* node);// 设置节点数据void setNodeData(TreeNode* node, const QVariant& data);// 获取节点数据QVariant getNodeData(TreeNode* node) const;// 移除节点数据void clearNodeData(TreeNode* node);// 查找节点(通过名称)TreeNode* findNode(TreeNode* root, const QString& name) const;// 过滤节点(通过数据关键字)QList<TreeNode*> filterNodes(const QString& keyword) const;// 打印树结构void printTree() const;private:TreeNode* root;// 辅助递归方法void filterRecursive(TreeNode* node, const QString& keyword, QList<TreeNode*>& result) const;void printRecursive(TreeNode* node, int depth) const;
};#endif // TREE_H
Tree.cpp
#include "Tree.h"
#include <QDebug>Tree::Tree() {root = new TreeNode(tc("根节点"));qDebug() << tc("成功初始化根节点。");
}Tree::~Tree() {delete root; // 自动删除所有节点
}TreeNode* Tree::addNode(TreeNode* parent, const QString& name) {if (!parent) {if (!root) {qWarning() << tc("添加失败:根节点未创建!");return nullptr;}qDebug() << tc("未指定父节点,默认添加到根节点。");parent = root; // 如果父节点为空,默认添加到根节点}return parent->addChild(name);
}bool Tree::removeNode(TreeNode* node) {if (!node || node == root) {qWarning() << tc("移除失败:节点为空或为根节点!");return false;}TreeNode* parent = node->getParent();if (!parent) {qWarning() << tc("移除失败:父节点为空!");return false;}return parent->removeChild(node);
}void Tree::setNodeData(TreeNode* node, const QVariant& data) {if (!node) {qWarning() << tc("设置失败:节点为空!");return;}node->setData(data);
}QVariant Tree::getNodeData(TreeNode* node) const {if (!node) {qWarning() << tc("获取失败:节点为空!");return QVariant();}return node->getData();
}void Tree::clearNodeData(TreeNode* node) {if (!node) {qWarning() << tc("清除失败:节点为空!");return;}node->clearData();
}TreeNode* Tree::findNode(TreeNode* root, const QString& name) const {if (!root) return nullptr;if (root->getName() == name) return root;for (TreeNode* child : root->getChildren()) {TreeNode* found = findNode(child, name);if (found) return found;}return nullptr;
}QList<TreeNode*> Tree::filterNodes(const QString& keyword) const {QList<TreeNode*> result;filterRecursive(root, keyword, result);return result;
}void Tree::filterRecursive(TreeNode* node, const QString& keyword, QList<TreeNode*>& result) const {if (node->getData().toString().contains(keyword)) {result.append(node);}for (TreeNode* child : node->getChildren()) {filterRecursive(child, keyword, result);}
}void Tree::printTree() const {printRecursive(root, 0);
}void Tree::printRecursive(TreeNode* node, int depth) const {qDebug().noquote() << QString(depth * 2, ' ') + node->getName() + " (" + node->getData().toString() + ")";for (TreeNode* child : node->getChildren()) {printRecursive(child, depth + 1);}
}
main.cpp
#include <QCoreApplication>
#include "Tree.h"int main(int argc, char* argv[]) {QCoreApplication a(argc, argv);// 创建树Tree tree;// 创建子节点,明确传入父节点TreeNode* nodeA = tree.addNode(nullptr, tc("节点A")); // 默认添加到根节点TreeNode* nodeB = tree.addNode(nodeA, tc("节点B"));TreeNode* nodeC = tree.addNode(nodeA, tc("节点C"));// 添加数据tree.setNodeData(nodeA, tc("温度过高"));tree.setNodeData(nodeB, tc("正常"));tree.setNodeData(nodeC, tc("压力过低"));// 获取数据qDebug() << tc("节点A数据:") << tree.getNodeData(nodeA).toString();// 清除数据tree.clearNodeData(nodeC);// 过滤节点QList<TreeNode*> filteredNodes = tree.filterNodes(tc("温度"));qDebug() << tc("过滤结果:");for (TreeNode* node : filteredNodes) {qDebug() << node->getName() << ":" << node->getData().toString();}// 打印树结构tree.printTree();return a.exec();
}
运行结果
成功初始化根节点。
未指定父节点,默认添加到根节点。
未指定父节点,默认添加到根节点。
未指定父节点,默认添加到根节点。
节点A数据: "温度过高"
过滤结果:
"节点A" : "温度过高"
根节点 ()节点A (温度过高)节点B (正常)节点C ()
功能总结
该实现支持树节点的 添加、删除、查询、过滤,以及节点数据的 设置、获取、清除。同时,包含中文提示与日志输出,逻辑健壮且易于扩展。
相关文章:
基于Qt实现的自定义树结构容器:设计与应用
在Qt框架中,尽管其提供了许多强大的容器类(如 QList, QMap, QTreeWidget 等),但缺少一个通用的、灵活的树结构容器,直接支持多层级数据管理。为了满足这些需求,本文设计并实现了一个可复用的自定义树结构容…...
网络命令Linux
目录 一,Linux 二,CMD 一,Linux ping www.baidu.com 测试联网 -c 2 次数,ping几次 , -i 间隔 -W timeout 超时时间,等待响应的超时时间 ss -lntup |grep -w 22 netstat -lntup |grep -w 22 lsof -i:22 ls…...
简单的Activiti Modoler 流程在线编辑器
简单的Activiti Modoler 流程在线编辑器 1.需求 我们公司使用的流程是activiti5.22.0,版本有些老了,然后使用的编辑器都是eclipse的流程编辑器插件,每次编辑流程需要打开eclipse进行编辑,然后再导入到项目里面,不是特…...
【NodeJS】Express写接口的整体流程
前提条件 开发 Node.js,首先就必须要安装 Node.js。推荐使用 nvm,它可以随意切换 node 版本。下载 nvm,具体可以看本人另一篇文章:nvm的作用、下载、使用、以及Mac使用时遇到commond not found:nvm如何解决。 nvm官方࿱…...
Oracle 锁表的解决方法及避免锁表问题的最佳实践
背景介绍 在 Oracle 数据库中,锁表或锁超时相信大家都不陌生,是一个常见的问题,尤其是在执行 DML(数据操作语言)语句时。当一个会话对表或行进行锁定但未提交事务时,其他会话可能会因为等待锁资源而出现超…...
关于 vue+element 日期时间选择器 限制只能选当天以及30天之前的日期
业务需求,需要实现选择当天以及30天之前的日期,于是我想到的是利用picker-options去限制可选范围 代码如下 <el-date-pickerv-model"searchData.acceptTime"type"datetimerange"value-format"yyyy-MM-dd hh:mm:ss"styl…...
租辆酷车小程序开发(二)—— 接入微服务GRPC
vscode中golang的配置 设置依赖管理 go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct GO111MODULEauto 在$GOPATH/src 外面且根目录有go.mod 文件时,开启模块支持 GO111MODULEoff 无模块支持,go会从GOPATH 和 vendor 文件夹寻找包…...
如何在 Ubuntu 22.04 上安装 Metabase 数据可视化分析工具
简介 Metabase 提供了一个简单易用的界面,让你能够轻松地对数据进行探索和分析。通过本文的指导,你将能够在 Ubuntu 22.04 系统上安装并配置 Metabase,并通过 Nginx 进行反向代理以提高安全性。本教程假设你已经拥有了一个非 root 用户&…...
MySQL 用户与权限管理
MySQL 是一种广泛使用的关系型数据库管理系统,支持多用户访问和权限控制。在多用户环境下,数据库安全至关重要,而用户和权限管理是数据库管理中最基础也是最重要的一部分。通过合理地创建和管理用户、分配和管理权限、使用角色权限,可以有效地保护数据库,确保数据的安全性…...
【Web前端】如何构建简单HTML表单?
HTML 表单是 Web 开发中非常重要的组成部分。它们是与用户交互的主要方式,能够收集用户输入的数据。表单的灵活性使它们成为 HTML 中最复杂的结构之一,但若使用正确的结构和元素,可以确保其可用性和无障碍性。 表单的基本结构 HTML 表单使用…...
Spring Boot 3 集成 Spring Security(3)数据管理
文章目录 准备工作新建项目引入MyBatis-Plus依赖创建表结构生成基础代码 逻辑实现application.yml配置SecurityConfig 配置自定义 UserDetailsService创建测试 启动测试 在前面的文章中我们介绍了 《Spring Boot 3 集成 Spring Security(1)认证》和 《…...
书生大模型实战营第四期-入门岛-4. maas课程任务
书生大模型实战营第四期-入门岛-4. maas课程任务 任务一、模型下载 任务内容 使用Hugging Face平台、魔搭社区平台(可选)和魔乐社区平台(可选)下载文档中提到的模型(至少需要下载config.json文件、model.safetensor…...
Spring ApplicationListener监听
【JavaWeb】Spring ApplicationListener-CSDN博客 ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布…...
K8s调度器扩展(scheduler)
1.K8S调度器 筛选插件扩展 为了熟悉 K8S调度器扩展步骤,目前只修改 筛选 插件 准备环境(到GitHub直接下载压缩包,然后解压,解压要在Linux系统下完成) 2. 编写调度器插件代码 在 Kubernetes 源代码目录下编写调度插件…...
IntelliJ IDEA 中,自动导包功能
在 IntelliJ IDEA 中,自动导包功能可以极大地提高开发效率,减少手动导入包所带来的繁琐和错误。以下是如何在 IntelliJ IDEA 中设置和使用自动导包功能的详细步骤: 一、设置自动导包 打开 IntelliJ IDEA: 启动 IntelliJ IDEA 并打…...
Spring事务笔记
目录 1.Spring 编程式事务 2.Transactional 3.事务隔离级别 4.Spring 事务传播机制 什么是事务? 事务是⼀组操作的集合, 是⼀个不可分割的操作. 事务会把所有的操作作为⼀个整体, ⼀起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成 功, 要么同时失败 1.Spri…...
SQLite 管理工具 SQLiteStudio 3.4.5 发布
SQLiteStudio 3.4.5 版本现已发布,它带来了大量的 bug 修复,并增加了一些小功能。SQLiteStudio 是一个跨平台的 SQLite 数据库的管理工具。 具体更新内容包括: 现在可以使用 Collations Editor 窗口在数据库中注册 Extension-based collatio…...
QT 实现组织树状图
1.实现效果 在Qt中使用QGraphicsItem和QGraphicsScene实现树状图,你需要创建自定义的QGraphicsItem类来表示树的节点,并管理它们的位置和连接,以下是实现效果图。 2.实现思路 可以看见,上图所示,我们需要自定义连线类和节点类。 每个节点类Node,需要绘制矩形框体文字…...
go-学习
文章目录 简介标识符字符串的拼接,关键字数据类型声明变量常量算术运算符关系运算符逻辑运算符位运算赋值运算符其他运算符 简介 Go 语言的基础组成有以下几个部分: 1.包声明 2.引入包 3.函数 4.变量 5.语句 & 表达式 6.注释 package main import &q…...
【面试分享】主流编程语言的内存回收机制及其优缺点
以下是几种主流编程语言的内存回收机制及其优缺点: 一、Java 内存回收机制: Java 使用自动内存管理,主要通过垃圾回收器(Garbage Collector,GC)来回收不再被使用的对象所占用的内存。Java 的垃圾回收器会定…...
STM32-- 串口发送数据
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)RESET);?? 答: 这行代码: while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) RESET);的作用是等待串口 USART2 的发送数据寄存器(TXE,Transmit Dat…...
数据结构 (13)串的应用举例
前言 数据结构中的串(String),也称为字符串,是一种常见且重要的数据结构,在计算机科学中被广泛应用于各种场景。 一、文本处理 文本编辑器:在文本编辑器中,字符串被用来表示和存储用户输入的文本…...
qt-- - 版本和下载介绍
qt版本很多,每个大版本都有几个版本是长期支持的(LTS),最好使用长期支持的。 例如qt5.15 qt6.2 qt6.8 都是LTS版本的。 qt在线安装需要提供账号,之前安装qt6.8因为账号问题试了很长时间,密码错了。 …...
解锁 Vue 项目中 TSX 配置与应用简单攻略
在 Vue 项目中配置 TSX 写法 在 Vue 项目中使用 TSX 可以为我们带来更灵活、高效的开发体验,特别是在处理复杂组件逻辑和动态渲染时。以下是详细的配置步骤: 一、安装相关依赖 首先,我们需要在命令行中输入以下命令来安装 vitejs/plugin-v…...
ShuffleNet:一种为移动设备设计的极致高效的卷积神经网络
摘要 https://arxiv.org/pdf/1707.01083 我们介绍了一种名为ShuffleNet的计算效率极高的卷积神经网络(CNN)架构,该架构专为计算能力非常有限的移动设备(例如10-150 MFLOPs)而设计。新架构利用两种新操作:逐…...
yum源问题的解决方案
linux课堂作业 问题描述 yum 直接安装tree的问题截图 这个错误表明你的系统没有正确注册到 Red Hat Subscription Management(这个问题不用管),也没有配置有效的 YUM 软件仓库,因此无法安装或更新软件包。 解决方案(…...
在Linux中备份msyql数据库和表的详细操作
目录 前情提要 一、备份mysql数据库 原库展示 (一)新建一个数据库 (二)在linux根目录下找个位置暂时存放 (三)临时sql还原真正存放到库中 (四)查看是否备份成功 备份库成功展示 二、备份表的操作 编辑 原表emp展示 (一)快速新建一个原结构相同的表 (二)原表所…...
实时数仓Kappa架构:从入门到实战
引言 随着大数据技术的不断发展,企业对实时数据处理和分析的需求日益增长。实时数仓(Real-Time Data Warehouse, RTDW)应运而生,其中Kappa架构作为一种简化的数据处理架构,通过统一的流处理框架,解决了传统…...
【老白学 Java】Warship v2.0(四)
Warship v2.0(四) 文章来源:《Head First Java》修炼感悟。 上一篇文章中,老白仔细分析了 v2.0 的设计思路以及实现手段,如果大家有好的设计方案也可以自行尝试。 本篇文章的主要内容是对 Warship 类进行最后的修改&a…...
LLM之学习笔记(一)
前言 记录一下自己的学习历程,也怕自己忘掉了某些知识点 Prefix LM 和 Causal LM区别是什么? Prefix LM (前缀语⾔模型)和 Causal LM(因果语言模型)是两者不同类型的语言模型,它们的区别在于生…...
网站怎么添加广告/怎么做竞价托管
首先在qq邮箱的设置中打开第一个pop3/SMTAP授权,并且记住授权码 from django.core.mail import send_mail subject 天天生鲜 message 正文 sender settings.EMAIL_FROM receiver [email] send_mail(subject,message,sender,receiver)在settings配置文件中写入 EMAIL_BACK…...
wordpress 安装插件 ftp/网购平台推广方案
前提条件 如果没有安装office的话,需要安装引擎 安装了office就不用安装引擎 连接数据库 Dim plMydb As Microsoft.Office.Interop.Access.Dao.DatabaseplMydb DAODBEngine_definst.OpenDatabase(ppDataBase) 备份数据库 AccessDB.CompactDatabase(iMDBFile, iTMPF…...
dz做的网站容易收录吗/百度seo优化教程免费
在Linux当中查找文件的命令但多,但个人觉得最重要的搜索文件的命令是find,这个命令使用非常频繁,需要熟练掌握 文章目录前言find 使用详解1.介绍2.语法详解3. find 选项示例(option)4、可选项总结友情链接前言 在Linux当中查找文件的命令但多…...
网站托管解决方案/seo搜索如何优化
请创建一个一维整型数组用来存储待排序关键码,关键码从数组下标为1的位置开始存储,下标为0的位置不存储关键码。输入关键码的个数,以及各个关键码,采用希尔排序的方法对关键码数组进行排序,输出每轮比较的过程。 输入描…...
免费网站建设哪个好/看b站视频软件下载安装
2020/08/12每日二十个英语单词 tuning 调谐;调频tuning and control interface 调谐及控制接口tuning fork 音叉tuning, fine 微调;精调tunnel diode 隧道二极管tunnel effect 隧道效应tunneling 隧道技术tunneling, quantum 量子隧道turbine 涡轮turbo…...
互联网保险监管/吉林关键词排名优化软件
本文作者戴尔伍德(Dale Woods)2007年开始交易外汇,是一名外汇“发烧友”,尤其偏爱价格运动(Price Action)分析,至今拥有12年的交易经验。在这十多年的交易生涯中,伍德一直对价格运动…...