最近一周写了两个类,第一个类是用Python实现一个渐进色填充图片,对任意一条与x轴围成一个闭合曲面的曲线都可以实现渐进色填充。第二个类就是方便跑大程序的时候自动监督程序,然后程序跑完之后得到的数据自动压缩成一个压缩包,然后自动发送到自己的邮箱上去。具体的效果以及部分核心代码等有空的时候进行补充,基本上写这些类就是给自己图一个方便。先把渐进色填充的目标图片发出来。
渐进色填充代码已经完美实现了,同时还实现了一些调参的功能,能够直观的滑动参数条现实对应的图像,等今天下午更新一下。现在先把自动发邮件的代码附上,同时稍微解释一下其怎么实现的,代码如下:
from email.mime.text import MIMEText
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import zipfile
import smtplib
import os
import datetime
class Email:
"""from_mail: 发送者的邮箱 (字符串形式)mail_pass: 发送者邮箱的授权码 (字符串形式)注意:授权码不是登录密码,需要自己在邮箱开启,可以百度邮箱授权码to_mail: 收件人的邮箱 (列表类似,[字符串1,字符串2,...],每个字符串都是一个邮箱)dir_path: 需要压缩的文件夹路径 (字符串形式,路径是用这种/斜杠)outFullName: 压缩文件保存的路径+压缩文件名称(如Name.rar) (字符串形式)content: 邮件正文内容 (字符串形式)topic: 邮件主题 (字符串形式)"""
def __init__(self, from_mail, mail_pass, to_mail, dir_path, outFullName, content, topic):
self.from_mail = from_mail
self.mail_pass = mail_pass
self.to_mail = to_mail
self.dir_path = dir_path
self.outFullName = outFullName
self.content = content
self.topic = topic
self.zip_dir()
self.send_mail()
def zip_dir(self):
"""压缩指定文件夹"""
testcase_zip = zipfile.ZipFile(self.outFullName, 'w', zipfile.ZIP_DEFLATED)
for path, dir_names, file_names in os.walk(self.dir_path):
for filename in file_names:
testcase_zip.write(os.path.join(path, filename))
testcase_zip.close()
print("文件夹压缩成功")
def set_content(self):
path = self.outFullName
each_rar = path[path.rfind('/') + 1:]
if 'qq' in self.from_mail:
smtp_server = 'smtp.qq.com'
else:
smtp_server = 'smtp.163.com'
msg = MIMEMultipart()
msg['From'] = self.from_mail
msg['To'] = ','.join(self.to_mail)
msg['Subject'] = Header(self.topic, 'utf-8').encode()
msg.attach(MIMEText(self.content, 'plain', 'utf-8'))
with open(each_rar, 'rb') as f:
mime = MIMEBase('rar', 'rar', filename=each_rar)
mime.add_header('Content-Disposition', 'attachment', filename=each_rar)
mime.set_payload(f.read())
encoders.encode_base64(mime)
msg.attach(mime)
return msg, smtp_server
def send_mail(self):
msg, smep_server = self.set_content()
try:
s = smtplib.SMTP()
s.connect(smep_server, "25")
s.login(self.from_mail, self.mail_pass)
s.sendmail(self.from_mail, self.to_mail, msg.as_string())
s.quit()
print('邮件发送成功!')
except smtplib.SMTPException as e:
print("Error:%s" %e)
这里面所有的包都是Python自带的,无需额外下载,因此用起来也十分方便。其中几个变量的含义我已经在代码中注释的很清楚了,无需解释,先是定义了几个函数,一个是压缩文件夹的函数:rar_dir,这里我是压缩成rar格式,当然你只要把压缩包后缀取成其它的那就会变成其它的格式,看自己喜好吧!然后第二个函数是设置邮件的正文(包括邮件主题,内容,附件等)(这里附件地址最后放在跟我这里py文件同一个目录下,因为里面的open函数好像只能在同目录下找文件),这里我仅仅给了两种发件人的形式:一种是163邮箱,还有一个是qq邮箱,在这里我主要是用163邮箱来发送给我的qq邮箱的,里面一些自带的包上的函数具体功能可自信百度,不难,同时我们只需要会用就ok,最后一个函数就是发送邮件,这里有一点要注意的就是我们的email_pass不是邮箱的密码,而是授权码,需要去相应的邮箱设置,操作很简单,还有在这里经常碰见的问题就是,我的邮件老是被退回,原因很简单,就是老师发送同一个主题和同一个正文往同一个邮箱发送时,很可能会被当成垃圾邮件,然后被退回去,所以可以提一个建议,就是去整一个自动输出一串文字的代码,每次运行得到的连贯的文字是不一样的,这样可能就不会出现这个问题了,稍微演示一下成功的示例:
f_e = '*********@163.com'
e_p = '*****************'
t_e = ['********@qq.com']
d_p = 'C:/Users/Young/Desktop/picture'
o_p = 'E:/Pycharm/Python_text/exercise/test.rar'
c_t = '时间{}程序已经跑完,现在将所得数据以邮件形式发送给你,请接收!!!'.format(str(datetime.datetime.today()))
t_p = 'fisher_information_test'
Email(f_e, e_p, t_e, d_p, o_p, c_t, t_p)
然后基本上没啥延迟就给我的qq发送了过去。
综合看起来还是满成功的,唯一不足的就是当我跑程序的电脑断网时,就发不出去了,可能我还需要一个服务器。接下来就是介绍一下再反复修改了多次,以及多次优化,以及为了实现画渐变图的普适性的代码,花了我大概有半周至一周的时间,总感觉是在不务正业了,好了废话少说,先把代码整上去:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider, RadioButtons
import pandas as pd
import os
import shutil
class Drawing:
def __init__(self, g, a, N, site):
self.g = g #渐变色程度
self.a = a #透明度程度
self.N = N #数据扩充倍数
self.site = site #导入数据的地址
#self.main()
def divide_data(self):
df = pd.read_excel(self.site)
data = np.array(df)
num_of_curve = int(data.shape[1] / 2)
return data, num_of_curve
def creat_file(self):
site = self.site
last_str = '/'
file_name = 'picture'
new_site = site[: site.rfind(last_str) + 1] + file_name
if os.path.exists(new_site):
shutil.rmtree(new_site)
os.mkdir(new_site)
return new_site
def create_data(self, xdata, ydata):
dx = xdata[1] - xdata[0]
new_dx = dx / self.N
new_xdata, new_ydata = [], []
for i in range(len(xdata)):
if i != len(xdata) - 1:
k_i = (ydata[i + 1] - ydata[i]) / dx
else:
k_i = (ydata[i] - ydata[i - 1]) / dx
for j in range(self.N):
new_xdata.append(xdata[i] + j * new_dx)
new_ydata.append(k_i * (new_xdata[i * self.N + j] - xdata[i]) + ydata[i])
return np.array(new_xdata), np.array(new_ydata)
def main(self):
xdata, ydata, xtot, ytot, im_tot, Z_tot = [], [], [], [], [], []
cmap = ['Reds', 'Blues', 'Greens', 'Purples', 'Oranges', 'PuBu', 'OrRd', 'YlGn', 'Greys']
color = [(213/255,34/255,33/255), (21/255,98/255,169/255), (13/255,120/255,53/255), (91/255,53/255,149/255), (211/255,69/255,1/255), (4/255,102/255,160/255), (205/255,34/255,22/255),
(26/255,125/255,64/255), (73/255,73/255,73/255)]
#file_cite = self.creat_file()
data, num = self.divide_data()
for n in range(num):
xdata.append(data[:, n * 2])
ydata.append(data[:, (2 * n + 1)])
xtot.append(min(xdata[n]))
xtot.append(max(xdata[n]))
ytot.append(min(ydata[n]))
ytot.append(max(ydata[n]))
xdata[n], ydata[n] = self.create_data(xdata[n], ydata[n])
Length = len(xdata[0])
X, Y = np.meshgrid(np.linspace(0, 1, Length), np.linspace(0, 1, 1000))
fig = plt.figure(figsize=(6, 5), dpi=150)
for n in range(num):
# 第一层,实现平面渐变
Z = Y ** self.g # 将渐变的平均效果弱化,即0.1**3=0.01,0.9**3=0.729,显然做到了将显著的渐变控制在最上面
for i in range(Length):
index = int(1000 * (ydata[n][i] - ydata[n].min()) / (ydata[n].max() - ydata[n].min())) + 1
Z[index:, i] = None
ax = plt.gca()
im = plt.imshow(Z, extent=[min(xdata[n]), max(xdata[n]), min(ydata[n]), max(ydata[n])], cmap=plt.get_cmap(cmap[n]),
origin='lower', alpha=self.a)
# 第二层,画曲线
ax.plot(xdata[n], ydata[n], color=color[n], label='line_{}'.format(n+1), linewidth=1.5)#linewidth线条粗细
ax.axis('auto')
#ax.set_aspect(0.08) #y轴长度/x轴长度=0.2
plt.xlim(min(xtot), max(xtot)) # 调节x轴的范围
plt.ylim(min(ytot), max(ytot)+0.05) # 调节y轴的范围
im_tot.append(im)
Z_tot.append(Z)
#site = file_cite + '/' + 'fig' + '_a=' + str(self.a) + '_g=' + str(self.a) + '.jpg'
plt.legend()
return fig, im_tot, Z_tot
def update(val):
for n in range(curve_number):
globals()["alpha_%s"%(n+1)] = globals()["sa_%s"%(n+1)].val
globals()["gradent_%s" % (n + 1)] = globals()["sg_%s" % (n + 1)].val
im[n].set_alpha(globals()['alpha_%s'%(n+1)])
im[n].set_data(Z[n] ** globals()['gradent_%s'%(n+1)])
alpha = [globals()['alpha_%s'%(n+1)] for n in range(curve_number)]
gradent = [globals()['gradent_%s'%(n+1)] for n in range(curve_number)]
print('alpha={}'.format(alpha), 'gradent={}'.format(gradent))
fig.canvas.draw_idle()
site = 'C:/Users/Young/Desktop/data1.xlsx'
Drawing = Drawing(1, 0, 5, site)
fig, im, Z = Drawing.main()
curve_number = len(im)
plt.subplots_adjust(bottom=0.35, left=0.3)
axcolor = 'lightgoldenrodyellow'
for n in range(curve_number):
vars()["g_%s"%(n+1)] = plt.axes([0.03, 0.5 + (n+1)*0.05, 0.15, 0.03], facecolor=axcolor)
vars()["sg_%s"%(n+1)] = Slider(vars()["g_%s"%(n+1)], r'$g_%s$'%(n+1), 0, 5, valinit=1)
vars()["a_%s"%(n+1)] = plt.axes([0.25, 0.1 + (n+1)*0.05, 0.65, 0.03], facecolor=axcolor)
vars()["sa_%s"%(n+1)] = Slider(vars()["a_%s"%(n+1)], r'$\alpha_%s$'%(n+1), 0, 1, valinit=.2)
vars()['sg_%s'%(n+1)].on_changed(update)
vars()['sa_%s'%(n+1)].on_changed(update)
plt.show()
我感觉这个代码的功能差不多是实现了,同时还写了个小算法来实现当数据点比较小的时候,可能画出来的渐变图效果不是很好,如下图:
是不是显然能看到红色之间有白色的区域,接下来当我将填充区域的数据(也是线条的数据)变得更加密集(整个5倍吧),看看效果如何:
现在在同样的参数下是不是就变得很顺滑了,实现这个功能的就是里面的一个函数create_data,算法很简单,就是顺着matplotlib画曲线图的意思着手,将原本每个横坐标的最小间隔是dx变成dx/N,然后按两点之间为一条直线,就可以得到纵轴的数据了。其中我的渐变填充是基于函数imshow才能实现的,通过这个函数先是在平面上铺上一层基于曲线的沿着y轴的等色渐变,当然也可以沿着x轴渐变,y=x轴渐变等等,完全看个人需要,只需要更改Z这个数据点即可,铺上之后画曲线即可,由于imshow渐变色的局限性,我只能提供大约9条曲线的不同色调,不过完全足够了,还需要注意的就是导入excel的数据形式应该全是一列一列的,同时如果有多条曲线,那么曲线1的x轴和y轴数据分别位于第一列和第二列,曲线2的x和y的数据位于第三列第四列,依次下去,所以excel的数据不要排列错了,同时代码中的非类的其它代码完全就是为了实现通过滑动条来调一些参数,然后根据对应参数所对应的图片,选取一组最好的参数,通过给定的参数去得到最合适的渐变图形,当然里面还有一些vars()和gloabls()函数,这是基于不知道到底有多少条曲线所自动定义的变量,显得功能上更加智能化吧,好了废话说了这么多,那我就来试一试渐变图的效果如何吧!!!
综合看起来效果并不是很差,而且参数的话自己还可以细调一下,可能效果更好,这就是这一两周不务正业搞出来的东西。