matplotlib与tkinter的一些总结

Python绘图】matplotlib:先搞明白plt. /ax./ fig再画

matplotlib approaches

采用matlotlib画图有多种方法,选择依据是代码是否容易维护。如果图像复杂,plt.xlabel,plt.ylabel,plt.title这些不能很好地定位对应的axis,相比之下面向对象的方式更容易修改和维护。

  • pyplot API: state-based API,绘图操作类似Matlab。
  • object-oriented API:object-oriented API,有复杂绘图需求时,matplotlib推荐直接操作对象。对象包括Figure,Axes等。
  • pylab

pylab is a module that includes matplotlib.pyplot, numpy and some additional functions within a single namespace. Its original purpose was to mimic a MATLAB-like way of working by importing all functions into the global namespace. This is considered bad style nowadays.

matplotlib 术语

  • canvas:
  • Figure:上图红框,包含child Axes、少量 ‘special’ artists (titles, figure legends, x label,y label)、canvas
  • Axes:上图蓝框,包含2个Axis对象、artists (titles, figure legends, x label,y label)
  • Axis:上图绿框中2个像数轴一样的对象(分别对应Axes.xaxis和Axes.yaxis),包含数据极值、ticks和ticklabels。

Axes

Axes就是我们常用的fig,ax = plt.subplots()中的ax。从官方文档axes_api中可以看出它的重要性,可以完成画图、设置Appearance/label/legend/ticks/ticklabels等。

以坐标轴刻度设置为例:细调坐标轴

Axes.tick_params(axis=‘both’, **kwargs)
参数:
axis : {‘x’, ‘y’, ‘both’} Axis on which to operate; default is ‘both’.
reset : bool If True, set all parameters to defaults before processing other keyword arguments. Default is False.
which : {‘major’, ‘minor’, ‘both’} Default is ‘major’; apply arguments to which ticks.
direction : {‘in’, ‘out’, ‘inout’} Puts ticks inside the axes, outside the axes, or both.
length : float Tick length in points.
width : float Tick width in points.
color : color Tick color; accepts any mpl color spec.
pad : float Distance in points between tick and label.
labelsize : float or str Tick label font size in points or as a string (e.g., ‘large’).
labelcolor : color Tick label color; mpl color spec.
colors : color Changes the tick color and the label color to the same value: mpl color spec.
zorder : float Tick and label zorder.
bottom, top, left, right : bool or {‘on’, ‘off’} controls whether to draw the respective ticks.
labelbottom, labeltop, labelleft, labelright : bool or {‘on’, ‘off’} controls whether to draw the respective tick labels.
labelrotation : float Tick label rotation

x轴ticks、tickslabel旋转30°,写在上方的方法:

import matplotlib.pyplot as plt
import numpy as np

fig,ax = plt.subplots()
plt.plot(np.random.rand(10))
ax.tick_params(axis='x', bottom=False, top=True, labelbottom=False, labeltop=True, labelrotation=30)
plt.show()

在这里插入图片描述

tkinter+ pyplot API无法退出进程

tkinter做界面时, pyplot API画图不关闭主界面直接退出mainGUI时会无法退出进程(感兴趣可以尝试选择method1或method2取消注释前后直接关闭主界面的差别)。解决方案是plt.close()或者退出时采用root.quit()杀死全部进程;另一种就是避免采用 pyplot API,选择面向对象的方式画图。

import numpy as np
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

root = tk.Tk()
fig = plt.figure()
plt.plot(np.random.rand(10))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)

# # method 1
# def quit():
#     plt.close()
#     root.destroy()
# root.protocol('WM_DELETE_WINDOW', quit) #退出前先关闭plt
#
# # method 2
# root.protocol('WM_DELETE_WINDOW', root.quit) #退出前杀死全部进程。
root.mainloop()

多轴绘制

在这里插入图片描述

Event handling and picking

Event connections
为了接收事件,需要写回调函数并绑定在事件管理器(FigureCanvasBase的一部分)上。如下所示是将onclick回调函数绑定在按钮按压事件上:

fig, ax = plt.subplots()
ax.plot(np.random.rand(10))

def onclick(event):
    print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
          ('double' if event.dblclick else 'single', event.button,
           event.x, event.y, event.xdata, event.ydata))

cid = fig.canvas.mpl_connect('button_press_event', onclick)

interactive navigation的改写【home,back,forward,zoom,save】

plt时toolbar自动创建,如果是自己写user interface code,可以把toolbar作为widget添加

  1. NTbar是基于NavigationToolbar2Tk的toolbar组件,去掉了修改subplots参数的按钮,这里只是为了展示继承的方法。
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk
class NTbar(NavigationToolbar2Tk):
    '''toolbar组件'''
    def __init__(self, canvas, window,btnname):
        self.canvas = canvas
        self.window = window
        NavigationToolbar2Tk.__init__(self, canvas,window)
        self.children['!button6'].pack_forget() #无法修改,去掉
        self.window.config(text='%s画布区  第1张 / 共1张' % btnname)

    # def forward(self, *args):
    #     """Move forward in the view lim stack."""
    #     self._nav_stack.forward()
    #     self.set_history_buttons()
    #     self._update_view()
    # 
    # def home(self, *args):
    #     """Restore the original view."""
    #     self._nav_stack.home()
    #     self.set_history_buttons()
    #     self._update_view()
    #     
    # def back(self, *args):
    #     """move back up the view lim stack"""
    #     self._nav_stack.back()
    #     self.set_history_buttons()
    #     self._update_view()
  1. CustomToolbar是基于NavigationToolbar2Tk的toolbar组件,
  • 去掉了修改subplots参数的按钮
  • 将forward、back函数改成了多张图片的前后切换,home改为回到第一张图
  • 增加了滑条重定向特定序号图片的功能
  • 增加了批量保存图片到本地的功能
import os
from ttkwidgets import TickScale
import tkinter as tk
from tkinter import ttk
import tkinter.messagebox
from tkinter.filedialog import askdirectory
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk

class CustomToolbar(NavigationToolbar2Tk):
    '''用于除日期可视化外的其他图片的toolbar组件'''
    def __init__(self, canvas, window, btnname,fignum,pic_plot,logger=None,
                 figpath=os.getcwd()):
        self.toolitems = [('Home', 'Lorem ipsum dolor sit amet', 'home', 'home'),
                          ('Back', 'consectetuer adipiscing elit', 'back', 'back'),
                          ('Forward', 'sed diam nonummy nibh euismod', 'forward', 'forward'),
                          (None, None, None, None),
                          ('Pan', 'tincidunt ut laoreet', 'move', 'pan'),
                          ('Zoom', 'dolore magna aliquam', 'zoom_to_rect', 'zoom'),
                          (None, None, None, None),
                          ('Subplots', 'putamus parum claram', 'subplots', 'configure_subplots'),
                          ('Save', 'sollemnes in futurum', 'filesave', 'save_figure')]
        # self.canvas = canvas
        # self.window = window

        self.toggle = 0
        self.fignum = fignum
        if self.fignum > 10:
            self.toolitems.extend([(None, None, None, None),
                                   ('BatchSave', 'Batch save', "filesave_large", 'batch_save'),
                                   ('Redirect', 'redirect to specific fig', "hand", 'redirect')])
        NavigationToolbar2Tk.__init__(self, canvas, window)
        self.btnname = btnname
        self.pic_plot = pic_plot
        self.children['!button6'].pack_forget()  # 无法修改,去掉
        self.logger = logger
        self.figpath = figpath

    def base_draw(self,toggle):
        self.window.config(text='%s画布区  第%d张 / 共%d张' % (self.btnname, toggle + 1, self.fignum))
        self.pic_plot(toggle)

    def pan(self):
        """Restore the original view."""
        self._nav_stack.home()
        self.set_history_buttons()
        self._update_view()

    def home(self):
        '''切回首页'''
        self.toggle = 0
        self.base_draw(self.toggle)

    def forward(self):
        '''向前查看'''
        self.toggle += 1

        if self.toggle > (self.fignum - 1):
            self.toggle = self.fignum - 1
            return
        self.base_draw(self.toggle)

    def back(self):
        '''向后查看'''
        self.toggle -= 1
        if self.toggle < 0:
            self.toggle = 0
            return
        self.base_draw(self.toggle)

    def batch_save(self):
        print("You clicked the selection tool")
        result = tkinter.messagebox.askyesno("批量保存图片", "图片数量较多,批量存储耗时较长,是否继续?")
        if result:
            try:
                # This method will handle the delegation to the correct type
                path_ = askdirectory()
                self.figpath_ = os.path.join(path_, self.btnname)
                self.mkfigdir(self.figpath_)
                for ii in range(self.fignum):
                    self.base_draw(ii)
                    figname = '%s_%s_%s.jpeg' % (self.btnname, ii, date.today())
                    self.canvas.figure.savefig(os.path.join(self.figpath_, figname))

                if self.logger:
                    self.logger.info('此次图片保存路径:%s' % self.figpath_)
            except Exception as e:
                if self.logger:
                    self.logger.error("Error saving file"+str(e))

    def redirect(self):
        print('redirect')
        top = tkinter.Toplevel(self.window)
        s2 = TickScale(top, orient='horizontal', from_=1, to=self.fignum, tickinterval=5, resolution=1,
                       showvalue=True, length=400)#style='my.Horizontal.TScale',
        s2.pack(fill='x')
        def print_sel():
            asd = int(s2.get())
            print(asd)
            if asd < self.toggle:
                self.toggle = asd
                self.back()
            elif asd == self.toggle:
                self.back()
            else:
                self.toggle = asd - 2
                self.forward()

        # s2.bind("<ButtonRelease>", print_sel)
        ttk.Button(master=top,text='转到指定页',command=print_sel).pack()

    def mkfigdir(self,path):
        isExists = os.path.exists(path)
        # 判断结果
        if not isExists:
            # 如果不存在则创建目录
            # 创建目录操作函数
            os.makedirs(path)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值