本文通过Python3+PyQt5实现《python Qt Gui 快速编程》这本书的page Designer应用程序,采用QGraphicsView,QGraphicsScene,QGraphicsItem,这个程序包含有多个文本,图片和框的页面。有些图形类在PyQt5已过时,所以本代码改动幅度比较大。主要的类或方法的改变如下:

QMatrix==>QTransform

setMatrix==>setTransform

rotate ==> setRotation

本例中,由于event.delta()已过时,还重写了wheelEvent方法:

def wheelEvent(self, event):

#factor = 1.41 ** (-event.delta() / 240.0)

#factor = 1.41 ** (-abs(event.startX()-event.y()) / 240.0)

factor = event.angleDelta().y()/120.0

if event.angleDelta().y()/120.0 > 0:

factor=2

else:

factor=0.5

self.scale(factor, factor)

为了保持代码可读行,增加了一个类:

class GraphicsPixmapItem(QGraphicsPixmapItem): #add by yangrongdong

def __init__(self,pixmap):

super(QGraphicsPixmapItem, self).__init__(pixmap)

本例中还有包含菜单的按钮:

if text == "&Align":

menu = QMenu(self)

for text, arg in (

("Align &Left", Qt.AlignLeft),

("Align &Right", Qt.AlignRight),

("Align &Top", Qt.AlignTop),

("Align &Bottom", Qt.AlignBottom)):

wrapper = functools.partial(self.setAlignment, arg)

self.wrapped.append(wrapper)

menu.addAction(text, wrapper)

button.setMenu(menu)

本例中还针对QStyleOptionGraphicsItem.levelOfDetail已过时,改写如下:

option.levelOfDetailFromTransform(self.transform())

下面为完整的代码:

#!/usr/bin/env python3

import functools

import random

import sys

from PyQt5.QtCore import (QByteArray, QDataStream, QFile, QFileInfo,

QIODevice, QPoint, QPointF, QRectF, Qt)

from PyQt5.QtWidgets import (QApplication, QDialog,

QDialogButtonBox, QFileDialog, QFontComboBox,

QGraphicsItem, QGraphicsPixmapItem,

QGraphicsScene, QGraphicsTextItem, QGraphicsView, QGridLayout,

QHBoxLayout, QLabel, QMenu, QMessageBox,QPushButton, QSpinBox,

QStyle, QTextEdit, QVBoxLayout)

from PyQt5.QtGui import QFont,QCursor,QFontMetrics,QTransform,QPainter,QPen,QPixmap

from PyQt5.QtPrintSupport import QPrinter,QPrintDialog

MAC = True

try:

from PyQt5.QtGui import qt_mac_set_native_menubar

except ImportError:

MAC = False

#PageSize = (595, 842) # A4 in points

PageSize = (612, 792) # US Letter in points

PointSize = 10

MagicNumber = 0x70616765

FileVersion = 1

Dirty = False

class TextItemDlg(QDialog):

def __init__(self, item=None, position=None, scene=None, parent=None):

super(QDialog, self).__init__(parent)

self.item = item

self.position = position

self.scene = scene

self.editor = QTextEdit()

self.editor.setAcceptRichText(False)

self.editor.setTabChangesFocus(True)

editorLabel = QLabel("&Text:")

editorLabel.setBuddy(self.editor)

self.fontComboBox = QFontComboBox()

self.fontComboBox.setCurrentFont(QFont("Times", PointSize))

fontLabel = QLabel("&Font:")

fontLabel.setBuddy(self.fontComboBox)

self.fontSpinBox = QSpinBox()

self.fontSpinBox.setAlignment(Qt.AlignRight|Qt.AlignVCenter)

self.fontSpinBox.setRange(6, 280)

self.fontSpinBox.setValue(PointSize)

fontSizeLabel = QLabel("&Size:")

fontSizeLabel.setBuddy(self.fontSpinBox)

self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|

QDialogButtonBox.Cancel)

self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)

if self.item is not None:

self.editor.setPlainText(self.item.toPlainText())

self.fontComboBox.setCurrentFont(self.item.font())

self.fontSpinBox.setValue(self.item.font().pointSize())

layout = QGridLayout()

layout.addWidget(editorLabel, 0, 0)

layout.addWidget(self.editor, 1, 0, 1, 6)

layout.addWidget(fontLabel, 2, 0)

layout.addWidget(self.fontComboBox, 2, 1, 1, 2)

layout.addWidget(fontSizeLabel, 2, 3)

layout.addWidget(self.fontSpinBox, 2, 4, 1, 2)

layout.addWidget(self.buttonBox, 3, 0, 1, 6)

self.setLayout(layout)

self.fontComboBox.currentFontChanged.connect(self.updateUi)

self.fontSpinBox.valueChanged.connect(self.updateUi)

self.editor.textChanged.connect(self.updateUi)

self.buttonBox.accepted.connect(self.accept)

self.buttonBox.rejected.connect(self.reject)

self.setWindowTitle("Page Designer - {0} Text Item".format(

"Add" if self.item is None else "Edit"))

self.updateUi()

def updateUi(self):

font = self.fontComboBox.currentFont()

font.setPointSize(self.fontSpinBox.value())

self.editor.document().setDefaultFont(font)

self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(

bool(self.editor.toPlainText()))

def accept(self):

if self.item is None:

self.item = TextItem("", self.position, self.scene)

font = self.fontComboBox.currentFont()

font.setPointSize(self.fontSpinBox.value())

self.item.setFont(font)

self.item.setPlainText(self.editor.toPlainText())

self.item.update()

global Dirty

Dirty = True

QDialog.accept(self)

class TextItem(QGraphicsTextItem):

def __init__(self, text, position, scene,

font=QFont("Times", PointSize), matrix=QTransform()):

super(TextItem, self).__init__(text)

self.setFlags(QGraphicsItem.ItemIsSelectable|

QGraphicsItem.ItemIsMovable)

self.setFont(font)

self.setPos(position)

self.setTransform(matrix)

scene.clearSelection()

scene.addItem(self)

self.setSelected(True)

global Dirty

Dirty = True

def parentWidget(self):

return self.scene().views()[0]

def itemChange(self, change, variant):

if change != QGraphicsItem.ItemSelectedChange:

global Dirty

Dirty = True

return QGraphicsTextItem.itemChange(self, change, variant)

def mouseDoubleClickEvent(self, event):

dialog = TextItemDlg(self, self.parentWidget())

dialog.exec_()

class GraphicsPixmapItem(QGraphicsPixmapItem): #add by yangrongdong

def __init__(self,pixmap):

super(QGraphicsPixmapItem, self).__init__(pixmap)

class BoxItem(QGraphicsItem):

def __init__(self, position, scene, style=Qt.SolidLine,

rect=None, matrix=QTransform()):

super(BoxItem, self).__init__()

self.setFlags(QGraphicsItem.ItemIsSelectable|

QGraphicsItem.ItemIsMovable|

QGraphicsItem.ItemIsFocusable)

if rect is None:

rect = QRectF(-10 * PointSize, -PointSize, 20 * PointSize,

2 * PointSize)

self.rect = rect

self.style = style

self.setPos(position)

self.setTransform(matrix)

scene.clearSelection()

scene.addItem(self)

self.setSelected(True)

self.setFocus()

global Dirty

Dirty = True

def parentWidget(self):

return self.scene().views()[0]

def boundingRect(self):

return self.rect.adjusted(-2, -2, 2, 2)

def paint(self, painter, option, widget):

pen = QPen(self.style)

pen.setColor(Qt.black)

pen.setWidth(1)

if option.state & QStyle.State_Selected:

pen.setColor(Qt.blue)

painter.setPen(pen)

painter.drawRect(self.rect)

def itemChange(self, change, variant):

if change != QGraphicsItem.ItemSelectedChange:

global Dirty

Dirty = True

return QGraphicsItem.itemChange(self, change, variant)

def contextMenuEvent(self, event):

wrapped = []

menu = QMenu(self.parentWidget())

for text, param in (

("&Solid", Qt.SolidLine),

("&Dashed", Qt.DashLine),

("D&otted", Qt.DotLine),

("D&ashDotted", Qt.DashDotLine),

("DashDo&tDotted", Qt.DashDotDotLine)):

wrapper = functools.partial(self.setStyle, param)

wrapped.append(wrapper)

menu.addAction(text, wrapper)

menu.exec_(event.screenPos())

def setStyle(self, style):

self.style = style

self.update()

global Dirty

Dirty = True

def keyPressEvent(self, event):

factor = PointSize / 4

changed = False

if event.modifiers() & Qt.ShiftModifier:

if event.key() == Qt.Key_Left:

self.rect.setRight(self.rect.right() - factor)

changed = True

elif event.key() == Qt.Key_Right:

self.rect.setRight(self.rect.right() + factor)

changed = True

elif event.key() == Qt.Key_Up:

self.rect.setBottom(self.rect.bottom() - factor)

changed = True

elif event.key() == Qt.Key_Down:

self.rect.setBottom(self.rect.bottom() + factor)

changed = True

if changed:

self.update()

global Dirty

Dirty = True

else:

QGraphicsItem.keyPressEvent(self, event)

class GraphicsView(QGraphicsView):

def __init__(self, parent=None):

super(GraphicsView, self).__init__(parent)

self.setDragMode(QGraphicsView.RubberBandDrag)

self.setRenderHint(QPainter.Antialiasing)

self.setRenderHint(QPainter.TextAntialiasing)

def wheelEvent(self, event):

#factor = 1.41 ** (-event.delta() / 240.0)

factor = event.angleDelta().y()/120.0

if event.angleDelta().y()/120.0 > 0:

factor=2

else:

factor=0.5

self.scale(factor, factor)

class MainForm(QDialog):

def __init__(self, parent=None):

super(MainForm, self).__init__(parent)

self.filename = ""

self.copiedItem = QByteArray()

self.pasteOffset = 5

self.prevPoint = QPoint()

self.addOffset = 5

self.borders = []

self.printer = QPrinter(QPrinter.HighResolution)

self.printer.setPageSize(QPrinter.Letter)

self.view = GraphicsView()

self.scene = QGraphicsScene(self)

self.scene.setSceneRect(0, 0, PageSize[0], PageSize[1])

self.addBorders()

self.view.setScene(self.scene)

self.wrapped = [] # Needed to keep wrappers alive

buttonLayout = QVBoxLayout()

for text, slot in (

("Add &Text", self.addText),

("Add &Box", self.addBox),

("Add Pi&xmap", self.addPixmap),

("&Align", None),

("&Copy", self.copy),

("C&ut", self.cut),

("&Paste", self.paste),

("&Delete...", self.delete),

("&Rotate", self.rotate),

("Pri&nt...", self.print_),

("&Open...", self.open),

("&Save", self.save),

("&Quit", self.accept)):

button = QPushButton(text)

if not MAC:

button.setFocusPolicy(Qt.NoFocus)

if slot is not None:

button.clicked.connect(slot)

if text == "&Align":

menu = QMenu(self)

for text, arg in (

("Align &Left", Qt.AlignLeft),

("Align &Right", Qt.AlignRight),

("Align &Top", Qt.AlignTop),

("Align &Bottom", Qt.AlignBottom)):

wrapper = functools.partial(self.setAlignment, arg)

self.wrapped.append(wrapper)

menu.addAction(text, wrapper)

button.setMenu(menu)

if text == "Pri&nt...":

buttonLayout.addStretch(5)

if text == "&Quit":

buttonLayout.addStretch(1)

buttonLayout.addWidget(button)

buttonLayout.addStretch()

layout = QHBoxLayout()

layout.addWidget(self.view, 1)

layout.addLayout(buttonLayout)

self.setLayout(layout)

fm = QFontMetrics(self.font())

self.resize(self.scene.width() + fm.width(" Delete... ") + 50,

self.scene.height() + 50)

self.setWindowTitle("Page Designer")

def addBorders(self):

self.borders = []

rect = QRectF(0, 0, PageSize[0], PageSize[1])

self.borders.append(self.scene.addRect(rect, Qt.yellow))

margin = 5.25 * PointSize

self.borders.append(self.scene.addRect(

rect.adjusted(margin, margin, -margin, -margin),

Qt.yellow))

def removeBorders(self):

while self.borders:

item = self.borders.pop()

self.scene.removeItem(item)

del item

def reject(self):

self.accept()

def accept(self):

self.offerSave()

QDialog.accept(self)

def offerSave(self):

if (Dirty and QMessageBox.question(self,

"Page Designer - Unsaved Changes",

"Save unsaved changes?",

QMessageBox.Yes|QMessageBox.No) ==

QMessageBox.Yes):

self.save()

def position(self):

point = self.mapFromGlobal(QCursor.pos())

if not self.view.geometry().contains(point):

coord = random.randint(36, 144)

point = QPoint(coord, coord)

else:

if point == self.prevPoint:

point += QPoint(self.addOffset, self.addOffset)

self.addOffset += 5

else:

self.addOffset = 5

self.prevPoint = point

return self.view.mapToScene(point)

def addText(self):

dialog = TextItemDlg(position=self.position(),

scene=self.scene, parent=self)

dialog.exec_()

def addBox(self):

BoxItem(self.position(), self.scene)

def addPixmap(self):

path = (QFileInfo(self.filename).path()

if self.filename else ".")

fname,filetype = QFileDialog.getOpenFileName(self,

"Page Designer - Add Pixmap", path,

"Pixmap Files (*.bmp *.jpg *.png *.xpm)")

if not fname:

return

self.createPixmapItem(QPixmap(fname), self.position())

def createPixmapItem(self, pixmap, position, matrix=QTransform()):

item = GraphicsPixmapItem(pixmap)

item.setFlags(QGraphicsItem.ItemIsSelectable|

QGraphicsItem.ItemIsMovable)

item.setPos(position)

item.setTransform(matrix)

self.scene.clearSelection()

self.scene.addItem(item)

item.setSelected(True)

global Dirty

Dirty = True

return item

def selectedItem(self):

items = self.scene.selectedItems()

if len(items) == 1:

return items[0]

return None

def copy(self):

item = self.selectedItem()

if item is None:

return

self.copiedItem.clear()

self.pasteOffset = 5

stream = QDataStream(self.copiedItem, QIODevice.WriteOnly)

self.writeItemToStream(stream, item)

def cut(self):

item = self.selectedItem()

if item is None:

return

self.copy()

self.scene.removeItem(item)

del item

def paste(self):

if self.copiedItem.isEmpty():

return

stream = QDataStream(self.copiedItem, QIODevice.ReadOnly)

self.readItemFromStream(stream, self.pasteOffset)

self.pasteOffset += 5

def setAlignment(self, alignment):

# Items are returned in arbitrary order

items = self.scene.selectedItems()

if len(items) <= 1:

return

# Gather coordinate data

leftXs, rightXs, topYs, bottomYs = [], [], [], []

for item in items:

rect = item.sceneBoundingRect()

leftXs.append(rect.x())

rightXs.append(rect.x() + rect.width())

topYs.append(rect.y())

bottomYs.append(rect.y() + rect.height())

# Perform alignment

if alignment == Qt.AlignLeft:

xAlignment = min(leftXs)

for i, item in enumerate(items):

item.moveBy(xAlignment - leftXs[i], 0)

elif alignment == Qt.AlignRight:

xAlignment = max(rightXs)

for i, item in enumerate(items):

item.moveBy(xAlignment - rightXs[i], 0)

elif alignment == Qt.AlignTop:

yAlignment = min(topYs)

for i, item in enumerate(items):

item.moveBy(0, yAlignment - topYs[i])

elif alignment == Qt.AlignBottom:

yAlignment = max(bottomYs)

for i, item in enumerate(items):

item.moveBy(0, yAlignment - bottomYs[i])

global Dirty

Dirty = True

def rotate(self):

for item in self.scene.selectedItems():

item.setRotation(item.rotation()+30)

def delete(self):

items = self.scene.selectedItems()

if (len(items) and QMessageBox.question(self,

"Page Designer - Delete",

"Delete {0} item{1}?".format(len(items),

"s" if len(items) != 1 else ""),

QMessageBox.Yes|QMessageBox.No) ==

QMessageBox.Yes):

while items:

item = items.pop()

self.scene.removeItem(item)

del item

global Dirty

Dirty = True

def print_(self):

dialog = QPrintDialog(self.printer)

if dialog.exec_():

painter = QPainter(self.printer)

painter.setRenderHint(QPainter.Antialiasing)

painter.setRenderHint(QPainter.TextAntialiasing)

self.scene.clearSelection()

self.removeBorders()

self.scene.render(painter)

self.addBorders()

def open(self):

self.offerSave()

path = (QFileInfo(self.filename).path()

if self.filename else ".")

fname,filetype = QFileDialog.getOpenFileName(self,

"Page Designer - Open", path,

"Page Designer Files (*.pgd)")

if not fname:

return

self.filename = fname

fh = None

try:

fh = QFile(self.filename)

if not fh.open(QIODevice.ReadOnly):

raise IOError(str(fh.errorString()))

items = self.scene.items()

while items:

item = items.pop()

self.scene.removeItem(item)

del item

self.addBorders()

stream = QDataStream(fh)

stream.setVersion(QDataStream.Qt_5_7)

magic = stream.readInt32()

if magic != MagicNumber:

raise IOError("not a valid .pgd file")

fileVersion = stream.readInt16()

if fileVersion != FileVersion:

raise IOError("unrecognised .pgd file version")

while not fh.atEnd():

self.readItemFromStream(stream)

except IOError as e:

QMessageBox.warning(self, "Page Designer -- Open Error",

"Failed to open {0}: {1}".format(self.filename, e))

finally:

if fh is not None:

fh.close()

global Dirty

Dirty = False

def save(self):

if not self.filename:

path = "."

fname,filetype = QFileDialog.getSaveFileName(self,

"Page Designer - Save As", path,

"Page Designer Files (*.pgd)")

if not fname:

return

self.filename = fname

fh = None

try:

fh = QFile(self.filename)

if not fh.open(QIODevice.WriteOnly):

raise IOError(str(fh.errorString()))

self.scene.clearSelection()

stream = QDataStream(fh)

stream.setVersion(QDataStream.Qt_5_7)

stream.writeInt32(MagicNumber)

stream.writeInt16(FileVersion)

for item in self.scene.items():

self.writeItemToStream(stream, item)

except IOError as e:

QMessageBox.warning(self, "Page Designer -- Save Error",

"Failed to save {0}: {1}".format(self.filename, e))

finally:

if fh is not None:

fh.close()

global Dirty

Dirty = False

def readItemFromStream(self, stream, offset=0):

type = ""

position = QPointF()

matrix = QTransform()

rotateangle=0#add by yangrongdong

type=stream.readQString()

stream >> position >> matrix

if offset:

position += QPointF(offset, offset)

if type == "Text":

text = ""

font = QFont()

text=stream.readQString()

stream >> font

rotateangle=stream.readFloat()

tx=TextItem(text, position, self.scene, font, matrix)

tx.setRotation(rotateangle)

elif type == "Box":

rect = QRectF()

stream >> rect

style = Qt.PenStyle(stream.readInt16())

rotateangle=stream.readFloat()

bx=BoxItem(position, self.scene, style, rect, matrix)

bx.setRotation(rotateangle)

elif type == "Pixmap":

pixmap = QPixmap()

stream >> pixmap

rotateangle=stream.readFloat()

px=self.createPixmapItem(pixmap, position, matrix)

px.setRotation(rotateangle)

def writeItemToStream(self, stream, item):

if isinstance(item, TextItem):

stream.writeQString("Text")

stream<

stream.writeQString(item.toPlainText())

stream<< item.font()

stream.writeFloat(item.rotation())#add by yangrongdong

elif isinstance(item, GraphicsPixmapItem):

stream.writeQString("Pixmap")

stream << item.pos() << item.transform() << item.pixmap()

stream.writeFloat(item.rotation())#add by yangrongdong

elif isinstance(item, BoxItem):

stream.writeQString("Box")

stream<< item.pos() << item.transform() << item.rect

stream.writeInt16(item.style)

stream.writeFloat(item.rotation())#add by yangrongdong

app = QApplication(sys.argv)

form = MainForm()

rect = QApplication.desktop().availableGeometry()

form.resize(int(rect.width() * 0.6), int(rect.height() * 0.9))

form.show()

app.exec_()

运行结果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

python3应用程序代码_python3+PyQt5图形项的自定义和交互 python3实现page Designer应用程序...相关推荐

  1. python3爬虫小型代码_python3简单爬虫实现代码

    分享一个python实现的网络爬虫代码.转自:http://www.cnblogs.com/yes123/p/3761388.html 写了个python3的.代码非常简单就不解释了,直接贴代码. # ...

  2. python3结果窗口打开_python3+PyQt5 使用三种不同的简便项窗口部件显示数据的方法...

    本文通过将同一个数据集在三种不同的简便项窗口部件中显示.三个窗口的数据得到实时的同步,数据和视图分离.当添加或删除数据行,三个不同的视图均保持同步.数据将保存在本地文件中,而非数据库.对于小型和临时性 ...

  3. python3 gui tk代码_python3 修改计算机名称GUI程序

    from tkinter import * from tkinter import messagebox import tkinter import winreg import os def serc ...

  4. python3图片转代码_python3图片转换二进制存入mysql示例代码

    python3图片转换二进制存入mysql示例代码 发布于 2014-09-29 18:00:01 | 198 次阅读 | 评论: 0 | 来源: 网友投递 Python编程语言Python 是一种面 ...

  5. python3实例车代码_python3 爬取汽车之家所有车型数据操作步骤(更新版)

    题记: 互联网上关于使用python3去爬取汽车之家的汽车数据(主要是汽车基本参数,配置参数,颜色参数,内饰参数)的教程已经非常多了,但大体的方案分两种: 1.解析出汽车之家某个车型的网页,然后正则表 ...

  6. 如何阅读他人的程序代码

    近日,在互联网上游荡,偶然发现一篇曾经的文章,是关于如何阅读他人程序代码的,阅后颇为受益,于是乎重新整理了一下格式,将此文转载如下: 如何阅读他人的程序代码 文/王建兴 作者简介: 王建兴,清华大学资 ...

  7. qt获取当前场景中的所有图形项的层次

    提要 场景中有多个窗口,窗口处于不断更新中,获取场景中当前所有窗口的图层.由于特定的结构,这里场景中的窗口采用自定义窗口,将该自定义窗口作为自定义代理窗口的窗口,图形项作为自定义代理窗口的父窗口,再将 ...

  8. hikvision v2.3控件网页demo_《快速掌握PyQt5》第三十章 网页交互QWebEngineView

    如果需要在程序中加载并显示网页,那QWebEngineView绝对是最佳的选择.该控件基于Chrome浏览器内核引擎,所提供的功能和方法还是比较强大的. 注:V5.11及更高版本的PyQt5中不包含Q ...

  9. pycharm+python3.7+pyqt配置_Python3+Pycharm+PyQt5环境搭建步骤图文详解

    搭建环境: 操作系统:Win10 64bit Python版本:3.7 Pycharm:社区免费版 一.Python3.7安装 下载到安装包后打开,如果想安装到默认路径(C盘)的话一直点下一步就可以了 ...

最新文章

  1. Android应用打开外部文件
  2. ROS Hotspot服务器的搭建与设定!(上网认证)
  3. 实战项目五:抓取简书文章信息
  4. 基于.NET下的人工智能系列专题|.NET下的人工智能系列专题|用Keras.NET 做一个图像识别的训练...
  5. Intel 5400平台 芯片组
  6. java 注解: Annotation
  7. IE下AjaxForm上传文件直接提示下载的兼容性Bug
  8. 企业wiki之confluence安装部署(linux)及其破解
  9. 2020美赛B题题目及解题思路
  10. 字模提取软件的使用(pctolCD2002,基于FPGA的VGA显示汉字)
  11. 联想小新电脑摄像头黑屏、检测不到设备、指示灯不亮解决方案
  12. 复化梯形公式matlab程序,复化梯形公式,辛普森公式的matlab程序
  13. CAD查看器软件的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  14. unity SteamVR利用手柄合理移动
  15. 为什么刹车热了会失灵_急!我的刹车为什么突然不管用了?
  16. DICOM:Transfer Syntax传输语义之奇葩GE Private TS
  17. SpringBoot + Excel 读写技术与实际问题解决
  18. 微信编辑器都有什么功能?
  19. Contacts(CNContactStore)
  20. 如何更改PDF阅读器的阅读模式?

热门文章

  1. UBNT ex-r +netgear gs105e v2 +ap 设置vlan 步骤记录 及相关知识整理
  2. 设置eclipse代码自动补全功能
  3. osm数据下载 python_用Python解锁处理OSM数据的全部姿势
  4. Bootstraptable源码
  5. 【转】ASP.NET MVC实现权限控制
  6. final finally finalize 区别
  7. xml文件的三种解析方式 DOM SAM PULL
  8. [转]加密技术在企业数据安全中的应用
  9. 【重识 HTML + CSS】CSS 特性
  10. windows下数据库mysql8.0安装