问题描述
昨天一直在搞pyecharts绘图和输出,网上搜了不少,坑太多了。。。最终我绘图的结果如下: 遇到的难点:(下面会逐一给出解法!完整代码涉及隐私,恕不提供!有问题请留言讨论)
- x轴和y轴的名称需要修改位置、字体、颜色
- 更改线条颜色
- x轴横坐标最小值与原点重合
- y轴的刻度需要自定义+控制格式
- x轴和y轴的副刻度需要自定义
- 曲线顶点处需要放置文字
- 需要输出pdf
环境
win10 + Python 3.9.12 + pyecharts 1.9.1 + snapshot_phantomjs 0.0.3
解决方案
以下直接略过数据准备阶段。已知 x_list是x的值列表,pts_list是y的值列表。
整体结构
代码的整体结构如下:
from pyecharts.charts import Line, Grid
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
def draw(file_name):
line = (
Line(init_opts=opts.InitOpts(height="600px", bg_color='rgb(255, 255, 255)'))
.add_xaxis(x_list)
.add_yaxis(pts_list)
.set_global_opts(
xaxis_opts=opts.AxisOpts(),
yaxis_opts=opts.AxisOpts(),
graphic_opts=[
opts.GraphicText(
graphic_item=opts.GraphicItem(),
graphic_textstyle_opts=opts.GraphicTextStyleOpts(),
),
],
)
)
line2 = (Line()
.add_xaxis(x_list2)
.set_global_opts(
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts()),
yaxis_opts=opts.AxisOpts(),
)
)
grid = Grid()
grid.add(line, grid_opts=opts.GridOpts(pos_left='12%'))
grid.add(line2, grid_opts=opts.GridOpts(pos_left='12%'))
x轴和y轴的名称需要修改位置、字体、颜色
下面以x轴举例。y轴是差不多的。
.set_global_opts(
xaxis_opts=opts.AxisOpts(
name="xxx",
min_=x_list[0],
max_=x_list[-1],
boundary_gap=False,
name_location='center',
name_gap=30,
name_textstyle_opts=opts.TextStyleOpts(
color='navy',
font_family='Courier New',
font_size=15,
),
axislabel_opts=opts.LabelOpts(
color='navy',
font_size=15,
font_family='Courier New',
interval=299,
),
更改线条颜色
.add_yaxis(y_axis=pts_list,series_name="",
is_symbol_show=False,
linestyle_opts=opts.LineStyleOpts(color="red", width=0.7),
label_opts=opts.LabelOpts(is_show=False),
)
x轴横坐标最小值与原点重合
boundary_gap=False,
参见上上节。
y轴的刻度需要自定义+控制格式
axislabel_opts=opts.LabelOpts(
color='navy',
font_size=15,
font_family='Courier New',
formatter=JsCode(
'''
function(value){
if (value == '0') {
return '0.0E+000';
} else {
return value + 'E+00' + 'TAIL';
}
}'''.replace('TAIL', postfix)
),
),
这里要说明一下formatter。JsCode实际是一段字符串,value表示的是某个刻度的值,是字符串类型,可以用console.log打出来看看。 怎么传递变量进去,我真的没搞懂,但是,用另一个常量代替待传递变量,然后replace掉,是可以实现我要的效果的。
x轴和y轴的副刻度需要自定义
因为line1的坐标轴刻度是跟着数据走的,为了多加一套副刻度,所以我另外写了一个line2,只用来控制副刻度,然后再把line1和line2叠加起来。这里副刻度需要控制的是interval。
line2 = (Line()
.add_xaxis(x_list2)
.set_global_opts(
...
yaxis_opts=opts.AxisOpts(
max_=max_y,
min_=0.0,
interval=y_step/ 4,
axislabel_opts=opts.LabelOpts(
color='transparent',
font_size=15,
font_family='Courier New',
),
axisline_opts=opts.AxisLineOpts(linestyle_opts=opts.LineStyleOpts(color='navy')),
),
)
)
曲线顶点处需要放置文字
graphic_opts=[
opts.GraphicText(
graphic_item=opts.GraphicItem(position=(455,40),rotation=0.5*pi),
graphic_textstyle_opts=opts.GraphicTextStyleOpts(
text=str(num),
font="13 Courier New",
graphic_basicstyle_opts=opts.GraphicBasicStyleOpts(fill='navy')
),
),
],
输出pdf
先上结论:使用pyecharts推荐的phantomjs是最好的。 不过,这种方法生成的pdf只有图像,没办法调节边距、图纸方向和大小等,一种解决方案是使用毒霸PDF转换器-图片转PDF,可以批量转换(测试时,200个png转pdf在3分钟以内完成)。当然,也有很多工具可以使用。 代码:
from pyecharts.render import make_snapshot
from snapshot_phantomjs import snapshot
make_snapshot(snapshot, grid.render(), "pdf/" + file_name + ".pdf")
make_snapshot(snapshot, grid.render(), "pdf/" + file_name + ".png")
除了需要pip安装一下snapshot_phantomjs外,还需要下载一下phantomjs-2.1.1-windows,将下图这个的路径放入系统环境变量Path,就是让程序调用时可以找到它。
html输出为pdf
另外,我还尝试了一些包,但是在将html打印为pdf时会出现以下几种情况:
- 打印出的pdf是空白的
- 能打印出坐标轴,但没有折线图像
这些包在打印标准格式的html时是ok的,所以下面将列出代码存档备用。
下面的包都是在anaconda中使用pip安装的,如果有问题自行搜素。
weasyprint
from weasyprint import HTML
HTML("7024.html").write_pdf("7024.pdf")
如果遇到找不到cairo/cairo2之类的库,很可能是因为没装gtk,装一个gtk2或者gtk3即可解决。
pdf_reports
https://github.com/Edinburgh-Genome-Foundry/pdf_reports/blob/master 这个包其实是基于weasyprint的。
from pdf_reports import write_report
write_report("html/7024.html", "example.pdf")
PyQt5
印象中这个是打印不出折线图像。坐标轴和文字附注是能打印出来的。
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import QMarginsF
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtGui import QPageLayout, QPageSize
import sys
app = QtWidgets.QApplication(sys.argv)
loader = QtWebEngineWidgets.QWebEngineView()
loader.load(QtCore.QUrl('file:///本地地址/7024.html'))
layout = QPageLayout(
QPageSize(QPageSize.A4),
QPageLayout.Portrait, QMarginsF(0, 0, 0, 0)
)
def printFinished():
page = loader.page()
print("%s Printing Finished!" % page.title())
app.exit()
def printToPDF(finished):
loader.show()
time.sleep(60)
page = loader.page()
page.printToPdf("E:\pdf%s.pdf" % page.title(), layout)
loader.page().pdfPrintingFinished.connect(printFinished)
loader.loadFinished.connect(printToPDF)
app.exec_()
pdfkit
下载wkhtmltopdf,然后放到系统环境变量Path。 https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_msvc2015-win64.exe
import pdfkit
pdfkit.from_file('test.html', 'out.pdf')
|