├── code ├── __init__.py ├── model │ ├── __init__.py │ ├── .DS_Store │ ├── __pycache__ │ │ ├── SqlGis.cpython-35.pyc │ │ ├── DrawTable.cpython-35.pyc │ │ ├── DrawTree.cpython-35.pyc │ │ ├── __init__.cpython-35.pyc │ │ └── mplCodeWrapper.cpython-35.pyc │ ├── DrawTable.py │ ├── pyqt_QThread.py │ ├── DrawTree.py │ ├── SqlGis.py │ └── mplCodeWrapper.py ├── view │ ├── __init__.py │ ├── .DS_Store │ ├── __pycache__ │ │ ├── Ui_main.cpython-35.pyc │ │ ├── Ui_history.cpython-35.pyc │ │ ├── __init__.cpython-35.pyc │ │ ├── Ui_analysis.cpython-35.pyc │ │ └── Ui_information.cpython-35.pyc │ ├── Ui_information.py │ ├── Ui_analysis.py │ ├── Ui_history.py │ └── Ui_main.py ├── controller │ ├── __init__.py │ ├── .DS_Store │ ├── __pycache__ │ │ ├── main.cpython-35.pyc │ │ ├── player.cpython-35.pyc │ │ ├── __init__.cpython-35.pyc │ │ ├── analysis.cpython-35.pyc │ │ ├── history.cpython-35.pyc │ │ ├── information.cpython-35.pyc │ │ └── mplCodeWrapper.cpython-35.pyc │ ├── analysis.py │ ├── information.py │ ├── main.py │ ├── history.py │ └── player.py ├── .DS_Store └── start.py ├── .DS_Store ├── data ├── .DS_Store └── water_sensing.db ├── README.md └── 模块分析.txt /code/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/model/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/view/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /code/controller/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/.DS_Store -------------------------------------------------------------------------------- /code/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/.DS_Store -------------------------------------------------------------------------------- /data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/data/.DS_Store -------------------------------------------------------------------------------- /code/view/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/view/.DS_Store -------------------------------------------------------------------------------- /code/model/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/model/.DS_Store -------------------------------------------------------------------------------- /data/water_sensing.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/data/water_sensing.db -------------------------------------------------------------------------------- /code/controller/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/.DS_Store -------------------------------------------------------------------------------- /code/model/__pycache__/SqlGis.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/model/__pycache__/SqlGis.cpython-35.pyc -------------------------------------------------------------------------------- /code/start.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from controller import main 4 | 5 | 6 | if __name__ == "__main__": 7 | main.main() -------------------------------------------------------------------------------- /code/view/__pycache__/Ui_main.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/view/__pycache__/Ui_main.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/main.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/main.cpython-35.pyc -------------------------------------------------------------------------------- /code/model/__pycache__/DrawTable.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/model/__pycache__/DrawTable.cpython-35.pyc -------------------------------------------------------------------------------- /code/model/__pycache__/DrawTree.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/model/__pycache__/DrawTree.cpython-35.pyc -------------------------------------------------------------------------------- /code/model/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/model/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /code/view/__pycache__/Ui_history.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/view/__pycache__/Ui_history.cpython-35.pyc -------------------------------------------------------------------------------- /code/view/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/view/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/player.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/player.cpython-35.pyc -------------------------------------------------------------------------------- /code/view/__pycache__/Ui_analysis.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/view/__pycache__/Ui_analysis.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/analysis.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/analysis.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/history.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/history.cpython-35.pyc -------------------------------------------------------------------------------- /code/model/__pycache__/mplCodeWrapper.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/model/__pycache__/mplCodeWrapper.cpython-35.pyc -------------------------------------------------------------------------------- /code/view/__pycache__/Ui_information.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/view/__pycache__/Ui_information.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/information.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/information.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/__pycache__/mplCodeWrapper.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weather319/gdal_UI/HEAD/code/controller/__pycache__/mplCodeWrapper.cpython-35.pyc -------------------------------------------------------------------------------- /code/controller/analysis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | import sys 4 | sys.path.append("..") 5 | from view.Ui_analysis import Ui_Analysis 6 | 7 | class analysiswindow(QtWidgets.QWidget): 8 | def __init__(self): 9 | super(analysiswindow,self).__init__() 10 | self.view=Ui_Analysis() 11 | self.view.setupUi(self) 12 | 13 | 14 | 15 | if __name__ == "__main__": 16 | app = QtWidgets.QApplication(sys.argv) 17 | analysis = analysiswindow() 18 | app.setQuitOnLastWindowClosed(True) 19 | analysis.show() 20 | sys.exit(app.exec_()) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### gdal_UI——基于遥感数据的河道水质监控系统 2 | ========== 3 | 使用python2.7 +pyqt4 4 | 主要使用的库为 Numpy,Pyqt,gdal,pandas,sqlit3,opencv3 5 | ``` 6 | -data #存放数据 7 | --water.db #数据库文件 8 | -code #存放代码 9 | --controller #每个模块的头文件代码 10 | --- 遥感图像模块.py 11 | --- 图像分析模块.py 12 | --- 水质数据模块.py 13 | --- 无人机模块.py 14 | --- 用户登陆模块.py 15 | --model #子功能函数 16 | --- 数据库操作.py 17 | --- 遥感图像读取.py 18 | --- pandas操作.py 19 | --- 经纬度坐标操作.py 20 | --- 水质数据操作.py 21 | --- md5加密.py 22 | --- 视频录像操作.py 23 | --view #UI框架 24 | ---框架 25 | Status API Training Shop Blog About 26 | ``` 27 | -------------------------------------------------------------------------------- /code/model/DrawTable.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | from PyQt5.QtWidgets import QHeaderView 5 | import sys 6 | 7 | 8 | '''''' 9 | 10 | class Gis_DrawTable(object): 11 | """输入一个pandas的frame,执行一个构造表格的行为""" 12 | 13 | def get_frame(self,frame): 14 | self.frame = frame 15 | self.rows_len = len(self.frame.values) 16 | self.cols_len = self.frame.columns.size 17 | 18 | def get_table(self,table): 19 | self.table = table 20 | self.table.setColumnCount(self.cols_len) 21 | self.table.setRowCount(self.rows_len) 22 | 23 | def draw_table(self,frame,table): 24 | self.get_frame(frame) 25 | self.get_table(table) 26 | self.rows = list(self.frame.columns.values) 27 | self.cols = list(self.frame.index) 28 | self.table.setHorizontalHeaderLabels(self.rows) 29 | self.table.setVerticalHeaderLabels(self.cols) 30 | for i in range(self.rows_len): 31 | for j in range(self.cols_len): 32 | cnt = self.frame.ix[i,j] 33 | newItem = QtWidgets.QTableWidgetItem(cnt) 34 | newItem.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) 35 | self.table.setItem(i,j,newItem) 36 | #self.table.show() 37 | return self.table 38 | 39 | def main(): 40 | app = QtWidgets.QApplication(sys.argv) 41 | table = QtWidgets.QTableWidget() 42 | DT = Gis_DrawTable() 43 | DT.get_table(table) 44 | DT.draw_table() 45 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /code/model/pyqt_QThread.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from PyQt5.QtCore import * 5 | from PyQt5.QtGui import * 6 | from PyQt5.QtWidgets import * 7 | 8 | global sec 9 | sec=0 10 | 11 | class WorkThread(QThread): 12 | trigger = pyqtSignal() 13 | def __int__(self): 14 | super(WorkThread,self).__init__() 15 | 16 | def run(self): 17 | for i in range(203300030): 18 | pass 19 | self.trigger.emit() #循环完毕后发出信号 20 | 21 | def countTime(): 22 | global sec 23 | sec+=1 24 | lcdNumber.display(sec) #LED显示数字+1 25 | 26 | def work(): 27 | timer.start(1000) #计时器每秒计数 28 | workThread.start() #计时开始 29 | workThread.trigger.connect(timeStop) #当获得循环完毕的信号时,停止计数 30 | 31 | def timeStop(): 32 | timer.stop() 33 | print("运行结束用时",lcdNumber.value()) 34 | global sec 35 | sec=0 36 | 37 | app=QApplication([]) 38 | top=QWidget() 39 | layout=QVBoxLayout(top) #垂直布局类QVBoxLayout; 40 | lcdNumber=QLCDNumber() #加个显示屏 41 | layout.addWidget(lcdNumber) 42 | button=QPushButton("测试") 43 | layout.addWidget(button) 44 | 45 | timer=QTimer() 46 | workThread=WorkThread() 47 | 48 | button.clicked.connect(work) 49 | timer.timeout.connect(countTime) #每次计时结束,触发setTime 50 | 51 | top.show() 52 | app.exec() -------------------------------------------------------------------------------- /code/view/Ui_information.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | from PyQt5.QtWidgets import QHeaderView 5 | 6 | class Ui_Information(object): 7 | def setupUi(self, Form): 8 | Form.setObjectName("Form") 9 | #Form.resize(285, 537) 10 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 11 | sizePolicy.setHorizontalStretch(0) 12 | sizePolicy.setVerticalStretch(0) 13 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 14 | Form.setSizePolicy(sizePolicy) 15 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form) 16 | self.verticalLayout_2.setObjectName("verticalLayout_2") 17 | self.verticalLayout = QtWidgets.QVBoxLayout() 18 | self.verticalLayout.setObjectName("verticalLayout") 19 | self.tableWidget = QtWidgets.QTableWidget(Form) 20 | self.tableWidget.setObjectName("tableWidget") 21 | 22 | self.tableWidget.horizontalHeader().setDefaultSectionSize(30) 23 | self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) 24 | self.verticalLayout.addWidget(self.tableWidget) 25 | self.verticalLayout_2.addLayout(self.verticalLayout) 26 | 27 | self.retranslateUi(Form) 28 | QtCore.QMetaObject.connectSlotsByName(Form) 29 | '''TODO:编写一个类,接受数据库返回的数值循环显示检测站和水质''' 30 | '''已完成''' 31 | def retranslateUi(self, Form): 32 | _translate = QtCore.QCoreApplication.translate 33 | Form.setWindowTitle(_translate("Form", "水质信息")) 34 | 35 | 36 | if __name__ == "__main__": 37 | import sys 38 | app = QtWidgets.QApplication(sys.argv) 39 | Form = QtWidgets.QWidget() 40 | ui = Ui_Information() 41 | ui.setupUi(Form) 42 | Form.show() 43 | sys.exit(app.exec_()) 44 | 45 | -------------------------------------------------------------------------------- /code/model/DrawTree.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | from PyQt5.QtWidgets import QHeaderView 5 | import sys 6 | 7 | 8 | '''''' 9 | 10 | class Gis_DrawTree(object): 11 | """输入一个pandas的frame,执行一个构造列表的行为""" 12 | 13 | def get_maplist(self,frame): 14 | self.maplist = frame 15 | 16 | def get_timelist(self): 17 | """把所有的时间,分片为年排列""" 18 | self.time_year = [] 19 | self.time_month = [] 20 | for i in range(len(self.maplist.index)): 21 | self.time_year.append(self.maplist.Time[i].split('-')[0]) 22 | self.time_month.append(self.maplist.Time[i].split('-')[1]) 23 | 24 | def build_maplist_from_year(self): 25 | self.get_timelist() 26 | map_dict = {} 27 | 28 | for year in self.time_year: 29 | map_dict[year] = \ 30 | self.maplist[(self.maplist.Time >= year) & \ 31 | (self.maplist.Time < str(int(year)+1))].MapId.tolist() 32 | print (map_dict) 33 | self.map_dict = map_dict 34 | 35 | def get_Qtree(self,Qtree): 36 | self.Qtree = Qtree 37 | self.Qtree.setColumnCount(2) 38 | self.Qtree.setHeaderLabels(['时间','地图列表']) 39 | 40 | def draw_tree(self,frame,tree): 41 | self.get_Qtree(tree) 42 | self.get_maplist(frame) 43 | self.build_maplist_from_year() 44 | #self.map_dict = frame 45 | i = 0 46 | for year in self.map_dict: 47 | item_0 = QtWidgets.QTreeWidgetItem(self.Qtree) 48 | self.Qtree.topLevelItem(i).setText(0, year) 49 | j = 0 50 | for mapid in self.map_dict[year]: 51 | item_1 = QtWidgets.QTreeWidgetItem(item_0) 52 | self.Qtree.topLevelItem(i).child(j).setText(0, self.time_month[j]+'月') 53 | self.Qtree.topLevelItem(i).child(j).setText(1, mapid) 54 | j = j+1 55 | i = i+1 56 | return self.Qtree 57 | 58 | def main(): 59 | map_dict = {'1991':[u'LT51190381991204BJC00'],\ 60 | '1992':[u'LT51190381991204BJC02']} 61 | print (map_dict) 62 | app = QtWidgets.QApplication(sys.argv) 63 | tree = QtWidgets.QTreeWidget() 64 | DT = Gis_DrawTree() 65 | tree = DT.draw_tree(map_dict,tree) 66 | tree.show() 67 | sys.exit(app.exec_()) 68 | 69 | 70 | if __name__ == "__main__": 71 | main() 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /code/controller/information.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | from PyQt5.QtGui import QFont 4 | import sys 5 | sys.path.append("..") 6 | from view.Ui_information import Ui_Information 7 | from model.mplCodeWrapper import MyMplCanvas, MyStaticMplCanvas, MyDynamicMplCanvas 8 | from model.DrawTable import Gis_DrawTable 9 | from model.SqlGis import gdal_sqlite 10 | 11 | 12 | class informationwindow(QtWidgets.QWidget): 13 | def __init__(self): 14 | super(informationwindow,self).__init__() 15 | self.view=Ui_Information() 16 | self.view.setupUi(self) 17 | self.DT = Gis_DrawTable() 18 | self.SQL = gdal_sqlite() 19 | '''TODO:修改为从用户列表处选择的返回值''' 20 | self.MapId = 'LT51190381991204BJC00' 21 | 22 | '''静态画布''' 23 | self.widget = MyStaticMplCanvas() 24 | self.widget.setMinimumSize(QtCore.QSize(0, 150)) 25 | self.widget.setObjectName("widget") 26 | self.view.verticalLayout.addWidget(self.widget) 27 | '''动态画布''' 28 | self.widget_2 = MyDynamicMplCanvas() 29 | self.widget_2.setMinimumSize(QtCore.QSize(0, 150)) 30 | self.widget_2.setObjectName("widget_2") 31 | self.view.verticalLayout.addWidget(self.widget_2) 32 | '''创建表格''' 33 | self.creat_table() 34 | 35 | '''TODO:creat_table函数移植到main函数中''' 36 | def creat_table(self): 37 | self.view.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 38 | self.view.tableWidget.resizeRowsToContents() 39 | self.view.tableWidget.setAlternatingRowColors(True) 40 | '''从数据库获取水质表,根据水质表具体内容,循环建立表格的行、列。''' 41 | 42 | waterframe = self.SQL.Read_WaterQuality(self.MapId) 43 | print (waterframe) 44 | print (type(waterframe.ix[1,1])) 45 | self.view.tableWidget = self.DT.draw_table(waterframe,self.view.tableWidget) 46 | 47 | 48 | 49 | 50 | 51 | 52 | if __name__ == "__main__": 53 | app = QtWidgets.QApplication(sys.argv) 54 | information = informationwindow() 55 | app.setQuitOnLastWindowClosed(True) 56 | information.show() 57 | sys.exit(app.exec_()) 58 | -------------------------------------------------------------------------------- /code/controller/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | from PyQt5.QtCore import Qt 4 | from PyQt5.QtGui import QPalette 5 | import sys 6 | sys.path.append("..") 7 | from view.Ui_main import Ui_MainWindow 8 | from controller.history import historywindow 9 | from controller.information import informationwindow 10 | from controller.analysis import analysiswindow 11 | from controller.player import Player 12 | 13 | 14 | class mainwindow(QtWidgets.QMainWindow): 15 | def __init__(self): 16 | super(mainwindow,self).__init__() 17 | '''TODO:view文件构建qt的布局。 18 | contro函数中的py文件,负责构造具体内容、按钮样式、生成图片等 19 | 所有的触发公共接口都留到main.py中,后期根据具体需求考虑多线程。 ''' 20 | self.view=Ui_MainWindow() 21 | self.view.setupUi(self) 22 | self.load_windows() 23 | '''TODO:调整背景 24 | self.setAutoFillBackground(True) # 设置背景颜色 25 | self.setStyleSheet("background-color:black;") 26 | self.setBackgroundRole(QtGui.QPalette.Midlight) 27 | ''' 28 | 29 | def load_windows(self): 30 | self.page = historywindow() 31 | self.widget = informationwindow() 32 | self.page_2 = analysiswindow() 33 | self.page_3 = Player(sys.argv[1:]) 34 | 35 | self.page.setObjectName("page") 36 | self.view.stackedWidget.addWidget(self.page) 37 | 38 | self.widget.setObjectName("widget") 39 | self.view.gridLayout.addWidget(self.widget, 0, 1, 4, 1) 40 | 41 | 42 | self.page_2.setObjectName("page_2") 43 | self.view.stackedWidget.addWidget(self.page_2) 44 | 45 | self.page_3.setObjectName("page_3") 46 | self.view.stackedWidget.addWidget(self.page_3) 47 | 48 | '''按钮触发''' 49 | self.view.pushButton.clicked.connect(self.changed_history) 50 | self.view.pushButton_2.clicked.connect(self.changed_analysis) 51 | self.view.pushButton_3.clicked.connect(self.changed_video) 52 | 53 | def update_information(): 54 | '''TODO:把history.py的函数调用中的数据接口部分放到main中, 55 | 目的是:用户在选取MapId后,数据库的操作放到main中''' 56 | pass 57 | 58 | def upadte_history(): 59 | pass 60 | 61 | '''按钮触发对应函数''' 62 | def changed_history(self): 63 | self.view.stackedWidget.setCurrentIndex(0) 64 | def changed_analysis(self): 65 | self.view.stackedWidget.setCurrentIndex(1) 66 | def changed_video(self): 67 | self.view.stackedWidget.setCurrentIndex(2) 68 | 69 | 70 | def main(): 71 | app = QtWidgets.QApplication(sys.argv) 72 | main = mainwindow() 73 | app.setQuitOnLastWindowClosed(True) 74 | main.show() 75 | sys.exit(app.exec_()) 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /code/view/Ui_analysis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | 4 | class Ui_Analysis(object): 5 | def setupUi(self, Form): 6 | Form.setObjectName("Form") 7 | Form.resize(548, 406) 8 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 9 | sizePolicy.setHorizontalStretch(0) 10 | sizePolicy.setVerticalStretch(0) 11 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 12 | Form.setSizePolicy(sizePolicy) 13 | self.gridLayout_2 = QtWidgets.QGridLayout(Form) 14 | self.gridLayout_2.setObjectName("gridLayout_2") 15 | self.verticalLayout = QtWidgets.QVBoxLayout() 16 | self.verticalLayout.setObjectName("verticalLayout") 17 | 18 | '''上方图片显示窗体''' 19 | self.widget = QtWidgets.QWidget(Form) 20 | self.widget.setObjectName("widget") 21 | self.verticalLayout.addWidget(self.widget) 22 | 23 | '''下方按钮模块''' 24 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 25 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 26 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 27 | self.horizontalLayout_3.addItem(spacerItem) 28 | self.pushButton = QtWidgets.QPushButton(Form) 29 | self.pushButton.setMinimumSize(QtCore.QSize(125, 0)) 30 | self.pushButton.setObjectName("pushButton") 31 | self.horizontalLayout_3.addWidget(self.pushButton) 32 | self.pushButton_2 = QtWidgets.QPushButton(Form) 33 | self.pushButton_2.setMinimumSize(QtCore.QSize(125, 0)) 34 | self.pushButton_2.setObjectName("pushButton_2") 35 | self.horizontalLayout_3.addWidget(self.pushButton_2) 36 | spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 37 | self.horizontalLayout_3.addItem(spacerItem1) 38 | self.verticalLayout.addLayout(self.horizontalLayout_3) 39 | #设置模块比例为9:1 40 | self.verticalLayout.setStretch(0, 9) 41 | self.verticalLayout.setStretch(1, 1) 42 | self.gridLayout_2.addLayout(self.verticalLayout, 0, 0, 1, 1) 43 | 44 | self.retranslateUi(Form) 45 | QtCore.QMetaObject.connectSlotsByName(Form) 46 | 47 | def retranslateUi(self, Form): 48 | _translate = QtCore.QCoreApplication.translate 49 | Form.setWindowTitle(_translate("Form", "WQA_Analysis")) 50 | self.pushButton.setText(_translate("Form", "上传")) 51 | self.pushButton_2.setText(_translate("Form", "分析")) 52 | 53 | 54 | if __name__ == "__main__": 55 | import sys 56 | app = QtWidgets.QApplication(sys.argv) 57 | Form = QtWidgets.QWidget() 58 | ui = Ui_Analysis() 59 | ui.setupUi(Form) 60 | Form.show() 61 | sys.exit(app.exec_()) 62 | 63 | -------------------------------------------------------------------------------- /code/model/SqlGis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | '''导入python模块''' 3 | import os 4 | import pandas.io.sql as pd_sql 5 | import pandas as pd 6 | import sqlite3 7 | import ast 8 | 9 | '''导入自定义功能''' 10 | 11 | 12 | 13 | class gdal_sqlite(object): 14 | """本功能是查询读取遥感图片的相关信息 15 | 一、读取图片的内容包括: 16 | 1 河流ID,Name————》显示下拉框 17 | 2 读取遥感地图列表 18 | 3 读取遥感地图对应的存储路径 19 | 4 读取图片 20 | 二、读取水质参数: 21 | 观测站与水质参数显示、画图 22 | 三、读取录像 23 | 读取录像列表 24 | 25 | """ 26 | def __init__(self): 27 | self.sql_path = os.path.abspath(os.path.dirname(__file__)) + "/../../data/water_sensing.db" 28 | 29 | def change_sqlpath(self,path): 30 | self.sql_path = path 31 | 32 | def get_conn(self): 33 | '''连接到数据库,如果文件路径存在则连接,如果不存在,则报错''' 34 | path = self.sql_path 35 | if os.path.exists(path) and os.path.isfile(path): 36 | conn = sqlite3.connect(path) 37 | print('成功连接到[{}]的数据库'.format(path)) 38 | return conn 39 | else: 40 | print('数据库不存在,请检查路径') 41 | 42 | '''获取所有的river列表''' 43 | def Read_riverlist(self): 44 | sql = "SELECT * FROM River" 45 | conn = self.get_conn() 46 | river_list = pd_sql.read_sql(sql,conn) 47 | conn.close() 48 | return river_list 49 | 50 | '''读取河流ID,然后用河流ID查找”河流-遥感地图”关系表''' 51 | def Read_maplist(self,RiverId): 52 | sql = "SELECT MapId,Time FROM River_Map WHERE RiverId='%s'" %RiverId 53 | conn = self.get_conn() 54 | map_list = pd_sql.read_sql(sql,conn) 55 | conn.close() 56 | return map_list 57 | 58 | '''获取MapId后,读取处理前遥感RGB图片和处理后河流图片''' 59 | def Read_GisRGB_Map(self,MapId): 60 | sql = "SELECT RGBImage FROM Map WHERE MapId='%s'" %MapId 61 | conn = self.get_conn() 62 | frame = pd_sql.read_sql(sql,conn) 63 | image_path = frame['RGBImage'][0] 64 | conn.close() 65 | return image_path 66 | 67 | def Read_River_Map(self,MapId): 68 | sql = "SELECT RiverImage FROM Map WHERE MapId='%s'" %MapId 69 | conn = self.get_conn() 70 | frame = pd_sql.read_sql(sql,conn) 71 | image_path = frame['RiverImage'][0] 72 | conn.close() 73 | return image_path 74 | 75 | '''获取MapId,读取水质表''' 76 | '''FROM (表1 INNER JOIN 表2 ON 表1.字段号=表2.字段号) INNER JOIN 表3 ON 表1.字段号=表3.字段号''' 77 | def Read_WaterQuality(self,MapId): 78 | sql = "SELECT ObStation_WaterQuility.StationId,\ 79 | WaterQuility.WaterQualityTime,\ 80 | WaterQuility.WaterQualityInfo,\ 81 | WaterQuility.WaterQualityId \ 82 | FROM (Map_ObStation INNER JOIN ObStation_WaterQuility \ 83 | ON Map_ObStation.StationId = ObStation_WaterQuility.StationId )\ 84 | INNER JOIN WaterQuility \ 85 | ON ObStation_WaterQuility.WaterQualityId = WaterQuility.WaterQualityId \ 86 | WHERE Map_ObStation.MapId = '%s' " %MapId 87 | conn = self.get_conn() 88 | water_frame = pd_sql.read_sql(sql,conn) 89 | conn.close() 90 | dicts = {} 91 | for i in range(len(water_frame.values)): 92 | dicts[water_frame["StationId"][i]] = ast.literal_eval(water_frame["WaterQualityInfo"][i]) 93 | water_frame_resut = pd.DataFrame(dicts).T 94 | return water_frame_resut 95 | 96 | '''获取RiverId,返回录像列表''' 97 | '''FROM (表1 INNER JOIN 表2 ON 表1.字段号=表2.字段号)''' 98 | def Read_VedioList(self,RiverId,StartTime,EndTime): 99 | sql = "SELECT Video.VideoPath,Video.VideoTime \ 100 | FROM Video INNER JOIN River_Video \ 101 | ON Video.VideoId = River_Video.VideoId \ 102 | WHERE River_Video.RiverId = '%s'\ 103 | and Video.VideoTime >= '%s' \ 104 | and Video.VideoTime <='%s'" %(RiverId,StartTime,EndTime) 105 | conn = self.get_conn() 106 | vedio_list = pd_sql.read_sql(sql,conn) 107 | conn.close() 108 | return vedio_list 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /模块分析.txt: -------------------------------------------------------------------------------- 1 | 2016-5-12 2 | 1. 打开后最大化。 3 | 2. 历史、分析、无人机按钮缩小后排放。 4 | 3. 右边参数,表格,2个图标。 5 | 6 | 7 | 无人机界面: 8 | 视频存储是按照:1,河流ID。2,时间年月日,每日存储一次。3。编号 9 | 用户播放视频,不从文件夹取。而是按照时间,比如(2015年10月1日-2015年10月2日),选择太湖。 10 | 11 | 12 | 13 | 显示遥感图像: 14 | 1,确认河流————》RiverID 15 | 2. 确认时间————》TIME 16 | 3. 确认遥感————》MAPID 17 | 4. 系统获取文件路径或图像矩阵 ————》预留显示部分接口,显示。 18 | 5. 显示图片平铺到窗口。 19 | **6. 训练好模型后,训练之前 遥感地图为观测站点对应的数据,训练之后为整个河流对应的数据。 20 | 按照颜色深浅进行分布。 21 | 22 | 显示水质数据和图表: 23 | 1,确认河流————》RiverID 24 | 2. 确认时间————》TIME 25 | 3. 确认遥感————》MAPID 26 | 4. 太湖有5个观测站 ————》ObId ————》5个经纬度 27 | 5. 经纬度 ————》一次sql 语句取pandas表 28 | 1. 画图 seaborn python base on matplolib 29 | 2, 4个折线图根据用户选择,画在一个表上,纵坐标为’水质数据‘折算到100 30 | 横坐标为’时间‘,一次显示一个观测站。 31 | 3 柱状图横坐标为’水质ID‘,柱状图分布为’观测站‘或’不同河流‘,限制一次性对比3个河流。 32 | 6,用户选择对比不同河流 --》切换界面 33 | 1. 选择对比的河流,时间。 34 | 2. 显示相应折线图和柱状图 35 | 36 | 播放视频 37 | 1,确认河流————》RiverID 38 | 2. 确认时间————》TIME 39 | 3. 选择播放的时间段,返回播放列表 40 | 4. 播放视频,或选择观看实时无人机画面 41 | **5 操作无人机或无人船界面 42 | 43 | 分析 44 | 1. 选择文件夹,返回所有tif列表 45 | 2. 处理tif文件,返回处理后水体图片和彩色RGB图片 46 | 3. 插入数据到数据库。 47 | 48 | 2016-5-14 49 | 程序结构 50 | gdal_UI 51 | -data #存放数据 52 | --water.db #数据库文件 53 | -code #存放代码 54 | --model #每个模块的头文件代码 55 | --- 遥感图像模块.py 56 | --- 图像分析模块.py 57 | --- 水质数据模块.py 58 | --- 无人机模块.py 59 | --- 用户登陆模块.py 60 | --function #子功能函数 61 | --- 数据库操作.py 62 | --- 遥感图像读取.py 63 | --- pandas操作.py 64 | --- 经纬度坐标操作.py 65 | --- 水质数据操作.py 66 | --- md5加密.py 67 | --- 视频录像操作.py 68 | --ui #UI框架 69 | ---框架 70 | 71 | 2016-5-16 72 | 73 | -data #存放数据 74 | --water.db #数据库文件 75 | -code #存放代码 76 | --controller #每个模块的头文件代码 77 | --- 遥感图像模块.py 78 | --- 图像分析模块.py 79 | --- 水质数据模块.py 80 | --- 无人机模块.py 81 | --- 用户登陆模块.py 82 | --model #子功能函数 83 | --- 数据库操作.py 84 | --- 遥感图像读取.py 85 | --- pandas操作.py 86 | --- 经纬度坐标操作.py 87 | --- 水质数据操作.py 88 | --- md5加密.py 89 | --- 视频录像操作.py 90 | --view #UI框架 91 | ---框架 92 | 93 | 遥感图像模块.py 94 | 读取当前时间,回退到1991年,自动返回生成年-月的可选择列表框。 95 | 创建新的放大缩小图像函数,对读取的图片进行自适应缩放,输入图片路径和窗体大小,根据放大缩小按钮返回比例的image矩阵。 96 | 放大缩小功能绑定到滚轮 97 | 水质数据模块.py 98 | 从数据库读取pandas表,index和cols显示。 99 | 100 | 2016-5-17 101 | 数据库结构: 102 | 实体: 103 | 河流:RiverId,Name 104 | 遥感地图:MapId,Time,Path,RGBImage,RiverImage 105 | 观测站:ObStationId,StationLongtitude,StationLatitude 106 | 视频:VedioId = RiverId+Time,Time,Path 107 | 水质 :WaterQualityId=MapId+ObStationId,Time,WaterQuality 108 | 用户: UserId,Password,Authority 109 | 关系表: 110 | 河流---地图:RiverId--MapId 111 | 地图---观测站: MapId,Time--ObStationId 112 | 河流---视频: RiverId,Time -- VedioId 113 | 观测站---水质: ObStationId--WaterQualityId 114 | 115 | 116 | 2016-5-19 117 | history.UI.label4显示遥感图像。 118 | TODO: 119 | 1、修改图像的分辨率,设计一个showImage类对图像进行缩放,该类的主要功能是,接受用户选择的mapid,创建一个QImage的类从数据库读取相应图片路径。读取图片后,根据用户滚轮选择的放大、缩小倍数,以及窗口分辨率进行缩放,返回该QImage。 120 | 2、history模块中,创建pixmap = QPixmap.fromImage(Qimage),然后UI.label4.setPixmap(pixmap)即可。 121 | 3、UI.label4的窗口高度固定,宽度适应图片的长宽比,窗口分辨率需要被showImage类读取。 122 | TODO: 123 | 问题: Qt 中所有界面都是在 UI 线程中(也被称为主线程),生成界面时,各窗体状态是show之前的形状,比如800*600。在生成后,窗体被拉伸到1920*1020,这时需要读取当前的窗体分辨率。现在的做法是在生成时读取分辨率,于是只能读到800*600.无法读取到1920*1080的状态。 124 | Pyqt多线程,主线程生成UI,其他线程操作图片更新、图表的更新。 125 | information.py 126 | TODO:创建一个类,该类的功能是从数据库获取水质表, 127 | 然后根据水质表具体内容,循环建立表格的行、列。 128 | 循环内增加文字居中的代码 129 | Item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter) 130 | 131 | qtdemos 里的demo有个电子地图的样例。OPenStreetMap,Mapcatcher 132 | 133 | 134 | 2016-5-22 135 | 修改数据库的水质表存储结构: 136 | 原本结构,分a,b,c,d等不同观测站,每个观测只放一个水质数据。 137 | 修改后结构,分a,b,c,d等不同观测站,但是观测站的一行中,以字典的方式存放所有的水质数值。 138 | TODO:pandas DataFrame 画折线图、曲线图。 139 | 140 | 2016-5-23 141 | '''TODO:把history.py的函数调用中的数据接口部分放到main中, 142 | 目的是:用户在选取MapId后,数据库的操作放到main中,返回的数值可以与其他UI窗口公用。 143 | 信号槽的接口,预留后点击触发upadte函数''' 144 | TODO:UI文件构建qt的布局。 145 | contro函数中的py文件,负责构造具体内容、按钮样式、生成图片等 146 | 所有的触发公共接口都留到main.py中,后期根据具体需求考虑多线程。 147 | 148 | 149 | 150 | ax = sns.barplot(x='CODMn',y='叶绿素',hue='StationId',data=frame,palette="Blues_d") 151 | 152 | 153 | 154 | 155 | 2016-6-24 156 | 太湖modis高光谱数据。glovis python与IDL。OpenGL 进行 3D 建模 157 | 158 | 159 | 160 | 2016-9-13 161 | 水质分级,查询图像和水质参数(叶绿素)现有的公式关系。美国研究的结论。HTM模型,J.Howkins。 -------------------------------------------------------------------------------- /code/controller/history.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5.QtWidgets import QWidget, QHBoxLayout,QLabel, QApplication,QMessageBox,QGraphicsScene 3 | from PyQt5.QtGui import QPixmap,QImage,QPainter 4 | from PyQt5 import QtCore 5 | import sys 6 | sys.path.append("..") 7 | from view.Ui_history import Ui_History 8 | from model.SqlGis import gdal_sqlite 9 | from model.DrawTree import Gis_DrawTree 10 | 11 | 12 | class historywindow(QWidget): 13 | def __init__(self): 14 | super(historywindow,self).__init__() 15 | self.UI=Ui_History() 16 | self.UI.setupUi(self) 17 | self.show() 18 | self.SQL = gdal_sqlite() 19 | self.MapId = 'LT51190381991204BJC00' # TODO:修改为从用户列表处选择的返回值 20 | 21 | self.get_imagepath() 22 | self.show_riverlist() 23 | self.show_maplist() 24 | self.show_image() 25 | self.UI.treeWidget.clicked.connect(self.getCurrentIndex) 26 | 27 | 28 | def getCurrentIndex(self): 29 | text = self.UI.treeWidget.currentItem().text(1) 30 | if text != None: 31 | print (text) 32 | QMessageBox.warning(None, "treeview select", 33 | str(text)) 34 | 35 | 36 | '''TODO:self.model''' 37 | def updata_mapid(self,MapId): 38 | self.MapId = MapId 39 | 40 | def updata_riverid(self): 41 | """用户点击选择河流,返回河流ID""" 42 | self.riverid = "TH" 43 | 44 | 45 | def get_imagepath(self): 46 | self.image_path = self.SQL.Read_River_Map(self.MapId) 47 | 48 | def get_riverlist(self): 49 | self.updata_riverid() 50 | self.riverlist = self.SQL.Read_riverlist() 51 | 52 | def get_maplist(self): 53 | self.maplist = self.SQL.Read_maplist(self.riverid) 54 | print (self.maplist) 55 | 56 | def show_riverlist(self): 57 | self.get_riverlist() 58 | for i in range(len(self.riverlist.index)): 59 | self.UI.comboBox.addItem("") 60 | self.UI.comboBox.setItemText(i, self.riverlist.RiverName[i]) 61 | 62 | 63 | def show_maplist(self): 64 | self.riverid = 'TH' 65 | DT = Gis_DrawTree() 66 | self.get_maplist() 67 | self.UI.treeWidget = DT.draw_tree(self.maplist,self.UI.treeWidget) 68 | pass 69 | 70 | 71 | def show_image(self): 72 | print (self.image_path) 73 | self.Image = QImage() 74 | self.Image.load(self.image_path) 75 | if self.Image == None: 76 | print ("图像读取错误") 77 | sys.exit() 78 | else: 79 | from PIL import Image 80 | from PIL.ImageQt import ImageQt 81 | #img = Image.open(self.image_path) 82 | img = Image.open('/Users/chensiye/zhizi.jpg') 83 | w, h = img.size 84 | print (w,h) 85 | imgQ = ImageQt(img) 86 | 87 | pixMap = QPixmap.fromImage(imgQ) 88 | self.scene = QGraphicsScene() 89 | self.scene.addPixmap(pixMap) 90 | #self.scene.setSceneRect(0, 0, 1, 1) 91 | view = self.UI.graphicsView 92 | view.setScene(self.scene) 93 | view.setSceneRect(0,0,w,h) 94 | #view.resize(1000,1000) 95 | #print (view.size()) 96 | view.fitInView(QtCore.QRectF(0, 0, w, h), QtCore.Qt.KeepAspectRatio) 97 | 98 | """ 99 | self.UI.widget.resize(800,650) 100 | height = self.UI.widget.size().height() 101 | width = self.UI.widget.size().width() 102 | pixmap = QPixmap.fromImage(self.Image.scaledToHeight(height)) 103 | '''TODO: 修改图像分辨率为适应值''' 104 | self.imagelabel = QLabel(self.UI.widget) 105 | self.imagelabel.setPixmap(pixmap) 106 | pix_x = pixmap.size().width() 107 | pix_y = pixmap.size().height() 108 | x = int((width - pix_x)/2) 109 | print (width,height,pix_x,pix_y,x) 110 | print (self.UI.widget.getContentsMargins()) 111 | self.imagelabel.move(10+x,0) 112 | """ 113 | 114 | 115 | 116 | if __name__ == "__main__": 117 | app = QApplication(sys.argv) 118 | history = historywindow() 119 | app.setQuitOnLastWindowClosed(True) 120 | history.show() 121 | sys.exit(app.exec_()) 122 | -------------------------------------------------------------------------------- /code/model/mplCodeWrapper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import random 4 | import seaborn as sns 5 | import pandas as pd 6 | from PyQt5 import QtCore 7 | from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, QSizePolicy, QMessageBox, QWidget 8 | 9 | from numpy import arange, sin, pi 10 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas 11 | from matplotlib.figure import Figure 12 | 13 | ''' 14 | reload(sys) 15 | sys.setdefaultencoding('utf-8') 16 | ''' 17 | class MyMplCanvas(FigureCanvas): 18 | """这是一个窗口部件,即QWidget(当然也是FigureCanvasAgg)""" 19 | def __init__(self, parent=None, width=5, height=4, dpi=100): 20 | fig = Figure(figsize=(width, height), dpi=dpi) 21 | self.axes = fig.add_subplot(111) 22 | # 每次plot()调用的时候,我们希望原来的坐标轴被清除(所以False) 23 | sns.set_style("darkgrid") 24 | self.axes.hold(False) 25 | 26 | self.compute_initial_figure() 27 | FigureCanvas.__init__(self, fig) 28 | self.setParent(parent) 29 | 30 | FigureCanvas.setSizePolicy(self, 31 | QSizePolicy.Expanding, 32 | QSizePolicy.Expanding) 33 | FigureCanvas.updateGeometry(self) 34 | 35 | def compute_initial_figure(self): 36 | pass 37 | 38 | class MyStaticMplCanvas(MyMplCanvas): 39 | """静态画布:一条正弦线""" 40 | def compute_initial_figure(self): 41 | t = arange(0.0, 3.0, 0.01) 42 | s = sin(2*pi*t) 43 | self.axes.plot(t, s) 44 | 45 | 46 | class MyDynamicMplCanvas(MyMplCanvas): 47 | """动态画布:每秒自动更新,更换一条折线。""" 48 | def __init__(self, *args, **kwargs): 49 | MyMplCanvas.__init__(self, *args, **kwargs) 50 | timer = QtCore.QTimer(self) 51 | timer.timeout.connect(self.update_figure) 52 | timer.start(1000) 53 | 54 | def compute_initial_figure(self): 55 | self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r') 56 | 57 | def update_figure(self): 58 | # 构建4个随机整数,位于闭区间[0, 10] 59 | l = [random.randint(0, 10) for i in range(4)] 60 | 61 | self.axes.plot([0, 1, 2, 3], l, 'r') 62 | self.draw() 63 | 64 | class ApplicationWindow(QMainWindow): 65 | def __init__(self): 66 | QMainWindow.__init__(self) 67 | self.setAttribute(QtCore.Qt.WA_DeleteOnClose) 68 | self.setWindowTitle("程序主窗口") 69 | 70 | self.file_menu = QMenu('&File', self) 71 | self.file_menu.addAction('&Quit', self.fileQuit, 72 | QtCore.Qt.CTRL + QtCore.Qt.Key_Q) 73 | self.menuBar().addMenu(self.file_menu) 74 | 75 | self.help_menu = QMenu('&Help', self) 76 | self.menuBar().addSeparator() 77 | self.menuBar().addMenu(self.help_menu) 78 | 79 | self.help_menu.addAction('&About', self.about) 80 | 81 | self.main_widget = QWidget(self) 82 | 83 | l = QVBoxLayout(self.main_widget) 84 | sc = MyStaticMplCanvas(self.main_widget)#, width=3, height=2, dpi=100) 85 | dc = MyDynamicMplCanvas(self.main_widget)#, width=3, height=2, dpi=100) 86 | l.addWidget(sc) 87 | l.addWidget(dc) 88 | 89 | self.main_widget.setFocus() 90 | self.setCentralWidget(self.main_widget) 91 | # 状态条显示2秒 92 | self.statusBar().showMessage("matplotlib 万岁!", 2000) 93 | 94 | def fileQuit(self): 95 | self.close() 96 | 97 | def closeEvent(self, ce): 98 | self.fileQuit() 99 | 100 | def about(self): 101 | QMessageBox.about(self, "About", 102 | """embedding_in_qt5.py example 103 | Copyright 2015 BoxControL 104 | 105 | This program is a simple example of a Qt5 application embedding matplotlib 106 | canvases. It is base on example from matplolib documentation, and initially was 107 | developed from Florent Rougon and Darren Dale. 108 | 109 | http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html 110 | 111 | It may be used and modified with no restriction; raw copies as well as 112 | modified versions may be distributed without limitation. 113 | """ 114 | ) 115 | 116 | if __name__ == '__main__': 117 | app = QApplication(sys.argv) 118 | aw = ApplicationWindow() 119 | aw.setWindowTitle("PyQt5 与 Matplotlib 例子") 120 | aw.show() 121 | #sys.exit(qApp.exec_()) 122 | app.exec_() 123 | -------------------------------------------------------------------------------- /code/view/Ui_history.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'E:\Anaconda3\Workspace\WQA\view\history.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.6 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_History(object): 12 | def setupUi(self, Form): 13 | Form.setObjectName("Form") 14 | Form.resize(1920, 1080) 15 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 16 | sizePolicy.setHorizontalStretch(0) 17 | sizePolicy.setVerticalStretch(0) 18 | sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) 19 | Form.setSizePolicy(sizePolicy) 20 | self.gridLayout_2 = QtWidgets.QGridLayout(Form) 21 | self.gridLayout_2.setObjectName("gridLayout_2") 22 | self.horizontalLayout = QtWidgets.QHBoxLayout() 23 | self.horizontalLayout.setSpacing(0) 24 | self.horizontalLayout.setObjectName("horizontalLayout") 25 | self.verticalLayout_2 = QtWidgets.QVBoxLayout() 26 | self.verticalLayout_2.setSpacing(0) 27 | self.verticalLayout_2.setObjectName("verticalLayout_2") 28 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 29 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 30 | 31 | '''下拉,月份选择(左上角部分布局,通过弹簧调整位置)''' 32 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 33 | self.horizontalLayout_2.addItem(spacerItem) 34 | self.label = QtWidgets.QLabel(Form) 35 | self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) 36 | self.label.setObjectName("label") 37 | self.horizontalLayout_2.addWidget(self.label) 38 | self.comboBox = QtWidgets.QComboBox(Form) 39 | self.comboBox.setObjectName("comboBox") 40 | ''' 41 | self.comboBox.addItem("") 42 | self.comboBox.addItem("") 43 | self.comboBox.addItem("") 44 | ''' 45 | self.horizontalLayout_2.addWidget(self.comboBox) 46 | spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 47 | self.horizontalLayout_2.addItem(spacerItem1) 48 | self.horizontalLayout_2.setStretch(0, 2) 49 | self.horizontalLayout_2.setStretch(1, 1) 50 | self.horizontalLayout_2.setStretch(2, 2) 51 | self.horizontalLayout_2.setStretch(3, 2) 52 | self.verticalLayout_2.addLayout(self.horizontalLayout_2) 53 | spacerItem2 = QtWidgets.QSpacerItem(0, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) 54 | self.verticalLayout_2.addItem(spacerItem2) 55 | 56 | '''图片列表''' 57 | self.treeWidget = QtWidgets.QTreeWidget(Form) 58 | self.treeWidget.setObjectName("treeWidget") 59 | self.verticalLayout_2.addWidget(self.treeWidget) 60 | 61 | spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 62 | self.verticalLayout_2.addItem(spacerItem3) 63 | #设置左边模块比例1:12 64 | self.verticalLayout_2.setStretch(0, 1) 65 | self.verticalLayout_2.setStretch(2, 12) 66 | self.verticalLayout_2.setStretch(3, 1) 67 | self.horizontalLayout.addLayout(self.verticalLayout_2) 68 | 69 | '''右边图片显示窗体''' 70 | '''更改为graphicsView''' 71 | self.verticalLayout = QtWidgets.QVBoxLayout() 72 | self.verticalLayout.setObjectName("verticalLayout") 73 | 74 | self.graphicsView = QtWidgets.QGraphicsView(Form) 75 | self.graphicsView.setObjectName("graphicsView") 76 | 77 | self.verticalLayout.addWidget(self.graphicsView) 78 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 79 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 80 | 81 | '''右下放大缩小按钮(通过弹簧调整位置)''' 82 | spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 83 | self.horizontalLayout_3.addItem(spacerItem4) 84 | self.pushButton = QtWidgets.QPushButton(Form) 85 | self.pushButton.setObjectName("pushButton") 86 | self.horizontalLayout_3.addWidget(self.pushButton) 87 | self.pushButton_2 = QtWidgets.QPushButton(Form) 88 | self.pushButton_2.setObjectName("pushButton_2") 89 | self.horizontalLayout_3.addWidget(self.pushButton_2) 90 | spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 91 | self.horizontalLayout_3.addItem(spacerItem5) 92 | self.horizontalLayout_3.setStretch(0, 2) 93 | self.horizontalLayout_3.setStretch(1, 1) 94 | self.horizontalLayout_3.setStretch(2, 1) 95 | self.horizontalLayout_3.setStretch(3, 2) 96 | self.verticalLayout.addLayout(self.horizontalLayout_3) 97 | #设置右边模块比例为12:1 98 | self.verticalLayout.setStretch(0, 12) 99 | self.verticalLayout.setStretch(1, 1) 100 | self.horizontalLayout.addLayout(self.verticalLayout) 101 | #设置左边模块与右边模块比例为1:4 102 | self.horizontalLayout.setStretch(0, 1) 103 | self.horizontalLayout.setStretch(1, 4) 104 | self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 1) 105 | 106 | self.retranslateUi(Form) 107 | QtCore.QMetaObject.connectSlotsByName(Form) 108 | 109 | def retranslateUi(self, Form): 110 | _translate = QtCore.QCoreApplication.translate 111 | Form.setWindowTitle(_translate("Form", "WQA_History")) 112 | self.label.setText(_translate("Form", "水体:")) 113 | self.pushButton.setText(_translate("Form", "+")) 114 | self.pushButton_2.setText(_translate("Form", "-")) 115 | 116 | 117 | if __name__ == "__main__": 118 | 119 | def show_image(): 120 | from PIL import Image 121 | from PIL.ImageQt import ImageQt 122 | #img = Image.open(self.image_path) 123 | img = Image.open('/Users/chensiye/zhizi.jpg') 124 | w, h = img.size 125 | imgQ = ImageQt(img) 126 | 127 | pixMap = QtGui.QPixmap.fromImage(imgQ) 128 | scene = QtWidgets.QGraphicsScene() 129 | scene.addPixmap(pixMap) 130 | return scene 131 | import sys 132 | app = QtWidgets.QApplication(sys.argv) 133 | Form = QtWidgets.QWidget() 134 | ui = Ui_History() 135 | ui.setupUi(Form) 136 | view = ui.graphicsView 137 | scene = show_image() 138 | view.setScene(scene) 139 | 140 | Form.show() 141 | view.fitInView(QtCore.QRectF(0, 0, 1024, 1024), QtCore.Qt.KeepAspectRatio) 142 | sys.exit(app.exec_()) 143 | 144 | -------------------------------------------------------------------------------- /code/view/Ui_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | 4 | class Ui_MainWindow(object): 5 | def setupUi(self, MainWindow): 6 | MainWindow.setObjectName("MainWindow") 7 | avGeom = QtWidgets.QDesktopWidget().availableGeometry() 8 | MainWindow.setGeometry(avGeom) 9 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 10 | sizePolicy.setHorizontalStretch(0) 11 | sizePolicy.setVerticalStretch(0) 12 | sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) 13 | MainWindow.setSizePolicy(sizePolicy) 14 | self.centralwidget = QtWidgets.QWidget(MainWindow) 15 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 16 | sizePolicy.setHorizontalStretch(0) 17 | sizePolicy.setVerticalStretch(0) 18 | sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) 19 | self.centralwidget.setSizePolicy(sizePolicy) 20 | self.centralwidget.setObjectName("centralwidget") 21 | self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) 22 | self.verticalLayout.setObjectName("verticalLayout") 23 | self.gridLayout = QtWidgets.QGridLayout() 24 | self.gridLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) 25 | self.gridLayout.setObjectName("gridLayout") 26 | self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget) 27 | self.stackedWidget.setObjectName("stackedWidget") 28 | 29 | self.gridLayout.addWidget(self.stackedWidget, 3, 0, 1, 1) 30 | self.widget = QtWidgets.QWidget(self.centralwidget) 31 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 32 | sizePolicy.setHorizontalStretch(0) 33 | sizePolicy.setVerticalStretch(0) 34 | sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth()) 35 | self.widget.setSizePolicy(sizePolicy) 36 | self.widget.setMinimumSize(QtCore.QSize(0, 350)) 37 | self.widget.setObjectName("widget") 38 | self.gridLayout.addWidget(self.widget, 0, 1, 4, 1) 39 | self.horizontalLayout = QtWidgets.QHBoxLayout() 40 | self.horizontalLayout.setObjectName("horizontalLayout") 41 | self.pushButton = QtWidgets.QPushButton(self.centralwidget) 42 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 43 | sizePolicy.setHorizontalStretch(0) 44 | sizePolicy.setVerticalStretch(0) 45 | sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth()) 46 | self.pushButton.setSizePolicy(sizePolicy) 47 | self.pushButton.setObjectName("pushButton") 48 | self.horizontalLayout.addWidget(self.pushButton) 49 | self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget) 50 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 51 | sizePolicy.setHorizontalStretch(0) 52 | sizePolicy.setVerticalStretch(0) 53 | sizePolicy.setHeightForWidth(self.pushButton_2.sizePolicy().hasHeightForWidth()) 54 | self.pushButton_2.setSizePolicy(sizePolicy) 55 | self.pushButton_2.setObjectName("pushButton_2") 56 | self.horizontalLayout.addWidget(self.pushButton_2) 57 | self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget) 58 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) 59 | sizePolicy.setHorizontalStretch(0) 60 | sizePolicy.setVerticalStretch(0) 61 | sizePolicy.setHeightForWidth(self.pushButton_3.sizePolicy().hasHeightForWidth()) 62 | self.pushButton_3.setSizePolicy(sizePolicy) 63 | self.pushButton_3.setObjectName("pushButton_3") 64 | self.horizontalLayout.addWidget(self.pushButton_3) 65 | self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1) 66 | spacerItem = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) 67 | self.gridLayout.addItem(spacerItem, 2, 0, 1, 1) 68 | self.gridLayout.setColumnStretch(0, 5) 69 | self.gridLayout.setColumnStretch(1, 2) 70 | self.verticalLayout.addLayout(self.gridLayout) 71 | MainWindow.setCentralWidget(self.centralwidget) 72 | self.menubar = QtWidgets.QMenuBar(MainWindow) 73 | #self.menubar.setGeometry(QtCore.QRect(50, 50, 791, 23)) 74 | self.menubar.setObjectName("menubar") 75 | self.menu = QtWidgets.QMenu(self.menubar) 76 | self.menu.setObjectName("menu") 77 | self.menu_2 = QtWidgets.QMenu(self.menubar) 78 | self.menu_2.setObjectName("menu_2") 79 | MainWindow.setMenuBar(self.menubar) 80 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 81 | self.statusbar.setObjectName("statusbar") 82 | MainWindow.setStatusBar(self.statusbar) 83 | self.action = QtWidgets.QAction(MainWindow) 84 | self.action.setObjectName("action") 85 | self.action_2 = QtWidgets.QAction(MainWindow) 86 | self.action_2.setObjectName("action_2") 87 | self.action_3 = QtWidgets.QAction(MainWindow) 88 | self.action_3.setObjectName("action_3") 89 | self.menu.addAction(self.action_2) 90 | self.menu.addAction(self.action_3) 91 | self.menu_2.addAction(self.action) 92 | self.menubar.addAction(self.menu.menuAction()) 93 | self.menubar.addAction(self.menu_2.menuAction()) 94 | 95 | self.retranslateUi(MainWindow) 96 | self.action_2.triggered.connect(MainWindow.hide) 97 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 98 | 99 | def retranslateUi(self, MainWindow): 100 | _translate = QtCore.QCoreApplication.translate 101 | MainWindow.setWindowTitle(_translate("MainWindow", "WQA_Main")) 102 | self.pushButton.setText(_translate("MainWindow", "历史")) 103 | self.pushButton_2.setText(_translate("MainWindow", "分析")) 104 | self.pushButton_3.setText(_translate("MainWindow", "无人机")) 105 | self.menu.setTitle(_translate("MainWindow", "用户")) 106 | self.menu_2.setTitle(_translate("MainWindow", "帮助")) 107 | self.action.setText(_translate("MainWindow", "关于")) 108 | self.action_2.setText(_translate("MainWindow", "锁定")) 109 | self.action_3.setText(_translate("MainWindow", "注销")) 110 | 111 | 112 | if __name__ == "__main__": 113 | import sys 114 | app = QtWidgets.QApplication(sys.argv) 115 | MainWindow = QtWidgets.QMainWindow() 116 | ui = Ui_MainWindow() 117 | ui.setupUi(MainWindow) 118 | MainWindow.showMaximized() 119 | sys.exit(app.exec_()) 120 | 121 | -------------------------------------------------------------------------------- /code/controller/player.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Q_ARG, QAbstractItemModel, 5 | QFileInfo, qFuzzyCompare, QMetaObject, QModelIndex, QObject, Qt, 6 | QThread, QTime, QUrl) 7 | from PyQt5.QtGui import QColor, qGray, QImage, QPainter, QPalette 8 | from PyQt5.QtMultimedia import (QAbstractVideoBuffer, QMediaContent, 9 | QMediaMetaData, QMediaPlayer, QMediaPlaylist, QVideoFrame, QVideoProbe) 10 | from PyQt5.QtMultimediaWidgets import QVideoWidget 11 | from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog, QFileDialog, 12 | QFormLayout, QHBoxLayout, QLabel, QListView, QMessageBox, QPushButton, 13 | QSizePolicy, QSlider, QStyle, QToolButton, QVBoxLayout, QWidget) 14 | 15 | 16 | class VideoWidget(QVideoWidget): 17 | 18 | def __init__(self, parent=None): 19 | super(VideoWidget, self).__init__(parent) 20 | 21 | self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) 22 | 23 | p = self.palette() 24 | p.setColor(QPalette.Window, Qt.black) 25 | self.setPalette(p) 26 | 27 | self.setAttribute(Qt.WA_OpaquePaintEvent) 28 | 29 | def keyPressEvent(self, event): 30 | if event.key() == Qt.Key_Escape and self.isFullScreen(): 31 | self.setFullScreen(False) 32 | event.accept() 33 | elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt: 34 | self.setFullScreen(not self.isFullScreen()) 35 | event.accept() 36 | else: 37 | super(VideoWidget, self).keyPressEvent(event) 38 | 39 | def mouseDoubleClickEvent(self, event): 40 | self.setFullScreen(not self.isFullScreen()) 41 | event.accept() 42 | 43 | 44 | class PlaylistModel(QAbstractItemModel): 45 | 46 | Title, ColumnCount = range(2) 47 | 48 | def __init__(self, parent=None): 49 | super(PlaylistModel, self).__init__(parent) 50 | 51 | self.m_playlist = None 52 | 53 | def rowCount(self, parent=QModelIndex()): 54 | return self.m_playlist.mediaCount() if self.m_playlist is not None and not parent.isValid() else 0 55 | 56 | def columnCount(self, parent=QModelIndex()): 57 | return self.ColumnCount if not parent.isValid() else 0 58 | 59 | def index(self, row, column, parent=QModelIndex()): 60 | return self.createIndex(row, column) if self.m_playlist is not None and not parent.isValid() and row >= 0 and row < self.m_playlist.mediaCount() and column >= 0 and column < self.ColumnCount else QModelIndex() 61 | 62 | def parent(self, child): 63 | return QModelIndex() 64 | 65 | def data(self, index, role=Qt.DisplayRole): 66 | if index.isValid() and role == Qt.DisplayRole: 67 | if index.column() == self.Title: 68 | location = self.m_playlist.media(index.row()).canonicalUrl() 69 | return QFileInfo(location.path()).fileName() 70 | 71 | return self.m_data[index] 72 | 73 | return None 74 | 75 | def playlist(self): 76 | return self.m_playlist 77 | 78 | def setPlaylist(self, playlist): 79 | if self.m_playlist is not None: 80 | self.m_playlist.mediaAboutToBeInserted.disconnect( 81 | self.beginInsertItems) 82 | self.m_playlist.mediaInserted.disconnect(self.endInsertItems) 83 | self.m_playlist.mediaAboutToBeRemoved.disconnect( 84 | self.beginRemoveItems) 85 | self.m_playlist.mediaRemoved.disconnect(self.endRemoveItems) 86 | self.m_playlist.mediaChanged.disconnect(self.changeItems) 87 | 88 | self.beginResetModel() 89 | self.m_playlist = playlist 90 | 91 | if self.m_playlist is not None: 92 | self.m_playlist.mediaAboutToBeInserted.connect( 93 | self.beginInsertItems) 94 | self.m_playlist.mediaInserted.connect(self.endInsertItems) 95 | self.m_playlist.mediaAboutToBeRemoved.connect( 96 | self.beginRemoveItems) 97 | self.m_playlist.mediaRemoved.connect(self.endRemoveItems) 98 | self.m_playlist.mediaChanged.connect(self.changeItems) 99 | 100 | self.endResetModel() 101 | 102 | def beginInsertItems(self, start, end): 103 | self.beginInsertRows(QModelIndex(), start, end) 104 | 105 | def endInsertItems(self): 106 | self.endInsertRows() 107 | 108 | def beginRemoveItems(self, start, end): 109 | self.beginRemoveRows(QModelIndex(), start, end) 110 | 111 | def endRemoveItems(self): 112 | self.endRemoveRows() 113 | 114 | def changeItems(self, start, end): 115 | self.dataChanged.emit(self.index(start, 0), 116 | self.index(end, self.ColumnCount)) 117 | 118 | 119 | class PlayerControls(QWidget): 120 | 121 | play = pyqtSignal() 122 | pause = pyqtSignal() 123 | stop = pyqtSignal() 124 | next = pyqtSignal() 125 | previous = pyqtSignal() 126 | changeVolume = pyqtSignal(int) 127 | changeMuting = pyqtSignal(bool) 128 | changeRate = pyqtSignal(float) 129 | 130 | def __init__(self, parent=None): 131 | super(PlayerControls, self).__init__(parent) 132 | 133 | self.playerState = QMediaPlayer.StoppedState 134 | self.playerMuted = False 135 | 136 | self.playButton = QToolButton(clicked=self.playClicked) 137 | self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) 138 | 139 | self.stopButton = QToolButton(clicked=self.stop) 140 | self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop)) 141 | self.stopButton.setEnabled(False) 142 | 143 | self.nextButton = QToolButton(clicked=self.next) 144 | self.nextButton.setIcon( 145 | self.style().standardIcon(QStyle.SP_MediaSkipForward)) 146 | 147 | self.previousButton = QToolButton(clicked=self.previous) 148 | self.previousButton.setIcon( 149 | self.style().standardIcon(QStyle.SP_MediaSkipBackward)) 150 | 151 | self.muteButton = QToolButton(clicked=self.muteClicked) 152 | self.muteButton.setIcon( 153 | self.style().standardIcon(QStyle.SP_MediaVolume)) 154 | 155 | self.volumeSlider = QSlider(Qt.Horizontal, 156 | sliderMoved=self.changeVolume) 157 | self.volumeSlider.setRange(0, 100) 158 | 159 | self.rateBox = QComboBox(activated=self.updateRate) 160 | self.rateBox.addItem("0.5x", 0.5) 161 | self.rateBox.addItem("1.0x", 1.0) 162 | self.rateBox.addItem("2.0x", 2.0) 163 | self.rateBox.setCurrentIndex(1) 164 | 165 | layout = QHBoxLayout() 166 | layout.setContentsMargins(0, 0, 0, 0) 167 | layout.addWidget(self.stopButton) 168 | layout.addWidget(self.previousButton) 169 | layout.addWidget(self.playButton) 170 | layout.addWidget(self.nextButton) 171 | layout.addWidget(self.muteButton) 172 | layout.addWidget(self.volumeSlider) 173 | layout.addWidget(self.rateBox) 174 | self.setLayout(layout) 175 | 176 | def state(self): 177 | return self.playerState 178 | 179 | def setState(self,state): 180 | if state != self.playerState: 181 | self.playerState = state 182 | 183 | if state == QMediaPlayer.StoppedState: 184 | self.stopButton.setEnabled(False) 185 | self.playButton.setIcon( 186 | self.style().standardIcon(QStyle.SP_MediaPlay)) 187 | elif state == QMediaPlayer.PlayingState: 188 | self.stopButton.setEnabled(True) 189 | self.playButton.setIcon( 190 | self.style().standardIcon(QStyle.SP_MediaPause)) 191 | elif state == QMediaPlayer.PausedState: 192 | self.stopButton.setEnabled(True) 193 | self.playButton.setIcon( 194 | self.style().standardIcon(QStyle.SP_MediaPlay)) 195 | 196 | def volume(self): 197 | return self.volumeSlider.value() 198 | 199 | def setVolume(self, volume): 200 | self.volumeSlider.setValue(volume) 201 | 202 | def isMuted(self): 203 | return self.playerMuted 204 | 205 | def setMuted(self, muted): 206 | if muted != self.playerMuted: 207 | self.playerMuted = muted 208 | 209 | self.muteButton.setIcon( 210 | self.style().standardIcon( 211 | QStyle.SP_MediaVolumeMuted if muted else QStyle.SP_MediaVolume)) 212 | 213 | def playClicked(self): 214 | if self.playerState in (QMediaPlayer.StoppedState, QMediaPlayer.PausedState): 215 | self.play.emit() 216 | elif self.playerState == QMediaPlayer.PlayingState: 217 | self.pause.emit() 218 | 219 | def muteClicked(self): 220 | self.changeMuting.emit(not self.playerMuted) 221 | 222 | def playbackRate(self): 223 | return self.rateBox.itemData(self.rateBox.currentIndex()) 224 | 225 | def setPlaybackRate(self, rate): 226 | for i in range(self.rateBox.count()): 227 | if qFuzzyCompare(rate, self.rateBox.itemData(i)): 228 | self.rateBox.setCurrentIndex(i) 229 | return 230 | 231 | self.rateBox.addItem("%dx" % rate, rate) 232 | self.rateBox.setCurrentIndex(self.rateBox.count() - 1) 233 | 234 | def updateRate(self): 235 | self.changeRate.emit(self.playbackRate()) 236 | 237 | 238 | class FrameProcessor(QObject): 239 | 240 | histogramReady = pyqtSignal(list) 241 | 242 | @pyqtSlot(QVideoFrame, int) 243 | def processFrame(self, frame, levels): 244 | histogram = [0.0] * levels 245 | 246 | if levels and frame.map(QAbstractVideoBuffer.ReadOnly): 247 | pixelFormat = frame.pixelFormat() 248 | 249 | if pixelFormat == QVideoFrame.Format_YUV420P or pixelFormat == QVideoFrame.Format_NV12: 250 | # Process YUV data. 251 | bits = frame.bits() 252 | for idx in range(frame.height() * frame.width()): 253 | histogram[(bits[idx] * levels) >> 8] += 1.0 254 | else: 255 | imageFormat = QVideoFrame.imageFormatFromPixelFormat(pixelFormat) 256 | if imageFormat != QImage.Format_Invalid: 257 | # Process RGB data. 258 | image = QImage(frame.bits(), frame.width(), frame.height(), imageFormat) 259 | 260 | for y in range(image.height()): 261 | for x in range(image.width()): 262 | pixel = image.pixel(x, y) 263 | histogram[(qGray(pixel) * levels) >> 8] += 1.0 264 | 265 | # Find the maximum value. 266 | maxValue = 0.0 267 | for value in histogram: 268 | if value > maxValue: 269 | maxValue = value 270 | 271 | # Normalise the values between 0 and 1. 272 | if maxValue > 0.0: 273 | for i in range(len(histogram)): 274 | histogram[i] /= maxValue 275 | 276 | frame.unmap() 277 | 278 | self.histogramReady.emit(histogram) 279 | 280 | 281 | class HistogramWidget(QWidget): 282 | 283 | def __init__(self, parent=None): 284 | super(HistogramWidget, self).__init__(parent) 285 | 286 | self.m_levels = 128 287 | self.m_isBusy = False 288 | self.m_histogram = [] 289 | self.m_processor = FrameProcessor() 290 | self.m_processorThread = QThread() 291 | 292 | self.m_processor.moveToThread(self.m_processorThread) 293 | self.m_processor.histogramReady.connect(self.setHistogram) 294 | 295 | def __del__(self): 296 | self.m_processorThread.quit() 297 | self.m_processorThread.wait(10000) 298 | 299 | def setLevels(self, levels): 300 | self.m_levels = levels 301 | 302 | def processFrame(self, frame): 303 | if self.m_isBusy: 304 | return 305 | 306 | self.m_isBusy = True 307 | QMetaObject.invokeMethod(self.m_processor, 'processFrame', 308 | Qt.QueuedConnection, Q_ARG(QVideoFrame, frame), 309 | Q_ARG(int, self.m_levels)) 310 | 311 | @pyqtSlot(list) 312 | def setHistogram(self, histogram): 313 | self.m_isBusy = False 314 | self.m_histogram = list(histogram) 315 | self.update() 316 | 317 | def paintEvent(self, event): 318 | painter = QPainter(self) 319 | 320 | if len(self.m_histogram) == 0: 321 | painter.fillRect(0, 0, self.width(), self.height(), 322 | QColor.fromRgb(0, 0, 0)) 323 | return 324 | 325 | barWidth = self.width() / float(len(self.m_histogram)) 326 | 327 | for i, value in enumerate(self.m_histogram): 328 | h = value * self.height() 329 | # Draw the level. 330 | painter.fillRect(barWidth * i, self.height() - h, 331 | barWidth * (i + 1), self.height(), Qt.red) 332 | # Clear the rest of the control. 333 | painter.fillRect(barWidth * i, 0, barWidth * (i + 1), 334 | self.height() - h, Qt.black) 335 | 336 | 337 | class Player(QWidget): 338 | 339 | fullScreenChanged = pyqtSignal(bool) 340 | 341 | def __init__(self, playlist, parent=None): 342 | super(Player, self).__init__(parent) 343 | 344 | self.colorDialog = None 345 | self.trackInfo = "" 346 | self.statusInfo = "" 347 | self.duration = 0 348 | 349 | self.player = QMediaPlayer() 350 | self.playlist = QMediaPlaylist() 351 | self.player.setPlaylist(self.playlist) 352 | 353 | self.player.durationChanged.connect(self.durationChanged) 354 | self.player.positionChanged.connect(self.positionChanged) 355 | self.player.metaDataChanged.connect(self.metaDataChanged) 356 | self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) 357 | self.player.mediaStatusChanged.connect(self.statusChanged) 358 | self.player.bufferStatusChanged.connect(self.bufferingProgress) 359 | self.player.videoAvailableChanged.connect(self.videoAvailableChanged) 360 | self.player.error.connect(self.displayErrorMessage) 361 | 362 | self.videoWidget = VideoWidget() 363 | self.player.setVideoOutput(self.videoWidget) 364 | 365 | self.playlistModel = PlaylistModel() 366 | self.playlistModel.setPlaylist(self.playlist) 367 | 368 | self.playlistView = QListView() 369 | self.playlistView.setModel(self.playlistModel) 370 | self.playlistView.setCurrentIndex( 371 | self.playlistModel.index(self.playlist.currentIndex(), 0)) 372 | 373 | self.playlistView.activated.connect(self.jump) 374 | 375 | self.slider = QSlider(Qt.Horizontal) 376 | self.slider.setRange(0, self.player.duration() / 1000) 377 | 378 | self.labelDuration = QLabel() 379 | self.slider.sliderMoved.connect(self.seek) 380 | 381 | # self.labelHistogram = QLabel() 382 | # self.labelHistogram.setText("Histogram:") 383 | # self.histogram = HistogramWidget() 384 | # histogramLayout = QHBoxLayout() 385 | # histogramLayout.addWidget(self.labelHistogram) 386 | # histogramLayout.addWidget(self.histogram, 1) 387 | 388 | self.probe = QVideoProbe() 389 | # self.probe.videoFrameProbed.connect(self.histogram.processFrame) 390 | self.probe.setSource(self.player) 391 | 392 | openButton = QPushButton("打开", clicked=self.open) 393 | 394 | controls = PlayerControls() 395 | controls.setState(self.player.state()) 396 | controls.setVolume(self.player.volume()) 397 | controls.setMuted(controls.isMuted()) 398 | 399 | controls.play.connect(self.player.play) 400 | controls.pause.connect(self.player.pause) 401 | controls.stop.connect(self.player.stop) 402 | controls.next.connect(self.playlist.next) 403 | controls.previous.connect(self.previousClicked) 404 | controls.changeVolume.connect(self.player.setVolume) 405 | controls.changeMuting.connect(self.player.setMuted) 406 | controls.changeRate.connect(self.player.setPlaybackRate) 407 | controls.stop.connect(self.videoWidget.update) 408 | 409 | self.player.stateChanged.connect(controls.setState) 410 | self.player.volumeChanged.connect(controls.setVolume) 411 | self.player.mutedChanged.connect(controls.setMuted) 412 | 413 | self.fullScreenButton = QPushButton("全屏") 414 | self.fullScreenButton.setCheckable(True) 415 | 416 | self.colorButton = QPushButton("颜色选项") 417 | self.colorButton.setEnabled(False) 418 | self.colorButton.clicked.connect(self.showColorDialog) 419 | 420 | displayLayout = QHBoxLayout() 421 | displayLayout.addWidget(self.videoWidget, 2) 422 | displayLayout.addWidget(self.playlistView) 423 | 424 | controlLayout = QHBoxLayout() 425 | controlLayout.setContentsMargins(0, 0, 0, 0) 426 | controlLayout.addWidget(openButton) 427 | controlLayout.addStretch(1) 428 | controlLayout.addWidget(controls) 429 | controlLayout.addStretch(1) 430 | controlLayout.addWidget(self.fullScreenButton) 431 | controlLayout.addWidget(self.colorButton) 432 | 433 | layout = QVBoxLayout() 434 | layout.addLayout(displayLayout) 435 | hLayout = QHBoxLayout() 436 | hLayout.addWidget(self.slider) 437 | hLayout.addWidget(self.labelDuration) 438 | layout.addLayout(hLayout) 439 | layout.addLayout(controlLayout) 440 | # layout.addLayout(histogramLayout) 441 | 442 | self.setLayout(layout) 443 | 444 | if not self.player.isAvailable(): 445 | QMessageBox.warning(self, "Service not available", 446 | "The QMediaPlayer object does not have a valid service.\n" 447 | "Please check the media service plugins are installed.") 448 | 449 | controls.setEnabled(False) 450 | self.playlistView.setEnabled(False) 451 | openButton.setEnabled(False) 452 | self.colorButton.setEnabled(False) 453 | self.fullScreenButton.setEnabled(False) 454 | 455 | self.metaDataChanged() 456 | 457 | self.addToPlaylist(playlist) 458 | 459 | def open(self): 460 | fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") 461 | self.addToPlaylist(fileNames) 462 | 463 | def addToPlaylist(self, fileNames): 464 | for name in fileNames: 465 | fileInfo = QFileInfo(name) 466 | if fileInfo.exists(): 467 | url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) 468 | if fileInfo.suffix().lower() == 'm3u': 469 | self.playlist.load(url) 470 | else: 471 | self.playlist.addMedia(QMediaContent(url)) 472 | else: 473 | url = QUrl(name) 474 | if url.isValid(): 475 | self.playlist.addMedia(QMediaContent(url)) 476 | 477 | def durationChanged(self, duration): 478 | duration /= 1000 479 | 480 | self.duration = duration 481 | self.slider.setMaximum(duration) 482 | 483 | def positionChanged(self, progress): 484 | progress /= 1000 485 | 486 | if not self.slider.isSliderDown(): 487 | self.slider.setValue(progress) 488 | 489 | self.updateDurationInfo(progress) 490 | 491 | def metaDataChanged(self): 492 | if self.player.isMetaDataAvailable(): 493 | self.setTrackInfo("%s - %s" % ( 494 | self.player.metaData(QMediaMetaData.AlbumArtist), 495 | self.player.metaData(QMediaMetaData.Title))) 496 | 497 | def previousClicked(self): 498 | # Go to the previous track if we are within the first 5 seconds of 499 | # playback. Otherwise, seek to the beginning. 500 | if self.player.position() <= 5000: 501 | self.playlist.previous() 502 | else: 503 | self.player.setPosition(0) 504 | 505 | def jump(self, index): 506 | if index.isValid(): 507 | self.playlist.setCurrentIndex(index.row()) 508 | self.player.play() 509 | 510 | def playlistPositionChanged(self, position): 511 | self.playlistView.setCurrentIndex( 512 | self.playlistModel.index(position, 0)) 513 | 514 | def seek(self, seconds): 515 | self.player.setPosition(seconds * 1000) 516 | 517 | def statusChanged(self, status): 518 | self.handleCursor(status) 519 | 520 | if status == QMediaPlayer.LoadingMedia: 521 | self.setStatusInfo("Loading...") 522 | elif status == QMediaPlayer.StalledMedia: 523 | self.setStatusInfo("Media Stalled") 524 | elif status == QMediaPlayer.EndOfMedia: 525 | QApplication.alert(self) 526 | elif status == QMediaPlayer.InvalidMedia: 527 | self.displayErrorMessage() 528 | else: 529 | self.setStatusInfo("") 530 | 531 | def handleCursor(self, status): 532 | if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): 533 | self.setCursor(Qt.BusyCursor) 534 | else: 535 | self.unsetCursor() 536 | 537 | def bufferingProgress(self, progress): 538 | self.setStatusInfo("Buffering %d%" % progress) 539 | 540 | def videoAvailableChanged(self, available): 541 | if available: 542 | self.fullScreenButton.clicked.connect( 543 | self.videoWidget.setFullScreen) 544 | self.videoWidget.fullScreenChanged.connect( 545 | self.fullScreenButton.setChecked) 546 | 547 | if self.fullScreenButton.isChecked(): 548 | self.videoWidget.setFullScreen(True) 549 | else: 550 | self.fullScreenButton.clicked.disconnect( 551 | self.videoWidget.setFullScreen) 552 | self.videoWidget.fullScreenChanged.disconnect( 553 | self.fullScreenButton.setChecked) 554 | 555 | self.videoWidget.setFullScreen(False) 556 | 557 | self.colorButton.setEnabled(available) 558 | 559 | def setTrackInfo(self, info): 560 | self.trackInfo = info 561 | 562 | if self.statusInfo != "": 563 | self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) 564 | else: 565 | self.setWindowTitle(self.trackInfo) 566 | 567 | def setStatusInfo(self, info): 568 | self.statusInfo = info 569 | 570 | if self.statusInfo != "": 571 | self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) 572 | else: 573 | self.setWindowTitle(self.trackInfo) 574 | 575 | def displayErrorMessage(self): 576 | self.setStatusInfo(self.player.errorString()) 577 | 578 | def updateDurationInfo(self, currentInfo): 579 | duration = self.duration 580 | if currentInfo or duration: 581 | currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, 582 | currentInfo%60, (currentInfo*1000)%1000) 583 | totalTime = QTime((duration/3600)%60, (duration/60)%60, 584 | duration%60, (duration*1000)%1000); 585 | 586 | format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' 587 | tStr = currentTime.toString(format) + " / " + totalTime.toString(format) 588 | else: 589 | tStr = "" 590 | 591 | self.labelDuration.setText(tStr) 592 | 593 | def showColorDialog(self): 594 | if self.colorDialog is None: 595 | brightnessSlider = QSlider(Qt.Horizontal) 596 | brightnessSlider.setRange(-100, 100) 597 | brightnessSlider.setValue(self.videoWidget.brightness()) 598 | brightnessSlider.sliderMoved.connect( 599 | self.videoWidget.setBrightness) 600 | self.videoWidget.brightnessChanged.connect( 601 | brightnessSlider.setValue) 602 | 603 | contrastSlider = QSlider(Qt.Horizontal) 604 | contrastSlider.setRange(-100, 100) 605 | contrastSlider.setValue(self.videoWidget.contrast()) 606 | contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) 607 | self.videoWidget.contrastChanged.connect(contrastSlider.setValue) 608 | 609 | hueSlider = QSlider(Qt.Horizontal) 610 | hueSlider.setRange(-100, 100) 611 | hueSlider.setValue(self.videoWidget.hue()) 612 | hueSlider.sliderMoved.connect(self.videoWidget.setHue) 613 | self.videoWidget.hueChanged.connect(hueSlider.setValue) 614 | 615 | saturationSlider = QSlider(Qt.Horizontal) 616 | saturationSlider.setRange(-100, 100) 617 | saturationSlider.setValue(self.videoWidget.saturation()) 618 | saturationSlider.sliderMoved.connect( 619 | self.videoWidget.setSaturation) 620 | self.videoWidget.saturationChanged.connect( 621 | saturationSlider.setValue) 622 | 623 | layout = QFormLayout() 624 | layout.addRow("亮度", brightnessSlider) 625 | layout.addRow("对比度", contrastSlider) 626 | layout.addRow("色调", hueSlider) 627 | layout.addRow("饱和度", saturationSlider) 628 | 629 | button = QPushButton("关闭") 630 | layout.addRow(button) 631 | 632 | self.colorDialog = QDialog(self) 633 | self.colorDialog.setWindowTitle("颜色选项") 634 | self.colorDialog.setLayout(layout) 635 | 636 | button.clicked.connect(self.colorDialog.close) 637 | 638 | self.colorDialog.show() 639 | 640 | 641 | if __name__ == '__main__': 642 | 643 | import sys 644 | 645 | app = QApplication(sys.argv) 646 | 647 | player = Player(sys.argv[1:]) 648 | player.show() 649 | 650 | sys.exit(app.exec_()) 651 | --------------------------------------------------------------------------------