import cv2 import numpy as np import random import tkinter import tkinter as tk import matplotlib.pyplot as plt import albumentations as A from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from tkinter.messagebox import showinfo, showwarning from tkinter import filedialog from PIL import Image,ImageTk from tkinter.filedialog import askopenfilename, askopenfilenames, asksaveasfilename, askdirectory # ---------------------------------------------------------------------- plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文 plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 picSize=400 img_open = None img_result = None img_empty = cv2.imread('data/image/OIP-C.jpg',0) # ---------------------------------------------------------------------- # 几何变化类 # 水平翻转 def filp_h(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return transform = A.HorizontalFlip(p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,'水平翻转后') # 垂直翻转 def flip_v(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return transform = A.VerticalFlip(p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,'垂直翻转后') # 缩放 def resize(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [["水平方向缩放后大小", 224], ["垂直方向缩放后大小", 224]] paraW = paraWindow(root, "请设置水平和垂直方向的缩放尺寸", paraLists) h = int(paraW.paraLists[0][1]) w = int(paraW.paraLists[1][1]) transform = A.Resize(h,w) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,'缩放后') # 旋转(可选择旋转角度) def roate(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['请设置旋转角度',30]] paraW = paraWindow(root, "旋转图片", paraLists) angle = int(paraW.paraLists[0][1]) transform = A.Rotate(angle) img_result = transform(image=np.array(img_open),p=1) img_result = img_result['image'] placePic2(img_result,'旋转后') # 裁剪(裁剪方式:centercrop、randomcrop和cutout) def Centercrop(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [["水平方向裁剪后大小", 224], ["垂直方向裁剪后大小", 224]] paraW = paraWindow(root, "请设置水平和垂直方向的裁剪后尺寸", paraLists) h = int(paraW.paraLists[0][1]) w = int(paraW.paraLists[1][1]) transform = A.CenterCrop(h, w,p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,'中心裁剪后') def Randomcrop(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [["水平方向缩放后大小", 224], ["垂直方向缩放后大小", 224]] paraW = paraWindow(root, "请设置水平和垂直方向的缩放尺寸", paraLists) h = int(paraW.paraLists[0][1]) w = int(paraW.paraLists[1][1]) transform = A.RandomCrop(h, w,p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,'随机裁剪后') # 反向裁剪(随机选择区域) def cutout(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return h,w,c = img_open.shape # 后续是原图和mask相乘,所以初始化为0 # 不需要裁剪的区域就不变,裁剪的区域变成0 mask = np.ones_like(img_open) print(mask) x = np.random.randint(0,w) y = np.random.randint(0,h) paraLists = [['相对原图裁剪区域的大小', 0.2]] paraW = paraWindow(root, "请设置具体参数", paraLists) # p = int(paraW.paraLists[0][1]) s = float(paraW.paraLists[0][1]) w_cut = int(s*w) h_cut = int(s*h) x1 = max(x - w_cut // 2, 0) # 计算裁剪的左上角坐标 x2 = min(x1 + w_cut, w) # 计算裁剪的右下角坐标 y1 = max(y - h_cut // 2, 0) y2 = min(y1 + h_cut, h) mask[y1:y2, x1:x2, :] = 0 # 将裁剪区域内像素值置为 0 img_result = img_open * mask # 与原始图像按元素相乘,得到 Cutout 后的图像 print(img_result[y1:y2, x1:x2, :]) placePic2(img_result,text2='反向随机裁剪后') # 对比度、饱和度、亮度、色调变化 def ColorJitter(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [["亮度", 0], ["色调", 0],['对比度',0],['饱和度',0]] paraW = paraWindow(root, "请设置具体参数", paraLists) brightness = float(paraW.paraLists[0][1]) # 亮度 hue = float(paraW.paraLists[1][1]) # 色调 contrast = float(paraW.paraLists[2][1]) # 对比度 saturation = float(paraW.paraLists[3][1]) # 饱和度 transform = A.ColorJitter(brightness=brightness,hue=hue,saturation=saturation,contrast=contrast,p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result) # 转换成灰度图 def ToGray(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return placePic2(img_gray,'灰度图') # 通道分离 def split_channel(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return ''' 现在 的img_open 是RGB的''' R,G,B = cv2.split(img_open) paraLists = [['分离通道', 0]] paraW = paraWindow(root, "012分别代表RGB", paraLists) dict = {'0':R,'1':G,'2':B} img_result = dict[str(int(paraW.paraLists[0][1]))] placePic2(img_result,'分离通道后') # 随机模糊 def Blur(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['模糊的次数',3]] paraW = paraWindow(root, "请设置具体参数", paraLists) count = int(paraW.paraLists[0][1]) transform = A.Blur(blur_limit=count,p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result) # 高斯滤波 def Gaussblur(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['模糊的次数', 3]] paraW = paraWindow(root, "请设置具体参数", paraLists) count = int(paraW.paraLists[0][1]) transform = A.GaussianBlur(blur_limit=count, p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,text2='经过高斯模糊后') # 中值滤波 def Medianblur(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['模糊的次数', 3]] paraW = paraWindow(root, "请设置具体参数", paraLists) count = int(paraW.paraLists[0][1]) transform = A.MedianBlur(blur_limit=count, p=1) img_result = transform(image=np.array(img_open)) img_result = img_result['image'] placePic2(img_result,text2='经过中值滤波后') # 添加高斯噪声 def add_gaussian_noise(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['请输入均值', 0],['请输入标准差',30]] paraW = paraWindow(root, "请设置具体参数", paraLists) mean = int(paraW.paraLists[0][1]) sigma = int(paraW.paraLists[1][1]) # 生成高斯噪声矩阵 row, col, ch = img_open.shape gaussian = np.random.randn(row, col, ch) * sigma + mean gaussian = gaussian.reshape(row, col, ch) img_result = img_open + gaussian # 转换数据类型为8位无符号整数类型 img_result = cv2.convertScaleAbs(img_result) placePic2(img_result,text2='添加高斯噪声后') # 添加椒盐噪声 def add_salt_and_pepper_noise(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['百分比', 0]] paraW = paraWindow(root, "请设置具体参数", paraLists) percentage = int(paraW.paraLists[0][1]) # 确保百分比在 0 到 100 之间 if percentage < 0 or percentage > 100: showwarning(title='警告', message='百分比必须在 0 到 100 之间!') return # 生成椒盐噪声矩阵 row, col, ch = img_open.shape noise = np.zeros((row, col, ch), np.uint8) for i in range(row): for j in range(col): rand = np.random.randint(0, 100) if rand < percentage: noise[i][j] = [0, 0, 0] elif rand > 100 - percentage: noise[i][j] = [255, 255, 255] else: noise[i][j] = img_open[i][j] # 将椒盐噪声矩阵添加到原始图像中 img_result = cv2.add(img_open, noise) placePic2(img_result,text2='添加椒盐噪声后') # 添加均值噪声 def add_mean_noise(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return paraLists = [['均值', 0], ['标准差', 30]] paraW = paraWindow(root, "请设置具体参数", paraLists) mean = int(paraW.paraLists[0][1]) std_dev = int(paraW.paraLists[1][1]) # 生成均值噪声矩阵 row, col, ch = img_open.shape noise = np.random.normal(mean, std_dev, (row, col, ch)).astype(np.uint8) # 将均值噪声矩阵添加到原始图像中 img_result = cv2.add(img_open, noise) placePic2(img_result,text2='添加均值噪声后') # 使用sobel算子进行锐化 def sobel(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return kernelx1 = np.array([[-1,-2,-1],[0,0,0],[1,2,1]], dtype=int) kernely1 = np.array([[-1,0,1],[-2,0,2],[-1,0,1]], dtype=int) x1 = cv2.filter2D(img_gray, cv2.CV_16S, kernelx1) y1 = cv2.filter2D(img_gray, cv2.CV_16S, kernely1) absX1 = cv2.convertScaleAbs(x1) absY1 = cv2.convertScaleAbs(y1) img_result = cv2.addWeighted(absX1, 0.5, absY1, 0.5, 0) placePic2(img_result) # 使用Prewitt算子进行锐化 def Prewitt(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return kernelx1 = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int) kernely1 = np.array([[-1,-1,1],[0,0,0],[1,1,1]], dtype=int) x1 = cv2.filter2D(img_gray, cv2.CV_16S, kernelx1) y1 = cv2.filter2D(img_gray, cv2.CV_16S, kernely1) absX1 = cv2.convertScaleAbs(x1) absY1 = cv2.convertScaleAbs(y1) img_result = cv2.addWeighted(absX1, 0.5, absY1, 0.5, 0) placePic2(img_result) # 使用robert算子进行锐化 def robert(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return kernelx1 = np.array([[-1, 0], [0, 1]], dtype=int) kernely1 = np.array([[0, -1], [1, 0]], dtype=int) x1 = cv2.filter2D(img_gray, cv2.CV_16S, kernelx1) y1 = cv2.filter2D(img_gray, cv2.CV_16S, kernely1) absX1 = cv2.convertScaleAbs(x1) absY1 = cv2.convertScaleAbs(y1) img_result = cv2.addWeighted(absX1, 0.5, absY1, 0.5, 0) placePic2(img_result) # 直方图均衡化 def equalhist(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return # 说明是灰度图像 if len(img_open)==2: img_result = cv2.equalizeHist(img_open) # RGB图像分别对每个通道都均衡化 然后在 合并 else: b,g,r = cv2.split(img_open) b_eq = cv2.equalizeHist(b) g_eq = cv2.equalizeHist(g) r_eq = cv2.equalizeHist(r) img_result = cv2.merge([b_eq,g_eq,r_eq]) placePic2(img_result) # 叠加图片操作 def mixup(): global img_open, img_gray, img_result if img_open is None: showwarning(title='警告', message='请先在文件菜单下打开图片!') return img_show1 = img_open # 读取另一张图片 path = askopenfilename(title='打开另一张图片') if path!=0 and path!='': img_show2 = cv_imread(path) if img_show2 is not None: placePic2(img_show2) else: placePic2(img_empty) showwarning(title='警告',message='请打开第二张图片') # 下面进行mixup操作 paraLists = [['请输入一张图片所占比列', 0.5]] paraW = paraWindow(root, "请设置具体参数", paraLists) # 判断两张图片的shape 是否相同 不相同则都取小的那边 if img_show1.shape[:-1]!=img_show2.shape[:-1]: showwarning(title='警告',message='两张图片大小不一样,会自动做裁剪使大小相同') if img_show1.shape[0] > img_show2.shape[0]: h = img_show2.shape[0] else: h = img_show1.shape[0] if img_show1.shape[1] > img_show2.shape[1]: w = img_show2.shape[1] else: w = img_show1.shape[1] img_show1 = cv2.resize(img_show1,(h,w)) img_show2 = cv2.resize(img_show2,(h,w)) # 判断图片的channel是否相同,如果不相同 在全部转换为 RGB的 if len(img_show1)==2: img_show1 = cv2.cvtColor(img_show1,cv2.COLOR_GRAY2RGB) if len(img_show2)==2: img_show2 = cv2.cvtColor(img_show1,cv2.COLOR_GRAY2RGB) alpha = float(paraW.paraLists[0][1]) print(alpha) # 这个方法需要两张图片的大小相同 需要做个resize操作 img_result = cv2.addWeighted(img_show1,alpha,img_show2,1-alpha,0) cv_show('after mixup',img_result) # ---------------------------------------------------------------------- def cv_show(name,img): # 打开文件后会有img_open,img_gray的信息,img_result指进行操作后的可视化结果 # 图片很大时需要进行resize h,w = img.shape[:2] if h>1000 or w>1000: img = cv2.resize(img,dsize=None,fx=0.8,fy=0.8) cv2.imshow(name,img) cv2.waitKey(0) cv2.destroyAllWindows() def cv_imread(filename,colorMode=cv2.IMREAD_COLOR): # 读取文件路径中带汉字的图片,替代cv2.imread(filename) # :param filename: 需要读取的文件 # :param colorMode: 彩色模式 # :return: 读取的图片 return cv2.imdecode(np.fromfile(filename, dtype=np.uint8), colorMode) def cv_imwrite(path, img): # 解决路径中有汉字的图片保存问题 # :param path: 保存的路径 # :param img: 保存的图片 cv2.imencode('.jpg', img)[1].tofile(path) def placePic1(img_show, text1="原始图像"): # 将打开的初始图像放置在左边窗格 # :param img_show: 需要显示的图片 # :param text1: 图片说明 global picSize if img_show is None: showwarning(title='警告', message='未打开图片!') return img = img_show.copy() if len(img.shape) > 2: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) height, width = img.shape[:2] scaling = max(width, height) / picSize newH = int(height / scaling) newW = int(width / scaling) img0 = Image.fromarray(img) # 由OpenCV图片转换为PIL图片格式 img0 = img0.resize((newW, newH)) img0 = ImageTk.PhotoImage(img0) myWindow.originalText.set(text1) myWindow.label3.config(image=img0) myWindow.label3.image = img0 myWindow.label3.place(relx=0.25, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER) # 设置绝对座标 def placePic2(img_show, text2="处理结果图", RGB=True): # 将处理结果图像放置在右边窗格 # :param img_show: 需要显示的图像 # :param text2: 图像文字说明 # :param RGB: 是否转换成RGB,默认转换 global picSize, img_result if img_show is None: showwarning(title='警告', message='没有结果图片!') return img = img_show.copy() if len(img.shape) > 2 and RGB: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) height, width = img.shape[:2] # img = PointProcessing.global_linear_transmation(img) scaling = max(width, height) / picSize newH = int(height / scaling) newW = int(width / scaling) img0 = Image.fromarray(img) # 由OpenCV图片转换为PIL图片格式 img0 = img0.resize((newW, newH)) img0 = ImageTk.PhotoImage(img0) myWindow.resultText.set(text2) myWindow.label4.config(image=img0) myWindow.label4.image = img0 # 通过标签来显示图片,使用ImageTK把img转成tk可以接收的形式 myWindow.label4.place(relx=0.75, rely=0.40, width=picSize, height=picSize, anchor=tkinter.CENTER) def Choosepic(): # 打开文件选择对话框,并读取图片文件,存放在img_open全局变量中 global picSize, path_, img_open, img_gray, img_empty path_ = askopenfilename(title="打开需要处理的图片") # path.set(path_) if path_ != 0 and path_ != '': img_open = cv_imread(path_) # 读取文件路径中有汉字的图片文件,读取的是RGB图像 img_gray = cv2.cvtColor(img_open,cv2.COLOR_BGR2GRAY) if img_open is not None: placePic1(img_open) else: placePic1(img_empty) showwarning(title='警告', message='无图片!') else: img_open = None img_gray = None placePic1(img_empty) showwarning(title='警告', message='未选择图片!') def Savepic(): # 打开文件对话框,将img_result图片保存到指定文件 global img_result if img_result is not None: fname = asksaveasfilename(title='保存文件', defaultextension='default.jpg',filetypes=[("JPG", ".jpg")]) if fname: cv_imwrite(str(fname), img_result) showinfo(title='提示', message='图片已保存!') else: showwarning(title='警告', message='请先输入文件名!') else: showwarning(title='警告', message='请先处理图片!') def initWindows(): # 将系统界面恢复到初始界面,并不清除结果图片 myWindow.setVisibleLeft() myWindow.setVisibleRight0() myWindow.hideFig0() myWindow.hideFig1() if img_result is not None: placePic2(img_result) class ImageApp: global picSize def __init__(self, master): self.master = master self.master.title("图像多功能增强可视化窗口") # 界面布局 self.init_menu(self.master) # 初始化菜单 self.init_widget(self.master) # 初始化需要用的控件(两个画布) self.setVisibleLeft() # 显示左窗格 self.setVisibleRight0() # 显示右窗格 self.hideFig0() # 隐藏左窗格的画布myCanvas0 self.hideFig1() # 隐藏右窗格的画布myCanvas1 def init_widget(self, master): self.originalText = tkinter.StringVar() self.label1 = tkinter.Label(master, textvariable=self.originalText, font=("song", 12)) self.originalText.set("原始图像") self.label3 = tkinter.Label(master, bg='gray86') # 用于显示原始图像,背景颜色设置成灰色 self.resultText = tkinter.StringVar() self.label2 = tkinter.Label(master, textvariable=self.resultText, font=("song", 12)) self.resultText.set("处理结果图") self.label4 = tkinter.Label(master, bg='gray86') # 用于显示处理结果,背景颜色设置成灰色 self.explainText = tkinter.StringVar() self.myFig0 = plt.figure(figsize=(4, 4), facecolor='#dddddd') self.myCanvas0 = FigureCanvasTkAgg(self.myFig0, master) # myfig1在下边窗格的画布中显示多张包含图片的注释说明 self.myFig1 = plt.figure(figsize=(9, 1.8), facecolor='lightgray') # myfig2在下边窗格中显示说明与单张图片 self.myFig2 = plt.figure(figsize=(4.5, 1.8), facecolor='lightgray') self.myCanvas1 = FigureCanvasTkAgg(self.myFig1, master) self.myCanvas2 = FigureCanvasTkAgg(self.myFig2, master) def init_menu(self,master): # 创建菜单和其内容 menubar = tk.Menu(master) # 创建菜单对象 # 创建菜单内容 后面加command 可以创建子菜单 到时候看看需不需要 fmenu_file = tk.Menu(master) # 文件 fmenu_file.add_command(label='打开',command=Choosepic) fmenu_file.add_command(label='保存处理结果', command=Savepic) fmenu_file.add_separator() # 添加一条分隔线 fmenu_file.add_command(label='退出',command=quit) # 几何变换 fmenu_geo = tk.Menu(master) fmenu_geo.add_command(label='水平翻转', command=filp_h) fmenu_geo.add_command(label='垂直翻转', command=flip_v) fmenu_geo.add_command(label='旋转', command=roate) fmenu_geo.add_command(label='缩放', command=resize) # 给裁剪菜单再次创建子菜单 submenu_crop = tk.Menu(fmenu_geo) fmenu_geo.add_cascade(label='裁剪',menu=submenu_crop) submenu_crop.add_command(label='中心裁剪',command=Centercrop) submenu_crop.add_command(label='随机裁剪', command=Randomcrop) submenu_crop.add_command(label='反向随机裁剪', command=cutout) # 颜色空间变换 fmenu_sp = tk.Menu(master) fmenu_sp.add_command(label='对比度变换',command=ColorJitter) fmenu_sp.add_command(label='亮度变换', command=ColorJitter) fmenu_sp.add_command(label='饱和度变换', command=ColorJitter) fmenu_sp.add_command(label='色调',command=ColorJitter) fmenu_sp.add_command(label='通道分离', command=split_channel) fmenu_sp.add_command(label='灰度图', command=ToGray) fmenu_sp.add_command(label='直方图均衡化', command=equalhist) # 像素点操作 fmenu_pix = tk.Menu(master) # 创建像素点操作的主菜单 submenu_blur = tk.Menu(fmenu_pix) # 模糊子菜单 fmenu_pix.add_cascade(label='模糊',menu=submenu_blur) # 把模糊子菜单加到模糊下面 # 给模糊子菜单加子菜单 submenu_blur.add_command(label='高斯滤波',command=Gaussblur) submenu_blur.add_command(label='随机模糊', command=Blur) submenu_blur.add_command(label='中值滤波', command=Medianblur) submenu_sh = tk.Menu(fmenu_pix) fmenu_pix.add_cascade(label='锐化',menu=submenu_sh) # 给锐化加子菜单 submenu_sh.add_command(label='robort算子',command=robert) submenu_sh.add_command(label='Prewitt算子',command=Prewitt) submenu_sh.add_command(label='Sobel算子',command=sobel) submenu_noise = tk.Menu(fmenu_pix) fmenu_pix.add_cascade(label='添加噪声',menu=submenu_noise) # 给添加噪声加子菜单 submenu_noise.add_command(label='高斯噪声',command=add_gaussian_noise) submenu_noise.add_command(label='椒盐噪声',command=add_salt_and_pepper_noise) submenu_noise.add_command(label='均值噪声',command=add_mean_noise) # 基于多张图片 fmenu_mul = tk.Menu(master) fmenu_mul.add_command(label='mixup',command=mixup) # 将菜单内容 添加到menubar menubar.add_cascade(label="文件", menu=fmenu_file) menubar.add_cascade(label="几何变化", menu=fmenu_geo) menubar.add_cascade(label="颜色空间变换", menu=fmenu_sp) menubar.add_cascade(label="频域像素点操作", menu=fmenu_pix) menubar.add_cascade(label='基于多张图片',menu=fmenu_mul) # 整体显示 master.config(menu=menubar) def setVisibleLeft(self): # label1是文字,label3目前是空只是占了位置,且背景颜色是灰色,需要放图片 self.label1.place(relx=0.25, rely=0.04, anchor=tkinter.CENTER) # 设置相对座标 self.label3.place(relx=0.25, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) # 设置绝对座标 def setVisibleRight0(self): # 右边窗格重叠的显示之一(right0) # right0一般算法结果显示窗格 # right1-用于显示车牌识别结果 # myFig0-使用mayplot显示运行结果图片 # label2 是文字,label4目前是空只是占了位置 且背景颜色是灰色,需要放图片 self.label2.place(relx=0.75, rely=0.04, anchor=tkinter.CENTER) self.label4.place(relx=0.75, rely=0.40, width=picSize, height=picSize,anchor=tkinter.CENTER) def setVisibleBottom(self): # 下边窗格的重叠显示之一(label5) # label5-仅显示文字说明 # myFig1-用matplot显示多个文字图片 # explain-左边是label5,右边是myFig2 self.label5.place(relx=0.50, rely=0.86, width=900, height=180,anchor=tkinter.CENTER) self.hideFig1() self.hideExplain() def hideFig0(self): self.myCanvas0.get_tk_widget().place_forget() # 隐藏画布self def hideFig1(self): self.myCanvas1.get_tk_widget().place_forget() # 隐藏画布self def hideExplain(self): self.label5.config(wraplength=850) self.myCanvas2.get_tk_widget().place_forget() # 隐藏画布self(暂时隐藏,用的时候可以用place方法恢复) # 有的功能还需要选择具体参数,这个时候需要弹出一个小窗口给参数,并获取这些参数值,给具体的变化 # ---------------------------------------------------------------------- class paraWindow(tkinter.Toplevel): # 多行一列的参数设置窗口 def __init__(self, root, title = None, paraLists=[]): # Constructor self.root = root self.paraLists=paraLists self.names = locals() #动态组件 tkinter.Toplevel.__init__(self,root) if title: self.title(title) # 创建对话框的主体内容 frame = tkinter.Frame(self) # 调用init_widgets方法来初始化对话框界面 self.initial_focus = self.init_widgets(frame) frame.pack(padx=5, pady=5) # 根据modal选项设置是否为模式对话框 self.grab_set() #重要,必须是模式对话框 # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 self.protocol("WM_DELETE_WINDOW", self.cancel_click) # 根据父窗口来设置对话框的位置 self.geometry("+%d+%d" % (root.winfo_rootx() + 100, root.winfo_rooty() + 100)) # 让对话框获取焦点 self.initial_focus.focus_set() self.wait_window(self) def init_widgets(self,master): # 创建自定义对话框的内容 nrow=0 for i in range(len(self.paraLists)): str0 = self.paraLists[i][0] nrow = i + 1 labelMessage = tkinter.Label(master, text=str0, font=("song", 12)) labelMessage.grid(row=nrow, column=0) self.names['paraV' + str(i)] = tkinter.StringVar() self.names['paraE' + str(i)] = tkinter.Entry(master, textvariable=self.names['paraV' + str(i)], width=20) self.names['paraV' + str(i)].set(self.paraLists[i][1]) self.names['paraE' + str(i)].grid(row=nrow, column=1) # 控件按列排列 b1 = tkinter.Button(master, text='确定退出', command=self.setPara) b1.grid(row=nrow + 1, column=1) self.bind("", self.setPara) self.bind("", self.cancel_click) return self.names['paraE0'] def cancel_click(self, event=None): showwarning(title='警告', message='必须先设置参数') self.initial_focus.focus_set() def setPara(self): # 通过对话框设置参数 for i in range(len(self.paraLists)): text0 = self.names['paraV' + str(i)].get() if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 showwarning(title='警告', message='必须输入数字') self.names['paraV' + str(i)].set(self.paraLists[i][1]) self.names['paraE'+ str(i)].focus_set() return else: self.paraLists[i][1] = float(text0) self.hide() def on_validate(self,content): # 该方法可对用户输入的数据进行校验,保证输入的是数字 for i in range(len(content) - 1, -1, -1): if not (48 <= ord(content[i]) <= 57 or content[i] == "." or content[i] =="+" or content[i] =="-" ): return False return True def hide(self): # 销毁对话框 self.withdraw() self.update_idletasks() # 将焦点返回给父窗口 self.root.focus_set() # 销毁自己 self.destroy() self.root.update() self.root.deiconify() self.root.focus_set() class paraWindow2(tkinter.Toplevel): # 多行多列参数设置窗口 def __init__(self, root, paraLists,title,explain): # Constructor self.root = root self.names=locals() self.paraLists=paraLists self.explain=explain tkinter.Toplevel.__init__(self,root) # self.geometry("400x300") self.title(title) # 调用init_widgets方法来初始化对话框界面 self.initial_focus = self.init_widgets() # 根据modal选项设置是否为模式对话框 self.grab_set() # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 self.protocol("WM_DELETE_WINDOW", self.cancel_click) # 根据父窗口来设置对话框的位置 self.geometry("+%d+%d" % (root.winfo_rootx() + 100, root.winfo_rooty() + 100)) # print(self.initial_focus) # 让对话框获取焦点 self.initial_focus.focus_set() self.wait_window(self) def init_widgets(self): frame1 = tkinter.Frame(self) explainLabel = tkinter.Label(frame1, text=self.explain, font=("song", 12)) explainLabel.pack(padx=50, pady=5) frame1.pack(padx=5, pady=5) frame2 = tkinter.Frame(self) nrow=1 for i in range(len(self.paraLists)): nrow = i + 3 for j in range(len(self.paraLists[i])): self.names['ev' + str(i) + str(j)] = tkinter.StringVar() self.names['e' + str(i) + str(j)] = tkinter.Entry(frame2, textvariable=self.names['ev' + str(i) + str(j)],width=10) self.names['ev' + str(i) + str(j)].set(self.paraLists[i][j]) self.names['e' + str(i) + str(j)].grid(row=nrow, column=j) b1 = tkinter.Button(frame2, text='确定退出', command=self.setPara) b1.grid(row=nrow + 1, column=1) frame2.pack(padx=5, pady=6) return b1 def setPara(self): # 通过对话框给参数赋值 for i in range(len(self.paraLists)): for j in range(len(self.paraLists[i])): text0 = self.names['ev' + str(i) + str(j)].get() if not self.on_validate(text0): # 如果不能通过校验,让用户重新输入 showwarning(title='警告', message='必须输入数字') self.names['ev' + str(i) + str(j)].set(self.paraLists[i][j]) self.names['e' + str(i) + str(j)].focus_set() return else: self.paraLists[i][j] = float(text0) self.hide() def on_validate(self,content): # 该方法可对用户输入的数据进行校验,保证输入的是数字 for i in range(len(content) - 1, -1, -1): if not (48 <= ord(content[i]) <= 57 or content[i] == "." or content[i] == "+" or content[i] == "-"): return False return True def cancel_click(self, event=None): showwarning(title='警告', message='必须先设置参数') self.initial_focus.focus_set() def hide(self): self.withdraw() self.update_idletasks() # 将焦点返回给父窗口 self.root.focus_set() # 销毁自己 self.destroy() self.root.update() self.root.deiconify() self.root.focus_set() class listWindow(tkinter.Toplevel): # 下拉列表窗口,用对参数设置 def __init__(self, root, title = None, paraLists=[]): # Constructor self.root = root self.paraLists=paraLists tkinter.Toplevel.__init__(self,root) if title: self.title(title) # 创建对话框的主体内容 # 调用init_widgets方法来初始化对话框界面 self.initial_focus = self.init_widgets(self) # 根据modal选项设置是否为模式对话框 self.grab_set() # 为"WM_DELETE_WINDOW"协议使用self.cancel_click事件处理方法 self.protocol("WM_DELETE_WINDOW", self.cancel_click) # 根据父窗口来设置对话框的位置 self.geometry("+%d+%d" % (root.winfo_rootx() + 100, root.winfo_rooty() + 100)) # print(self.initial_focus) # 让对话框获取焦点 self.initial_focus.focus_set() self.wait_window(self) def init_widgets(self,master): str0 = self.paraLists[0][0] labelMessage = tkinter.Label(master, text=self.paraLists[0][0], font=("song", 12)) labelMessage.grid(row=0, column=0) self.listBox = tkinter.Listbox(master,selectmode=tkinter.SINGLE) self.listBox.grid(row=1, column=0) lists=self.paraLists[0][1] for item in lists: self.listBox.insert(tkinter.END, item) self.listBox.select_set(0) b1 = tkinter.Button(master, text='确定退出', command=self.setPara) b1.grid(row=2, column=0) self.bind("", self.setPara) self.bind("", self.cancel_click) return self.listBox def cancel_click(self, event=None): showwarning(title='警告', message='必须先设置参数') self.initial_focus.focus_set() def setPara(self): self.returnValue=self.listBox.curselection()[0] self.hide() def hide(self): self.withdraw() self.update_idletasks() # 将焦点返回给父窗口 self.root.focus_set() # 销毁自己 self.destroy() self.root.update() self.root.deiconify() self.root.focus_set() # ---------------------------------------------------------------------- if __name__ == "__main__": root = tk.Tk() root.geometry("1000x640+150+5") # 界面大小,以相对屏幕的坐标 root.resizable(0, 0) myWindow = ImageApp(root) root.mainloop()