从零开始实现一个三维绘图系统

文章目录

    • 框架
    • 布局
    • 绘图函数
    • 源代码

框架

本文的目标是实现一个下图所示的系统,通过指定 x , y , z x,y,z x,y,z的表达式,以实现三维绘图的目的。这个需求其实此前也实现过,见此文,但其内容比较驳杂,并不利于快速实现,相比之下,本文直指核心问题,代码也进一步精简,更加适合学习。

在这里插入图片描述

为此,需要经历三个过程

  1. 实现将matplotlib的绘图窗口嵌入到tkinter的窗口中
  2. 实现对 x , y , z x,y,z x,y,z数据的读取
  3. 实现三维绘图

关于第一个问题,matplotlib中的图窗和工具栏分别是FigureCanvasTkAggNavigationToolbar2Tk对象,只需将二者押入到tkinter的布局窗口,即可实现二者的融合。

对于第二个问题,只需使用eval函数便可轻松实现,唯一的难点,则是 x , y , z x,y,z x,y,z三者之间的联系。

而只要第二个问题解决,那么第三个问题也就不是问题了,只需通过一个绘图函数,便可轻松解决。

下面代码给出了实现这个需求需要导入的模块,以及整个系统的基本框架

import tkinter as tk
import tkinter.ttk as ttk

import matplotlib as mpl
mpl.use('TkAgg')
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure

import numpy as np

class DarwSystem():
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("数据展示工具")

        self.data = {}

        self.setFrmCtrl()
        self.setFrmFig()
        self.root.mainloop()

    def setFrmCtrl(self): pass

    def setFrmFig(self): pass

    def setFrmAxis(self, frm, flag): pass

    def setCtrlButtons(self, frm): pass

    def readEntrys(self): pass

    def btnDrawImg(self): pass

其中,各函数含义如下

  • setFrmCtrl 用于右侧工具栏的组件布局
  • setFrmFig 用于左侧绘图窗口的嵌入,解决第一个问题
  • setFrmAxis 工具栏中的坐标输入框相似度很高,可以封装成专门的函数
  • readEntrys 读取输入窗口的数据,解决第二个问题
  • btnDrawImg 绘图函数,解决第三个问题

下面逐个讲解每个函数的实现过程,这些函数都是DataSystem类的成员,所以在书写时注意缩进。

布局

考虑matplotlib绘图窗口的嵌入是一个相对较难的问题,故而先行实现setFrmFig函数

def setFrmFig(self):
    frmFig = ttk.Frame(self.root)
    frmFig.pack(side=tk.LEFT,fill=tk.BOTH,expand=tk.YES)

    self.fig = Figure()
    self.canvas = FigureCanvasTkAgg(self.fig,frmFig)
    self.canvas.get_tk_widget().pack(
        side=tk.TOP,fill=tk.BOTH,expand=tk.YES)
    self.toolbar = NavigationToolbar2Tk(self.canvas,frmFig,
        pack_toolbar=False)
    self.toolbar.update()
    self.toolbar.pack(side=tk.RIGHT)

其中self.fig就是绘图窗口,后续若要画图,都要在这里设置坐标轴。canvas即绘图画布,toolbar则是画布下方的工具栏。在这里,可以将二者直接视为平平无奇的痛苦inter组件。

另一个和布局相关的函数是setFrmCtrl,其中又将 x , y , z x,y,z x,y,z三个坐标轴的设置封装成了一个函数,以简化代码。唯一的绘图按钮,绑定了btnDrawImg函数,将在后续实现。

def setFrmCtrl(self):
    frmCtrl = ttk.Frame(self.root,width=320)
    frmCtrl.pack(side=tk.RIGHT, fill=tk.Y)
    frm = ttk.Frame(frmCtrl, width=320)
    frm.pack(side=tk.TOP, fill=tk.X)
    ttk.Button(frm, text="绘图",width=5,
        command=self.btnDrawImg).pack(side=tk.LEFT)

    self.entrys = {}
    for flag in 'xyz':
        frm = ttk.Frame(frmCtrl)
        frm.pack(side=tk.TOP, fill=tk.X)
        self.setFrmAxis(frm, flag)

def setFrmAxis(self, frm, flag):
    tk.Label(frm, text=flag).pack(side=tk.LEFT)
    self.entrys[flag] = tk.Entry(frm)
    self.entrys[flag].pack(side=tk.LEFT, fill=tk.X)

绘图函数

绘图包括两个过程,一是读取输入内容,将其转换可供绘图的数据;二则是将这些数据绘制成图。

首先,第一步实现如下

def readEntrys(self):
    self.data = {}
    for flag in 'xyz':
        label = self.entrys[flag].get()
        if label=="": continue
        self.data[flag] = eval(self.entrys[flag].get())
        if flag =='x' : x = self.data['x']
        elif flag =='y' : y = self.data['y']

首先,定义一个全局字典 s e l f . d a t a self.data self.data用于绘图。

然后简单起见,用eval函数直接读取python表达式。但需要注意的是,由于循环顺序是 x → y → z x\to y\to z xyz,所以 x x x的表达式中不能出现 y , z y,z y,z y y y的表达式中只能出现 x x x z z z的表达式中 x , y x,y x,y都可以出现。

最后是绘图,这里需要实现给定 x , y x,y x,y x , y , z x,y,z x,y,z这两种情况的绘图代码,前者是二维坐标系,后者是三维的。而canvas的绘图逻辑与plt如出一辙,都是先创建一个坐标轴,然后在坐标轴上绘图,最后也只需把plt.draw改为canvas.draw,代码如下

def btnDrawImg(self):
    self.readEntrys()
    self.fig.clf()
    if 'z' in self.data:
        ax = self.fig.add_subplot(projection='3d')
        ax.plot(self.data['x'], self.data['y'], self.data['z'])
    else:
        ax = self.fig.add_subplot()
        ax.plot(self.data['x'], self.data['y'])
    self.fig.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.08)
    self.canvas.draw()

源代码

最后,将所有代码附在后面

import tkinter as tk
import tkinter.ttk as ttk

import matplotlib as mpl
mpl.use('TkAgg')
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure

import numpy as np

def setEntry(e, text):
    e.delete(0, "end")
    e.insert(0, text)

def detectArray(s):
    return s.rstrip('0123456789:, ')==''

class DarwSystem():
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("数据展示工具")

        self.data = {}

        self.setFrmCtrl()
        self.setFrmFig()
        self.root.mainloop()

    def setFrmCtrl(self):
        frmCtrl = ttk.Frame(self.root,width=320)
        frmCtrl.pack(side=tk.RIGHT, fill=tk.Y)
        frm = ttk.Frame(frmCtrl, width=320)
        frm.pack(side=tk.TOP, fill=tk.X)
        ttk.Button(frm, text="绘图",width=5,
            command=self.btnDrawImg).pack(side=tk.LEFT)

        self.entrys = {}
        for flag in 'xyz':
            frm = ttk.Frame(frmCtrl)
            frm.pack(side=tk.TOP, fill=tk.X)
            self.setFrmAxis(frm, flag)

    def setFrmAxis(self, frm, flag):
        tk.Label(frm, text=flag).pack(side=tk.LEFT)
        self.entrys[flag] = tk.Entry(frm)
        self.entrys[flag].pack(side=tk.LEFT, fill=tk.X)

    def readEntrys(self):
        self.data = {}
        for flag in 'xyz':
            label = self.entrys[flag].get()
            if label=="": continue
            self.data[flag] = eval(self.entrys[flag].get())
            if flag =='x' : x = self.data['x']
            elif flag =='y' : y = self.data['y']

    def btnDrawImg(self):
        self.readEntrys()
        self.fig.clf()
        if 'z' in self.data:
            ax = self.fig.add_subplot(projection='3d')
            ax.plot(self.data['x'], self.data['y'], self.data['z'])
        else:
            ax = self.fig.add_subplot()
            ax.plot(self.data['x'], self.data['y'])
        self.fig.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.08)
        self.canvas.draw()


    def setFrmFig(self):
        frmFig = ttk.Frame(self.root)
        frmFig.pack(side=tk.LEFT,fill=tk.BOTH,expand=tk.YES)

        self.fig = Figure()
        self.canvas = FigureCanvasTkAgg(self.fig,frmFig)
        self.canvas.get_tk_widget().pack(
            side=tk.TOP,fill=tk.BOTH,expand=tk.YES)
        self.toolbar = NavigationToolbar2Tk(self.canvas,frmFig,
            pack_toolbar=False)
        self.toolbar.update()
        self.toolbar.pack(side=tk.RIGHT)

if __name__ == "__main__":
    test = DarwSystem()

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VBA中表示单元格样式A1、R1C1和R[1]C[1]之间的区别

《VBA之Excel应用》(版权10178983)是非常经典的,是我推出的第七套教程,定位于初级,目前是第一版修订。这套教程从简单的录制宏开始讲解,一直到窗体的搭建,内容丰富,实例众多。大家可…

springboot189基于SpringBoot电商平台的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计,课程设计参考与学习用途。仅供学习参考, 不得用于商业或者非法用途,否则,一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

java-类与对象

一、类与对象 二、快速入门 三、类与对象的区别和联系 四、对象在内存中的存在形式

跟着pink老师前端入门教程-day27

三、变量 (一)变量概述 1、什么是变量 白话:变量就是一个装东西的盒子 通俗:变量是用于存放数据的容器,通过变量名获取数据,甚至数据可以修改 2、变量在内存中的存储 本质:变量是程序在内存…

原型模式-Prototype Pattern

原文地址:https://jaune162.blog/design-pattern/prototype-pattern/ 引言 在Java中如果我们想要拷贝一个对象应该怎么做?第一种方法是使用 getter和setter方法一个字段一个字段设置。或者使用 BeanUtils.copyProperties() 方法。这种方式不仅能实现相同类型之间对象的拷贝,…

解决vitepress首次加载慢(从40秒到1秒的倔强)

前言:在艰难的博客系统升级之路 这篇博客中我有提到vitepress首次加载非常耗时的问题,之前也在网上搜索时发现也有很多人说这个“问题”,但是在折腾了这么一段时间后,发现这也许本身不是vitepress的问题,而是我的启动方…

「递归算法」:两两交换链表中的节点

一、题目 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 示例 1: 输入:head [1,2,3,4] 输出&#xf…

Harris关键点检测原理简介

一、2D Harris 二、 3D Harris Harris关键点检测以及SAC-IA粗配准-CSDN博客

Docker部署Springboot项目

一、把Springboot项目打成jar包 (一)右键项目文件,点击Open Module Settings (二)选中Artifacts,点击中间的加号(Project Settings->Artifacts->JAR->From modules with dependencies…

WordPress站点如何实现发布文章即主动推送到百度快速收录和普通收录?

我们在WordPress后台成功发布文章之后,如果靠搜索引擎来抓取的话,可能会比较慢,所以十分有必要将我们成功发布的文章马上提交到百度、必应等搜索引擎中。下面boke112百科就跟大家说一说WordPress站点如何实现发布文章即主动推送到百度快速收录…

【Spring源码解读 底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理讲解

🎉🎉欢迎光临🎉🎉 🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀 🌟特别推荐给大家我的最新专栏《Spring 狂野之旅:底层原理高级进阶》 &#x1f680…

各类有关于花卉的深度学习数据集

花卉的识别和分类在深度学习过程中是最常见的使用的案例,因此各类有关花卉分类、识别、计数的图像数据集是大家都常用的数据集。最近收集到各类有关花卉的各类数据集分享给大家!! 1、16种花常见的图像数据集 数据说明:我们看到我…

简单的edge浏览器插件开发记录

今天在浏览某些网页的时候,我想要屏蔽掉某些信息或者修改网页中的文本的颜色、背景等等。于是在浏览器的控制台中直接输入JavaScript操作dom完成了我想要的功能。但是每次在网页之间跳转该功能都会消失,我需要反复复制粘贴js脚本,无法实现自动…

二、ActiveMQ安装

ActiveMQ安装 一、相关环境二、安装Java8三、下载安装包四、启动五、其他命令六、开放端口七、后台管理 一、相关环境 环境:Centos7.9安装ActiveMQ版本:5.15.9JDK8 二、安装Java8 安装教程:https://qingsi.blog.csdn.net/article/details/…

OpenCV-38 图像金字塔

目录 一、图像金字塔 1. 高斯金字塔 2. 拉普拉斯金字塔 一、图像金字塔 图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。简单来说,图像金字塔是同一图像不同分辨率的子图…

Qt for android : Qt6.6.2 搭建 环境

环境说明 参考Qt助手: Assistant 6.6.2 (MinGW 11.2.0 64-bit) ***Gradle : Gradle wrapper, version 8.3***JDK11 SDK Tools / NDK 25.1.8937393 参考 Qt For Android : Qt5.13.1 Qt for android: Qt6.4搭建环境遇到的几个问题

前方预警!2024年七大网络安全威胁

新颖创新技术的兴起和迅速采用已极大地改变了各行各业的全球网络安全和合规格局,比如生成式人工智能、无代码应用程序、自动化和物联网等新技术。 网络犯罪分子正转而采用新的技术、工具和软件来发动攻击,并造成更大的破坏。因此,《2023年网…

(02)Hive SQL编译成MapReduce任务的过程

目录 一、架构及组件介绍 1.1 Hive底层架构 1.2 Hive组件 1.3 Hive与Hadoop交互过程 二、Hive SQL 编译成MR任务的流程 2.1 HQL转换为MR源码整体流程介绍 2.2 程序入口—CliDriver 2.3 HQL编译成MR任务的详细过程—Driver 2.3.1 将HQL语句转换成AST抽象语法树 词法、语…

基于蓄电池和飞轮混合储能系统的SIMULINK建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 蓄电池储能原理 4.2 飞轮储能原理 4.3 混合储能系统原理 5.完整工程文件 1.课题概述 基于蓄电池和飞轮混合储能系统的SIMULINK建模与仿真。蓄电池和飞轮混合储能,蓄电池可以用SIMULINK…

私立医院患者大数据分析平台建设方案

一、项目目标 1、数据质量,统计数字不仅是真实可信,而且要及时,便于及时判断企业经营情况,同时通过内外部数据的对标,发现企业经营问题。 2、提供指标的监控预警,为决策提供支持,减少杂乱无用报表的生产。 3、提升数据处理效率,提高报表的可用性,让数据标准化,提高…