Python绘图系统24:添加辅助坐标轴

news2024/11/28 22:33:01

文章目录

    • 辅助坐标
    • 增减坐标轴
    • 时间轴**
    • 代码优化
    • 源代码

Python绘图系统:

  • 前置源码: Python打造动态绘图系统
  • 📈一 三维绘图系统 📈二 多图绘制系统📈三 坐 标 轴 定 制
  • 📈四 定制绘图风格 📈五 数据生成导入📈六 三维动态演示
  • 坐标列表进阶:导出数据📌系统菜单📌批量文件导入📌辅助坐标轴

辅助坐标

通过eval实现的源码阅读功能,具有非常强大的定制功能,但落实到具体使用上,坐标输入框中的代码总不能太长。如果想对数据进行复杂的处理,那么就相当于提出了一个新的需求,能否给出一些辅助坐标系用于数据处理。

这个功能并不复杂,而且代码敲到现在,应该可以说是很有经验了,首先在工具栏末尾填上加减号按钮,用于添加删除辅助坐标。

在initFeature结尾加上

btn = ttk.Button(frm, text="+", width=3)
btn.pack(side=tk.LEFT)
btn.bind("<Button-1>", self.addLast)

btn = ttk.Button(frm, text="-", width=3)
btn.pack(side=tk.LEFT)
btn.bind("<Button-1>", self.rmLast)

界面变为

在这里插入图片描述

增减坐标轴

若想添加辅助坐标轴,那么首先要给坐标轴取一个名字,由于具体计算时都在函数中局部地进行,故而可以用abcdefg这样一直下去,一般不会出太大的问题。另一方面,创建AxisFrame需要空间宽度和控件模式这两个参数,而这个参数是从初始化时由外部传入的,所以在初始化代码中添加两个全局变量

self.afWidth = widths
self.afMode = mode

addLast代码如下

def addLast(self, evt):
    ABC = "abcdefghijklmnopqrs"
    for a in ABC:
        if a in self.afs:
            continue
        self.afs[a] = AxisFrame(self._a, 
            a, self.afMode, self.afWidth)
        break
    self.updateVisible()

这段代码相当于预设了最多20个辅助坐标,大部分情况下应该是够用了。

减少坐标轴的逻辑和添加坐标轴相反,但在pack_forget之后,不要忘了把这个对象删掉。

def rmLast(self, evt):
    CBA = "abcdefghijklmnopqrs"[::-1]
    for a in CBA:
        if a not in self.afs:
            continue
        self.afs[a].pack_forget()
        del self.afs[a]
        break
    self.updateVisible()

效果如下

在这里插入图片描述

时间轴**

在具体绘图时,时间轴和其他坐标轴有着完全不同的操作,这种不同也应该体现在AxisList的布局上。所以下面要为事件轴专门做一个开关,就像最开始的txyz的独立开关按钮一样。

同时,考虑到作图需求,即有些图像不是三个轴能够完全搞定的,比如箭头向量图。所以需要新增几个坐标轴。

self.vis = {L : True for L in 'txyzuvw'}
for u in uvw: self.vis[u] = False

这里需要改动的地方很多很零碎,最重要的地方是dimChanged函数,要把txyz编程’xyzuvw’,

def dimChanged(self, evt):
    txyz = self.getDim()
    for flag in 'xyzuvw':
        self.vis[flag] = flag in txyz
    self.updateVisible()

代码优化

至此,AxisList就已经有了相对完备的功能,但新增功能之后,还需要做的就是代码的优化,从可读性、复用率的角度对代码进行整理。

首先是文件格式常量,以及字母表,可先将其设为全局变量。

然后,当对话框返回值为空时,需要跳出函数。

另外,控件显示和隐藏功能其实是可以和坐标轴的显隐功能合在一起的,这也可以解决之前一直存在的一个bug。

在这里插入图片描述在这里插入图片描述

源代码

最后,附上源代码。关于AxisList的修改暂时可以告一段落了,接下来将要做的是修改DrawStyle和DrawType这两个class。

import tkinter as tk
import tkinter.ttk as ttk
from tkinter.filedialog import *
from tkinter.simpledialog import *
from tkinter.messagebox import showwarning
import numpy as np

from aframe import AxisFrame, AskDct
from base import DrawType, DrawStyle

class AxisList(ttk.Frame):
    def __init__(self, master, 
        title, mode, widths, 
        types, typeDct,        # 绘图类型Combobox的参数
        **options):
        super().__init__(master, **options)
        self.pack()
        self.initConst()
        self.afs = {}
        self.data = {}

        self.afWidth = widths
        self.afMode = mode

        self.initWidgets(title, widths)
        self.initFeature(types, typeDct)
        self.initAxis(mode, widths)
        self.initStyleFrame()
    
    def initConst(self):
        self.ABC = "abcdefghijklmnopqrs"
        self.FILE_MERGE = [('numpy数组', 'npy'), ('文本文件', 'csv')]
        self.FILE_ALL =   [('文本文件', 'txt'), ('文本文件', 'csv'), 
                           ('二进制文件', 'bin')]

    def initWidgets(self, title, widths):
        self.btn = ttk.Button(self, text=title, width=sum(widths)+5,
            command=self.Click)
        self.btn.pack(side=tk.TOP, fill=tk.X, expand=tk.YES)
        self._c = ttk.Frame(self)       # 此为主控件
        self._b = ttk.Frame(self._c)    # 工具栏控件
        self._a = ttk.Frame(self._c)    # 此为坐标轴
        self._s = ttk.LabelFrame(self._c, text = "绘图风格")
        
        self._b.pack(side=tk.TOP)
        self._a.pack(side=tk.TOP)
        self._s.pack(side=tk.TOP)
        
        self.collapsed = True
        self.Click()
    
    # 初始化工具栏
    def initFeature(self, types, typeDct):
        frm = self._b
        frm.pack(pady=2, side=tk.TOP, fill=tk.X)
        btn = ttk.Menubutton(frm, text="功能",width=4)
        btn.pack(side=tk.LEFT)
        m = self.initFileMenu(btn)
        btn.config(menu=m)

        self.drawType = DrawType(frm, typeDct, 
            func=self.dimChanged)
        self.drawType.pack(side=tk.LEFT, padx=2)
        self.vis = {L : True for L in 'txyzuvw'}

        ttk.Button(frm, text='t', width=3, 
            command=self.showTimeAxis).pack(side=tk.LEFT)

        btn = ttk.Button(frm, text="+", width=3)
        btn.pack(side=tk.LEFT)
        btn.bind("<Button-1>", self.addLast)

        btn = ttk.Button(frm, text="-", width=3)
        btn.pack(side=tk.LEFT)
        btn.bind("<Button-1>", self.rmLast)

    def addLast(self, evt):
        for a in self.ABC:
            if a in self.afs:
                continue
            self.afs[a] = AxisFrame(self._a, 
                a, self.afMode, self.afWidth)
            break
        self.updateVisible()

    def rmLast(self, evt):
        for a in self.ABC[::-1]:
            if a not in self.afs:
                continue
            self.afs[a].pack_forget()
            del self.afs[a]
            break
        self.updateVisible()

    def showTimeAxis(self):
        self.vis['t'] = not self.vis['t']
        self.updateVisible()

    # 设置菜单
    def initFileMenu(self, btn):
        top = tk.Menu(btn, tearoff=False)
        menuDct = {
            "导入" : {"按坐标轴"  : self.mImportOne, 
                      "合并导入"  : self.mImportMerge, 
                      "多文件导入": self.mImportAll},
            "导出" : {"合并导出"  : self.mExportMerge,
                     "全部导出"   : self.mExportAll,
                     "单轴导出"   : self.mExportOne},
            "窗口" : {   "风格"   : self.btnShowStyle}}

        for key in menuDct:
            m = tk.Menu(top, tearoff=False)
            for L,C in menuDct[key].items():
                m.add_command(label=L, command = C)
            top.add_cascade(label=key, menu=m)

        self.showStyle = False
        return top
    
    # 加载数据
    def btnLoadData(self):
        name = askopenfilename()
        data = np.genfromtxt(name)
        for i, flag in enumerate('xyzuvw'):
            if i >= data.shape[1]:
                return
            self.setOneMode(flag, "外部导入")
            self.data[flag] = self.setData(flag, data[:,i])

    def mImportOne(self):
        fs = askstring("设置参数", "坐标轴间按照逗号分开")
        fs = fs.replace(",", ",").split(',')
        for f in fs:
            if f not in self.getDim():
                showwarning("您输入的坐标轴并不存在")
                continue
            self.afs[f].setMode("外部导入")
            self.afs[f].slctChanged(None)
            self.afs[f].btnImportComplex(None)        
    
    def mImportAll(self):
        fs = askopenfilenames(filetypes=self.FILE_ALL)
        for i,f in enumerate(self.getDim()):
            if i == len(fs) : return
            self.afs[f].setMode("外部导入")
            self.afs[f].slctChanged(None)
            self.afs[f].btnImportSimple(None)

    def mImportMerge(self):
        path = askopenfilename(filetypes==self.FILE_MERGE)
        if path == "" : return
        if path.endswith('.npy'):
            data = np.load(path)
        else:
            data = np.loadtxt(path)
        for i,f in enumerate(self.getDim()):
            if i == len(data): continue
            self.afs[f].setMode("外部导入")
            self.afs[f].slctChanged(None)
            self.afs[f].data = data[i]
    
    def mExportMerge(self):
        path = asksaveasfilename(filetypes=self.FILE_MERGE, 
            initialfile=True)
        if path == "" : return
        arr = np.array([self.afs[flag].data for flag in self.getDim()])
        if path.endswith('.npy'):
            arr.tofile(path)
        else:
            np.savetxt(path, arr, delimiter=', ')

    def mExportAll(self):
        path = askdirectory()
        if path == "": return
        for flag in self.getDim():
            #self.afs[flag].save(path)
            pass
    
    def mExportOne(self):
        flag = askstring("请选择输出的轴")
        if flag not in self.getDim():
            showwarning("您输入的坐标轴并不存在")
        #self.afs[flag].save()

    # 初始化坐标轴
    def initAxis(self, mode, widths):
        for flag in 'txyzuvw':
            self.afs[flag] = AxisFrame(self._a, flag, mode, widths)
            self.afs[flag].pack(side=tk.TOP, fill=tk.X)
        self.vis = {L : L in self.getDim() for L in 'txyzuvw'}
        self.updateVisible()
    
    # 初始化风格控件
    def initStyleFrame(self):
        self.drawStyle = DrawStyle(self._s)
        self.drawStyle.pack(side=tk.TOP, fill=tk.X)
        
    # 维度改变时的回调函数
    def dimChanged(self, evt):
        txyz = self.getDim()
        for flag in 'xyzuvw':
            self.vis[flag] = flag in txyz
        self.updateVisible()
    
    # 更新隐藏和显示
    def updateVisible(self):
        for key in self.afs:
            self.afs[key].pack_forget()
        for key in self.ABC:
            if key in self.afs:
                self.afs[key].pack(side=tk.TOP, fill=tk.X)

        for flag in 'txyzuvw':
            if self.vis[flag]:
                self.afs[flag].pack(side=tk.TOP, fill=tk.X)
        
        if self.showStyle:
            self._s.pack(side=tk.TOP, fill=tk.X)
        else:
            self._s.pack_forget()
  
    def btnShowStyle(self):
        self.showStyle = not self.showStyle
        self.updateVisible()

    def getSub(self):
        return self.drawType.getSub()

    def getProj(self):
        return self.drawType.getProj()

    def getType(self):
        return self.drawType.getType()
    
    def getDim(self):
        xyz = self.getXYZ()
        if self.vis['t']:
            xyz = 't' + xyz
        return xyz

    def getXYZ(self):
        return self.drawType.getDim()
    
    def hasTimeAxis(self):
        return self.vis['t']
    
    def getStyle(self):
        return self.drawStyle.getVarDct()

    def Click(self):
        if self.collapsed:
            self._c.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES)            
        else:
            self._c.pack_forget()
        self.collapsed = not self.collapsed

    # 设置数据
    def setData(self, flag, data=None, **options):
        return self.afs[flag].setData(data, **options)
    
    # 设置模式
    def setOneMode(self, flag, mode):
        self.afs[flag].setMode(mode)

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

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

相关文章

面试必考精华版Leetcode199. 二叉树的右视图

题目&#xff1a; 代码&#xff08;首刷看解析&#xff09;&#xff1a; class Solution { public:vector<int> rightSideView(TreeNode* root) {unordered_map<int,int> rightmostvalue;queue<TreeNode*> nodeQueue;queue<int> depthQueue;int maxDe…

上古神器:十六位应用程序 Debug 的基本使用

文章目录 参考环境上古神器 DebugBug 与 DebuggingDebugDebug 应用程序淘汰原因使用限制 DOSBox学习 Debug 的必要性DOSBox-X Debug 的基本使用命令 R查看寄存器的状态修改寄存器的内容 命令 D显示内存中的数据指定起始内存空间地址指定内存空间的范围 命令 A使用命令语法错误查…

第8章 Spring(二)

8.11 Spring 中哪些情况下,不能解决循环依赖问题 难度:★★ 重点:★★ 白话解析 有一下几种情况,循环依赖是不能解决的: 1、原型模式下的循环依赖没办法解决; 假设Girl中依赖了Boy,Boy中依赖了Girl;在实例化Girl的时候要注入Boy,此时没有Boy,因为是原型模式,每次都…

量化交易全流程(四)

本节目录 数据准备&#xff08;数据源与数据库&#xff09; CTA策略 数据源&#xff1a; 在进行量化分析的时候&#xff0c;最基础的工作是数据准备&#xff0c;即收集数据、清理数据、建立数据库。下面先讨论收集数据的来源&#xff0c;数据来源可分为两大类&#xff1a;免…

最新宽字节注入攻击和代码分析技术

点击星标&#xff0c;即时接收最新推文 本文选自《web安全攻防渗透测试实战指南&#xff08;第2版&#xff09;》 点击图片五折购书 宽字节注入攻击 宽字节注入攻击的测试地址在本书第2章。 访问id1&#xff0c;页面的返回结果如图4-51所示&#xff0c;程序并没有报错&#xff…

基于树莓派CM4制作img系统镜像批量制作TF卡

文章目录 前言1. 环境与工具2. 制作镜像3. 烧录镜像4. 总结 前言 树莓派烧录完系统做定制化配置比较费时间。在面对大批量的树莓派要配置&#xff0c;那时间成本是非常巨大的。第一次配置完可以说是摸着石头过河&#xff0c;但是会弄了以后再配置&#xff0c;都是一些重复性操…

Canal实现数据同步

1、Canal实现数据同步 canal可以用来监控数据库数据的变化&#xff0c;从而获得新增数据&#xff0c;或者修改的数据。 1.1 Canal工作原理 原理相对比较简单&#xff1a; 1、canal模拟mysql slave的交互协议&#xff0c;伪装自己为mysql slave&#xff0c;向mysql master发送…

零代码编程:用ChatGPT将特定文件标题重命名为特定格式

一个文件夹里面是同一系列文件&#xff0c;但是有两种命名方法&#xff0c;现在想把文件标题格式统一。 在ChatGPT中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个批量重命名的任务&#xff0c;具体步骤如下&#xff1a; 打开本地电脑文件夹&#xf…

38 翻转二叉树

翻转二叉树 理解题意&#xff0c;翻转即每个结点的左右子树翻转/对调题解1 递归——自下而上题解2 迭代——自上而下 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 提示&#xff1a; 树中节点数目范围在 [0, 100] 内-100 < Node.…

树莓派CM4开启I2C与UART串口登录同时serial0映射到ttyS0 开启多串口

文章目录 前言1. 树莓派开启I2C与UART串口登录2. 开启多串口总结&#xff1a; 前言 最近用CM4的时候使用到了I2C以及多个UART的情况。 同时配置端口映射也存在部分问题。 这里集中记录一下。 1. 树莓派开启I2C与UART串口登录 输入指令sudo raspi-config 跳转到如下界面&#…

机器人过程自动化(RPA)入门 9. 管理和维护代码

仅仅创建一个自动化项目是不够的。无论是决定使用哪种布局,还是正确命名步骤,以正确的方式组织项目都很重要。项目也可以在新的项目中重用,这对用户来说非常方便。本章解释了我们可以重用项目的方法。我们还将学习配置技术并看到一个示例。最后,我们将学习如何集成TFS服务器…

第十二章 类和对象——封装

C面向对象的三大特性为&#xff1a;封装、继承、多态 C认为万事万物都皆为对象&#xff0c;对象上有其属性和行为 例如&#xff1a; 人可以作为对象&#xff0c;属性有姓名、年龄、身高、体重...&#xff0c;行为有走、跑、跳、吃饭、唱歌... 车也可以作为对象&#xff0c;…

89、Redis 的 value 所支持的数据类型(String、List、Set、Zset、Hash)---->Zset 相关命令

本次讲解要点&#xff1a; ** Set相关命令&#xff1a;是指value中的数据类型** 启动redis服务器&#xff1a; 打开小黑窗&#xff1a; C:\Users\JH>e: E:>cd E:\install\Redis6.0\Redis-x64-6.0.14\bin E:\install\Redis6.0\Redis-x64-6.0.14\bin>redis-server.exe …

Android-Q 对 startActivity() 做了限制,怎么适配?

一. Q 禁用后台启动 Activity 今天来聊聊“Android Q 中后台禁止启动 Activity 对现有国内 App 中启动页设计的影响”这个话题&#xff0c;再聊聊 Android Q 限制后台启动 Activity 的具体细节。 有人可能会觉得 P 还没用上&#xff0c;Q 还远着。如果只是对于普通用户来说&a…

为什么要使用虚拟机?VMware安装使用

前言 大家好&#xff0c;本文是讲述了为什么需要使用虚拟机、使用虚拟机的好处&#xff0c;以及如何在Windows系统中安装VMware。希望对大家有所帮助~ 目录 前言一、为什么要安装使用虚拟机&#xff1f;1.1、什么是虚拟机&#xff1f;1.2、虚拟机的核心组件1.3、使用虚拟机的好…

【RabbitMQ】常用消息模型详解

文章目录 AMQP协议的回顾RabbitMQ支持的消息模型第一种模型(直连)开发生产者开发消费者生产者、消费者开发优化API参数细节 第二种模型(work quene)开发生产者开发消费者消息自动确认机制 第三种模型(fanout)开发生产者开发消费者 第四种模型(Routing)开发生产者开发消费者 第五…

网络-fetch

文章目录 前言一、fetch简介优点&#xff1a;缺点&#xff1a; 二、使用getpost进度实现取消请求超时实现 总结 前言 本文主要记录浏览器与服务端网络通讯 fetch 的介绍与使用&#xff0c;将完成get、post、进度、取消请求、和超时请求的功能实现。 一、fetch简介 fetch作为继…

基于自适应启动策略的混合交叉动态约束多目标优化算法(MC-DCMOEA)求解CEC2015/CEC2018/CEC2023(MATLAB代码)

一、动态多目标优化问题 1.1问题定义 1.2 动态支配关系定义 二、 基于自适应启动策略的混合交叉动态多目标优化算法 基于自适应启动策略的混合交叉动态多目标优化算法&#xff08;Mixture Crossover Dynamic Constrained Multi-objective Evolutionary Algorithm Based on Se…