PyQt/QGraphicsView/WorldMap.py

167 lines
10 KiB
Python
Raw Normal View History

2017-12-17 14:13:13 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
2018-12-28 20:50:07 +08:00
"""
2017-12-17 14:13:13 +08:00
Created on 2017年12月17日
@author: Irony."[讽刺]
2018-12-28 20:50:07 +08:00
@site: https://pyqt5.com https://github.com/892768447
2017-12-17 14:13:13 +08:00
@email: 892768447@qq.com
@file: WorldMap
@description:
2018-12-28 20:50:07 +08:00
"""
2017-12-17 14:13:13 +08:00
import json
import math
from PyQt5.QtCore import Qt, QPointF, QRectF
from PyQt5.QtGui import QColor, QPainter, QPolygonF, QPen, QBrush
from PyQt5.QtOpenGL import QGLFormat
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPolygonItem
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
__Version__ = "Version 1.0"
class GraphicsView(QGraphicsView):
# 背景区域颜色
backgroundColor = QColor(31, 31, 47)
# 边框颜色
borderColor = QColor(58, 58, 90)
def __init__(self, *args, **kwargs):
super(GraphicsView, self).__init__(*args, **kwargs)
self.resize(800, 600)
# 设置背景颜色
self.setBackgroundBrush(self.backgroundColor)
'''
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#CacheModeFlag-enum
CacheNone 不使用缓存
CacheBackground 缓存背景
'''
self.setCacheMode(self.CacheBackground)
'''
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#DragMode-enum
NoDrag 什么都没发生; 鼠标事件被忽略
ScrollHandDrag 光标变成指针拖动鼠标将滚动滚动条 该模式可以在交互式和非交互式模式下工作
RubberBandDrag 拖动鼠标将设置橡皮筋几何形状并选择橡皮筋所覆盖的所有项目 对于非交互式视图此模式被禁用
'''
self.setDragMode(self.ScrollHandDrag)
'''
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#OptimizationFlag-enum
DontClipPainter 已过时
DontSavePainterState 渲染时QGraphicsView在渲染背景或前景时以及渲染每个项目时保护painter状态请参阅QPainter.save() 这允许你离开painter处于改变状态即你可以调用QPainter.setPen()或QPainter.setBrush()而不需要在绘制之后恢复状态 但是如果项目一致地恢复状态则应启用此标志以防止QGraphicsView执行相同的操作
DontAdjustForAntialiasing 禁用QGraphicsView的抗锯齿自动调整曝光区域 在QGraphicsItem.boundingRect()的边界上渲染反锯齿线的项目可能会导致渲染部分线外 为了防止渲染失真QGraphicsView在所有方向上将所有曝光区域扩展2个像素 如果启用此标志QGraphicsView将不再执行这些调整最大限度地减少需要重绘的区域从而提高性能 一个常见的副作用是使用抗锯齿功能进行绘制的项目可能会在移动时在画面上留下绘画痕迹
IndirectPainting 从Qt 4.6开始恢复调用QGraphicsView.drawItems()和QGraphicsScene.drawItems()的旧绘画算法 仅用于与旧代码的兼容性
'''
self.setOptimizationFlag(self.DontSavePainterState)
'''
#参考 http://doc.qt.io/qt-5/qpainter.html#RenderHint-enum
Antialiasing 抗锯齿
TextAntialiasing 文本抗锯齿
SmoothPixmapTransform 平滑像素变换算法
HighQualityAntialiasing 请改用Antialiasing
NonCosmeticDefaultPen 已过时
Qt4CompatiblePainting 从Qt4移植到Qt5可能有用
'''
self.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing |
QPainter.SmoothPixmapTransform)
if QGLFormat.hasOpenGL():
self.setRenderHint(QPainter.HighQualityAntialiasing)
'''
#当视图被调整大小时,视图如何定位场景。使用这个属性来决定当视口控件的大小改变时,如何在视口中定位场景。 缺省行为NoAnchor在调整大小的过程中不改变场景的位置; 视图的左上角将显示为在调整大小时被锚定。请注意,只有场景的一部分可见(即有滚动条时),此属性的效果才明显。 否则如果整个场景适合视图QGraphicsScene使用视图对齐将视景中的场景定位。
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#ViewportAnchor-enum
NoAnchor 视图保持场景的位置不变
AnchorViewCenter 视图中心被用作锚点
AnchorUnderMouse 鼠标当前位置被用作锚点
'''
self.setResizeAnchor(self.AnchorUnderMouse)
'''
Rubber选择模式
#参考 http://doc.qt.io/qt-5/qt.html#ItemSelectionMode-enum
ContainsItemShape 输出列表仅包含形状完全包含在选择区域内的项目 不包括与区域轮廓相交的项目
IntersectsItemShape 默认输出列表包含其形状完全包含在选择区域内的项目以及与区域轮廓相交的项目
ContainsItemBoundingRect 输出列表仅包含边界矩形完全包含在选择区域内的项目 不包括与区域轮廓相交的项目
IntersectsItemBoundingRect 输出列表包含边界矩形完全包含在选择区域内的项目以及与区域轮廓相交的项目 这种方法通常用于确定需要重绘的区域
'''
self.setRubberBandSelectionMode(Qt.IntersectsItemShape)
'''
#在转换过程中如何定位视图。QGraphicsView使用这个属性决定当变换矩阵改变时如何在视口中定位场景并且视图的坐标系被变换。 默认行为AnchorViewCenter确保在视图中心的场景点在变换过程中保持不变例如在旋转时场景将围绕视图的中心旋转。请注意只有场景的一部分可见即有滚动条时此属性的效果才明显。 否则如果整个场景适合视图QGraphicsScene使用视图对齐将视景中的场景定位。
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#ViewportAnchor-enum
NoAnchor 视图保持场景的位置不变
AnchorViewCenter 视图中心被用作锚点
AnchorUnderMouse 鼠标当前位置被用作锚点
'''
self.setTransformationAnchor(self.AnchorUnderMouse)
# if QGLFormat.hasOpenGL(): # 如果开启了OpenGL则使用OpenGL Widget
# self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
'''
#参考 http://doc.qt.io/qt-5/qgraphicsview.html#ViewportUpdateMode-enum
FullViewportUpdate 当场景的任何可见部分改变或重新显示时QGraphicsView将更新整个视口 当QGraphicsView花费更多的时间来计算绘制的内容比如重复更新很多小项目这种方法是最快的 这是不支持部分更新如QGLWidget的视口以及需要禁用滚动优化的视口的首选更新模式
MinimalViewportUpdate QGraphicsView将确定需要重绘的最小视口区域通过避免重绘未改变的区域来最小化绘图时间 这是QGraphicsView的默认模式 虽然这种方法提供了一般的最佳性能但如果场景中有很多小的可见变化QGraphicsView最终可能花费更多的时间来寻找最小化的方法
SmartViewportUpdate QGraphicsView将尝试通过分析需要重绘的区域来找到最佳的更新模式
BoundingRectViewportUpdate 视口中所有更改的边界矩形将被重绘 这种模式的优点是QGraphicsView只搜索一个区域的变化最大限度地减少了花在确定需要重绘的时间 缺点是还没有改变的地方也需要重新绘制
NoViewportUpdate 当场景改变时QGraphicsView将永远不会更新它的视口 预计用户将控制所有更新 此模式禁用QGraphicsView中的所有可能较慢项目可见性测试适用于要求固定帧速率或视口以其他方式在外部进行更新的场景
'''
self.setViewportUpdateMode(self.SmartViewportUpdate)
# 设置场景(根据地图的经纬度,并让原点显示在屏幕中间)
self._scene = QGraphicsScene(-180, -90, 360, 180, self)
self.setScene(self._scene)
# 初始化地图
self.initMap()
def wheelEvent(self, event):
# 滑轮事件
if event.modifiers() & Qt.ControlModifier:
self.scaleView(math.pow(2.0, -event.angleDelta().y() / 240.0))
return event.accept()
super(GraphicsView, self).wheelEvent(event)
def scaleView(self, scaleFactor):
factor = self.transform().scale(
scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width()
if factor < 0.07 or factor > 100:
return
self.scale(scaleFactor, scaleFactor)
def initMap(self):
features = json.load(
2018-12-28 20:50:07 +08:00
open("Data/world.json", encoding="utf8")).get("features")
2017-12-17 14:13:13 +08:00
for feature in features:
geometry = feature.get("geometry")
if not geometry:
continue
_type = geometry.get("type")
coordinates = geometry.get("coordinates")
for coordinate in coordinates:
if _type == "Polygon":
polygon = QPolygonF(
[QPointF(latitude, -longitude) for latitude, longitude in coordinate])
item = QGraphicsPolygonItem(polygon)
item.setPen(QPen(self.borderColor, 0))
item.setBrush(QBrush(self.backgroundColor))
item.setPos(0, 0)
self._scene.addItem(item)
elif _type == "MultiPolygon":
for _coordinate in coordinate:
polygon = QPolygonF(
[QPointF(latitude, -longitude) for latitude, longitude in _coordinate])
item = QGraphicsPolygonItem(polygon)
item.setPen(QPen(self.borderColor, 0))
item.setBrush(QBrush(self.backgroundColor))
item.setPos(0, 0)
self._scene.addItem(item)
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
print("OpenGL Status:", QGLFormat.hasOpenGL())
view = GraphicsView()
view.setWindowTitle("世界地图")
view.show()
sys.exit(app.exec_())