├── .gitignore ├── main.icns ├── db └── influx.db ├── describe.png ├── template.xls ├── images ├── exec.ico ├── form.ico ├── main.ico ├── history.ico ├── exec_acitve.ico ├── server_close.ico ├── server_open.ico ├── database_close.ico └── database_open.ico ├── utils.py ├── README.md ├── requirements.txt ├── .github └── workflows │ └── superlinter.yml ├── setup.py ├── main.spec ├── constant.py ├── create_database.py ├── import_ui.py ├── MyTextEdit.py ├── create_ui.py ├── history_ui.py ├── new_connect_ui.py ├── ui.py └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | venv/ 4 | setup.app/ 5 | .idea/ 6 | __pycache__/ -------------------------------------------------------------------------------- /main.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/main.icns -------------------------------------------------------------------------------- /db/influx.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/db/influx.db -------------------------------------------------------------------------------- /describe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/describe.png -------------------------------------------------------------------------------- /template.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/template.xls -------------------------------------------------------------------------------- /images/exec.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/exec.ico -------------------------------------------------------------------------------- /images/form.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/form.ico -------------------------------------------------------------------------------- /images/main.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/main.ico -------------------------------------------------------------------------------- /images/history.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/history.ico -------------------------------------------------------------------------------- /images/exec_acitve.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/exec_acitve.ico -------------------------------------------------------------------------------- /images/server_close.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/server_close.ico -------------------------------------------------------------------------------- /images/server_open.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/server_open.ico -------------------------------------------------------------------------------- /images/database_close.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/database_close.ico -------------------------------------------------------------------------------- /images/database_open.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d270624/InfluxDB-MacOS-Desktop/HEAD/images/database_open.ico -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 _*- 2 | from PyQt5.QtCore import QThread, pyqtSignal 3 | 4 | 5 | class Runthread(QThread): # 步骤1.创建一个线程实例 6 | signal = pyqtSignal() # 创建一个自定义信号 7 | 8 | def __init__(self): # 通过初始化赋值的方式实现UI主线程传递值给子线程 9 | super(Runthread, self).__init__() 10 | 11 | def run(self): 12 | self.signal.emit() # 发射自定义信号 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 此工具适用于 InfluxDB 1.x,不支持InfluxDB 2.x 2 | 3 | 该脚本前端使用PYQT5编写,因为目前没有在GIT上找到MAC版Influx桌面版管理工具,所以开发了这款软件 4 | 5 | 使用py2app打包,兼容MAC 10.X, 11.X版本系统,因为只在这两个系统中测试过 6 | 7 | 注意: 8 | 9 | 1、更新软件会清空服务器列表,更新请先导出配置,更新后再导入。 10 | 11 | 2、模板中ssl填0代表不使用SSL,填2表示启用SSL 12 | 13 | 3、标个星再走 14 | 15 | 16 | 软件截图: 17 | ![1211609384821_.pic.jpg](https://i.jpg.dog/img/1495f37eef1975a78ce3f15d4432c735.png) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17 2 | certifi==2020.12.5 3 | chardet==4.0.0 4 | et-xmlfile==1.0.1 5 | idna==2.10 6 | influxdb==5.3.1 7 | jdcal==1.4.1 8 | macholib==1.16 9 | modulegraph==0.18 10 | msgpack==1.0.2 11 | numpy==1.19.4 12 | openpyxl==3.0.5 13 | pandas==1.1.5 14 | py2app==0.28 15 | PyInstaller==3.6 16 | PyQt5==5.15.2 17 | PyQt5-sip==12.8.1 18 | python-dateutil==2.8.1 19 | pytz==2020.4 20 | requests==2.25.1 21 | six==1.15.0 22 | urllib3==1.26.4 23 | xlrd==2.0.1 24 | xlwt==1.3.0 25 | -------------------------------------------------------------------------------- /.github/workflows/superlinter.yml: -------------------------------------------------------------------------------- 1 | name: Super-Linter 2 | 3 | # Run this workflow every time a new commit pushed to your repository 4 | on: push 5 | 6 | jobs: 7 | # Set the job key. The key is displayed as the job name 8 | # when a job name is not provided 9 | super-lint: 10 | # Name the Job 11 | name: Lint code base 12 | # Set the type of machine to run on 13 | runs-on: node 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - run: echo "Run your script here" 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a setup.py script generated by py2applet 3 | 4 | Usage: 5 | python setup.py py2app 6 | """ 7 | 8 | from setuptools import setup 9 | 10 | APP = ['main.py'] 11 | APP_NAME = "InfluxDBClient" 12 | DATA_FILES = ['main.py', 'create_database.py', 'create_ui.py', 'history_ui.py', 'new_connect_ui.py', 'ui.py', 'db', 13 | 'images'] 14 | PACKAGES = ['pandas'] 15 | OPTIONS = {'iconfile': 'main.icns', 'packages': PACKAGES} 16 | 17 | setup( 18 | app=APP, 19 | name=APP_NAME, 20 | data_files=DATA_FILES, 21 | options={'py2app': OPTIONS}, 22 | setup_requires=['py2app'], 23 | ) 24 | -------------------------------------------------------------------------------- /main.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | block_cipher = None 4 | import os 5 | import sys 6 | path = os.path.dirname(sys.argv[0]) 7 | 8 | a = Analysis(['main.py','create_database.py','create_ui.py','history_ui.py','new_connect_ui.py','ui.py'], 9 | pathex=['/Volumes/soft/InfluxDBClientDesktop'], 10 | binaries=[], 11 | datas=[('images','images'),('db','db')], 12 | hiddenimports=[], 13 | hookspath=[], 14 | runtime_hooks=[], 15 | excludes=[], 16 | win_no_prefer_redirects=False, 17 | win_private_assemblies=False, 18 | cipher=block_cipher, 19 | noarchive=False) 20 | pyz = PYZ(a.pure, a.zipped_data, 21 | cipher=block_cipher) 22 | exe = EXE(pyz, 23 | a.scripts, 24 | [], 25 | exclude_binaries=True, 26 | name='InfluxDBClient', 27 | debug=False, 28 | bootloader_ignore_signals=False, 29 | strip=False, 30 | upx=True, 31 | console=False,icon=path + '/images/main.ico') 32 | coll = COLLECT(exe, 33 | a.binaries, 34 | a.zipfiles, 35 | a.datas, 36 | strip=False, 37 | upx=True, 38 | upx_exclude=[], 39 | name='InfluxDBClient') 40 | -------------------------------------------------------------------------------- /constant.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 _*- 2 | 3 | sql_constant = [ 4 | "ALL", 5 | "ALTER", 6 | "ANALYZE", 7 | "ANY", 8 | "AS", 9 | "ASC", 10 | "BEGIN", 11 | "CREATE", 12 | "CONTINUOUS", 13 | "DATABASE", 14 | "DATABASES", 15 | "DEFAULT", 16 | "DELETE", 17 | "DESC", 18 | "DESTINATIONS", 19 | "DIAGNOSTICS", 20 | "DISTINCT", 21 | "DROP", 22 | "DURATION", 23 | "END", 24 | "EVERY", 25 | "EXPLAIN", 26 | "FIELD", 27 | "FOR", 28 | "FROM", 29 | "GRANT", 30 | "GRANTS", 31 | "GROUPS", 32 | "IN", 33 | "INF", 34 | "INSERT", 35 | "INTO", 36 | "KEY", 37 | "KEYS", 38 | "KILL", 39 | "LIMIT", 40 | "SHOW", 41 | "MEASUREMENT", 42 | "MEASUREMENTS", 43 | "NAME", 44 | "OFFSET", 45 | "ON", 46 | "PASSWORD", 47 | "POLICY", 48 | "POLICIES", 49 | "PRIVILEGES", 50 | "QUERIES", 51 | "QUERY", 52 | "READ", 53 | "REPLICATION", 54 | "RESAMPLE", 55 | "RETENTION", 56 | "REVOKE", 57 | "SELECT", 58 | "SERIES", 59 | "SET", 60 | "SHARD", 61 | "SHARDS", 62 | "SLIMIT", 63 | "SOFFSET", 64 | "STATS", 65 | "SUBSCRIPTION", 66 | "SUBSCRIPTIONS", 67 | "TAG", 68 | "TO", 69 | "USER", 70 | "USERS", 71 | "VALUES", 72 | "WHERE", 73 | "WITH", 74 | "WRITE", 75 | "GROUP BY", 76 | "ORDER BY", 77 | ] 78 | -------------------------------------------------------------------------------- /create_database.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'create_database.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.0 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_create_database(object): 15 | def setupUi(self, create_database): 16 | create_database.setObjectName("create_database") 17 | create_database.resize(245, 94) 18 | self.pushButton = QtWidgets.QPushButton(create_database) 19 | self.pushButton.setGeometry(QtCore.QRect(90, 60, 71, 32)) 20 | self.pushButton.setObjectName("pushButton") 21 | self.widget = QtWidgets.QWidget(create_database) 22 | self.widget.setGeometry(QtCore.QRect(16, 11, 191, 41)) 23 | self.widget.setObjectName("widget") 24 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget) 25 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0) 26 | self.horizontalLayout.setObjectName("horizontalLayout") 27 | self.label = QtWidgets.QLabel(self.widget) 28 | self.label.setObjectName("label") 29 | self.horizontalLayout.addWidget(self.label) 30 | self.lineEdit = QtWidgets.QLineEdit(self.widget) 31 | self.lineEdit.setObjectName("lineEdit") 32 | self.horizontalLayout.addWidget(self.lineEdit) 33 | 34 | self.retranslateUi(create_database) 35 | QtCore.QMetaObject.connectSlotsByName(create_database) 36 | 37 | def retranslateUi(self, create_database): 38 | _translate = QtCore.QCoreApplication.translate 39 | create_database.setWindowTitle(_translate("create_database", "创建数据库")) 40 | self.pushButton.setText(_translate("create_database", "创建")) 41 | self.label.setText(_translate("create_database", "数据库名:")) 42 | -------------------------------------------------------------------------------- /import_ui.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtWidgets 2 | 3 | 4 | class Import_UI(object): 5 | import_QWidget = None 6 | 7 | def __init__(self, import_ui): 8 | import_ui.setObjectName("import_ui") 9 | import_ui.resize(506, 113) 10 | import_ui.setMinimumSize(QtCore.QSize(506, 113)) 11 | import_ui.setMaximumSize(QtCore.QSize(506, 113)) 12 | self.gridLayout = QtWidgets.QGridLayout(import_ui) 13 | self.gridLayout.setObjectName("gridLayout") 14 | self.label_3 = QtWidgets.QLabel(import_ui) 15 | self.label_3.setObjectName("label_3") 16 | self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1) 17 | self.browse_button = QtWidgets.QPushButton(import_ui) 18 | self.browse_button.setObjectName("browse_button") 19 | self.browse_button.clicked.connect(self.setBrowerPath) 20 | self.gridLayout.addWidget(self.browse_button, 1, 3, 1, 1) 21 | self.label = QtWidgets.QLabel(import_ui) 22 | self.label.setObjectName("label") 23 | self.gridLayout.addWidget(self.label, 5, 0, 1, 1) 24 | self.import_button = QtWidgets.QPushButton(import_ui) 25 | self.import_button.setObjectName("import_button") 26 | self.gridLayout.addWidget(self.import_button, 5, 3, 1, 1) 27 | self.browse_edit = QtWidgets.QLineEdit(import_ui) 28 | self.browse_edit.setReadOnly(True) 29 | self.browse_edit.setObjectName("browse_edit") 30 | self.gridLayout.addWidget(self.browse_edit, 1, 1, 1, 2) 31 | 32 | self.retranslateUi(import_ui) 33 | QtCore.QMetaObject.connectSlotsByName(import_ui) 34 | 35 | def retranslateUi(self, import_ui): 36 | _translate = QtCore.QCoreApplication.translate 37 | import_ui.setWindowTitle(_translate("Import server info", "导入服务器信息")) 38 | self.label_3.setText(_translate("1. Click Browse and select the template", "1、点击浏览,选择模板")) 39 | self.label.setText(_translate("2. Click the import", "2、点击导入")) 40 | self.browse_button.setText(_translate("Browse", "浏览")) 41 | self.import_button.setText(_translate("Import", "导入")) 42 | 43 | def setBrowerPath(self): 44 | file_name, filetype = QtWidgets.QFileDialog.getOpenFileName(self.import_QWidget, "浏览", ".", 45 | "Select Xls Files (*.xls)") 46 | self.browse_edit.setText(file_name) 47 | -------------------------------------------------------------------------------- /MyTextEdit.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtGui 2 | from PyQt5.QtCore import QStringListModel, Qt 3 | from PyQt5.QtWidgets import QPlainTextEdit, QCompleter 4 | 5 | 6 | class MyCompleter(QCompleter): 7 | 8 | def __init__(self, *args, **kwargs): 9 | super().__init__(*args, **kwargs) 10 | model = QStringListModel() 11 | model.setStringList(args[0]) 12 | self.setModel(model) 13 | 14 | 15 | class MyTextEdit(QPlainTextEdit): 16 | constant = [] 17 | 18 | def __init__(self, *args, **kwargs): 19 | super().__init__(*args, **kwargs) 20 | self.completer = MyCompleter(self.constant) 21 | self.completer.setWidget(self) 22 | self.completer.setCompletionMode(QCompleter.PopupCompletion) 23 | self.completer.setCaseSensitivity(Qt.CaseInsensitive) 24 | self.completer.activated.connect(self.insert_completion) 25 | 26 | def insert_completion(self, completion): 27 | if completion == self.completer.completionPrefix(): 28 | return 29 | text_cursor = self.textCursor() 30 | delete_char_len = len(self.completer.completionPrefix()) 31 | for x in range(delete_char_len): 32 | text_cursor.deletePreviousChar() 33 | 34 | text_cursor.insertText(completion) 35 | self.setTextCursor(text_cursor) 36 | 37 | def text_before_cursor(self): 38 | text_cursor = self.textCursor() 39 | text_cursor.select(QtGui.QTextCursor.WordUnderCursor) 40 | return text_cursor.selectedText() 41 | 42 | def keyPressEvent(self, e: QtGui.QKeyEvent) -> None: 43 | if self.completer.popup().isVisible(): 44 | key = e.key() 45 | if key in (Qt.Key_Enter, Qt.Key_Return): 46 | e.ignore() 47 | return 48 | 49 | super().keyPressEvent(e) 50 | 51 | text_before_cursor = self.text_before_cursor() 52 | # print('text', self.text_before_cursor()) 53 | if text_before_cursor != self.completer.currentCompletion(): 54 | if text_before_cursor != self.completer.completionPrefix(): 55 | text_before_cursor, self.completer.currentCompletion() 56 | self.completer.setCompletionPrefix(text_before_cursor) 57 | self.completer.popup().setCurrentIndex(self.completer.completionModel().index(0, 0)) 58 | 59 | cursor_rectangle = self.cursorRect() 60 | popup = self.completer.popup() 61 | cursor_rectangle.setWidth(popup.sizeHintForColumn(0) + popup.verticalScrollBar().sizeHint().width()) 62 | self.completer.complete(cursor_rectangle) 63 | else: 64 | self.completer.popup().hide() 65 | -------------------------------------------------------------------------------- /create_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'create_table.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.0 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Create_Ui_Form(object): 15 | def setupUi(self, Form): 16 | Form.setObjectName("Form") 17 | Form.resize(525, 330) 18 | Form.setMinimumSize(QtCore.QSize(525, 330)) 19 | Form.setMaximumSize(QtCore.QSize(525, 330)) 20 | self.textEdit = QtWidgets.QTextEdit(Form) 21 | self.textEdit.setGeometry(QtCore.QRect(30, 20, 471, 261)) 22 | self.textEdit.setObjectName("textEdit") 23 | self.pushButton = QtWidgets.QPushButton(Form) 24 | self.pushButton.setGeometry(QtCore.QRect(210, 285, 101, 41)) 25 | self.pushButton.setObjectName("pushButton") 26 | 27 | self.retranslateUi(Form) 28 | QtCore.QMetaObject.connectSlotsByName(Form) 29 | 30 | def retranslateUi(self, Form): 31 | _translate = QtCore.QCoreApplication.translate 32 | Form.setWindowTitle(_translate("Form", "创建表")) 33 | self.textEdit.setHtml(_translate("Form", "\n" 34 | "\n" 37 | "

[

\n" 38 | "

{

\n" 39 | "

"measurement": "table_name",

\n" 40 | "

"tags": {

\n" 41 | "

"stuid": "s123"

\n" 42 | "

},

\n" 43 | "

"fields": {

\n" 44 | "

"score": 89

\n" 45 | "

}

\n" 46 | "

}

\n" 47 | "

]

")) 48 | self.pushButton.setText(_translate("Form", "创建")) 49 | -------------------------------------------------------------------------------- /history_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'history.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.0 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_history_ui(object): 15 | def setupUi(self, history_ui): 16 | history_ui.setObjectName("history_ui") 17 | history_ui.resize(550, 435) 18 | history_ui.setMinimumSize(QtCore.QSize(550, 435)) 19 | history_ui.setMaximumSize(QtCore.QSize(550, 435)) 20 | history_ui.setFocusPolicy(QtCore.Qt.TabFocus) 21 | self.verticalLayoutWidget = QtWidgets.QWidget(history_ui) 22 | self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 531, 411)) 23 | self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") 24 | self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) 25 | self.verticalLayout.setContentsMargins(0, 0, 0, 0) 26 | self.verticalLayout.setObjectName("verticalLayout") 27 | self.tableWidget = QtWidgets.QTableWidget(self.verticalLayoutWidget) 28 | self.tableWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 29 | self.tableWidget.setAutoScroll(True) 30 | self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed) 31 | self.tableWidget.setDragEnabled(False) 32 | self.tableWidget.setDragDropMode(QtWidgets.QAbstractItemView.NoDragDrop) 33 | self.tableWidget.setTextElideMode(QtCore.Qt.ElideRight) 34 | self.tableWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 35 | self.tableWidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) 36 | self.tableWidget.setGridStyle(QtCore.Qt.SolidLine) 37 | self.tableWidget.setWordWrap(True) 38 | self.tableWidget.setCornerButtonEnabled(True) 39 | self.tableWidget.setObjectName("tableWidget") 40 | self.tableWidget.setColumnCount(2) 41 | self.tableWidget.setRowCount(0) 42 | item = QtWidgets.QTableWidgetItem() 43 | self.tableWidget.setHorizontalHeaderItem(0, item) 44 | item = QtWidgets.QTableWidgetItem() 45 | self.tableWidget.setHorizontalHeaderItem(1, item) 46 | self.tableWidget.horizontalHeader().setVisible(True) 47 | self.tableWidget.horizontalHeader().setCascadingSectionResizes(False) 48 | self.tableWidget.horizontalHeader().setHighlightSections(True) 49 | self.tableWidget.horizontalHeader().setSortIndicatorShown(False) 50 | self.tableWidget.horizontalHeader().setStretchLastSection(True) 51 | self.tableWidget.verticalHeader().setVisible(True) 52 | self.tableWidget.verticalHeader().setHighlightSections(True) 53 | self.tableWidget.verticalHeader().setMinimumSectionSize(21) 54 | self.tableWidget.verticalHeader().setSortIndicatorShown(False) 55 | self.tableWidget.verticalHeader().setStretchLastSection(False) 56 | self.verticalLayout.addWidget(self.tableWidget) 57 | self.clear = QtWidgets.QPushButton(self.verticalLayoutWidget) 58 | self.clear.setObjectName("clear") 59 | self.verticalLayout.addWidget(self.clear) 60 | 61 | self.retranslateUi(history_ui) 62 | QtCore.QMetaObject.connectSlotsByName(history_ui) 63 | 64 | def retranslateUi(self, history_ui): 65 | _translate = QtCore.QCoreApplication.translate 66 | history_ui.setWindowTitle(_translate("Only the last 100 records are displayed", "只显示最近100条记录")) 67 | self.tableWidget.setSortingEnabled(False) 68 | item = self.tableWidget.horizontalHeaderItem(0) 69 | item.setText(_translate("time", "时间")) 70 | item = self.tableWidget.horizontalHeaderItem(1) 71 | item.setText(_translate("sql", "语句")) 72 | self.clear.setText(_translate("clear history", "清空记录")) 73 | -------------------------------------------------------------------------------- /new_connect_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '456.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.0 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_Form(object): 15 | def setupUi(self, Form): 16 | Form.setObjectName("Form") 17 | Form.resize(398, 283) 18 | Form.setMinimumSize(QtCore.QSize(398, 283)) 19 | Form.setMaximumSize(QtCore.QSize(398, 283)) 20 | self.address = QtWidgets.QLineEdit(Form) 21 | self.address.setGeometry(QtCore.QRect(120, 70, 171, 21)) 22 | self.address.setToolTipDuration(-1) 23 | self.address.setCursorPosition(0) 24 | self.address.setObjectName("address") 25 | self.name = QtWidgets.QLineEdit(Form) 26 | self.name.setGeometry(QtCore.QRect(120, 30, 251, 21)) 27 | self.name.setToolTipDuration(-1) 28 | self.name.setObjectName("name") 29 | self.user = QtWidgets.QLineEdit(Form) 30 | self.user.setGeometry(QtCore.QRect(120, 110, 251, 21)) 31 | self.user.setObjectName("user") 32 | self.label = QtWidgets.QLabel(Form) 33 | self.label.setGeometry(QtCore.QRect(30, 70, 81, 16)) 34 | self.label.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) 35 | self.label.setObjectName("label") 36 | self.label_2 = QtWidgets.QLabel(Form) 37 | self.label_2.setGeometry(QtCore.QRect(30, 150, 81, 16)) 38 | self.label_2.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) 39 | self.label_2.setObjectName("label_2") 40 | self.label_3 = QtWidgets.QLabel(Form) 41 | self.label_3.setGeometry(QtCore.QRect(30, 110, 81, 16)) 42 | self.label_3.setToolTipDuration(-1) 43 | self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight) 44 | self.label_3.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) 45 | self.label_3.setIndent(0) 46 | self.label_3.setObjectName("label_3") 47 | self.pushButton = QtWidgets.QPushButton(Form) 48 | self.pushButton.setGeometry(QtCore.QRect(230, 230, 113, 32)) 49 | self.pushButton.setObjectName("pushButton") 50 | self.pushButton_2 = QtWidgets.QPushButton(Form) 51 | self.pushButton_2.setGeometry(QtCore.QRect(60, 230, 113, 32)) 52 | self.pushButton_2.setObjectName("pushButton_2") 53 | self.label_4 = QtWidgets.QLabel(Form) 54 | self.label_4.setGeometry(QtCore.QRect(290, 70, 21, 16)) 55 | self.label_4.setAlignment(QtCore.Qt.AlignCenter) 56 | self.label_4.setObjectName("label_4") 57 | self.label_5 = QtWidgets.QLabel(Form) 58 | self.label_5.setGeometry(QtCore.QRect(30, 30, 81, 16)) 59 | self.label_5.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) 60 | self.label_5.setObjectName("label_5") 61 | self.password = QtWidgets.QLineEdit(Form) 62 | self.password.setGeometry(QtCore.QRect(120, 150, 251, 21)) 63 | self.password.setObjectName("password") 64 | self.password.setEchoMode(QtWidgets.QLineEdit.Password) 65 | self.port = QtWidgets.QSpinBox(Form) 66 | self.port.setGeometry(QtCore.QRect(310, 70, 71, 21)) 67 | self.port.setMaximum(65536) 68 | self.port.setValue(8086) 69 | self.port.setObjectName("port") 70 | self.label_6 = QtWidgets.QLabel(Form) 71 | self.label_6.setGeometry(QtCore.QRect(30, 190, 81, 16)) 72 | self.label_6.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) 73 | self.label_6.setObjectName("label_6") 74 | self.checkBox = QtWidgets.QCheckBox(Form) 75 | self.checkBox.setGeometry(QtCore.QRect(120, 190, 87, 20)) 76 | self.checkBox.setObjectName("checkBox") 77 | 78 | self.retranslateUi(Form) 79 | QtCore.QMetaObject.connectSlotsByName(Form) 80 | Form.setTabOrder(self.name, self.address) 81 | Form.setTabOrder(self.address, self.port) 82 | Form.setTabOrder(self.port, self.user) 83 | Form.setTabOrder(self.user, self.password) 84 | Form.setTabOrder(self.password, self.checkBox) 85 | Form.setTabOrder(self.checkBox, self.pushButton_2) 86 | Form.setTabOrder(self.pushButton_2, self.pushButton) 87 | 88 | def retranslateUi(self, Form): 89 | _translate = QtCore.QCoreApplication.translate 90 | Form.setWindowTitle(_translate("Form", "新建连接")) 91 | self.label.setText(_translate("Form", "服务器地址:")) 92 | self.label_2.setText(_translate("Form", "密码:")) 93 | self.label_3.setText(_translate("Form", "账号:")) 94 | self.pushButton.setText(_translate("Form", "保存")) 95 | self.pushButton_2.setText(_translate("Form", "测试")) 96 | self.label_4.setText(_translate("Form", ":")) 97 | self.label_5.setText(_translate("Form", "名称:")) 98 | self.label_6.setText(_translate("Form", "安全:")) 99 | self.checkBox.setText(_translate("Form", " 使用SSL")) 100 | -------------------------------------------------------------------------------- /ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '123.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.0 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | import sys 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | from PyQt5.QtGui import QIcon 13 | from PyQt5.QtWidgets import * 14 | from MyTextEdit import MyTextEdit 15 | 16 | 17 | class MyTableWidget(QtWidgets.QTableWidget): 18 | def __init__(self, centralwidget): 19 | super().__init__(centralwidget) 20 | 21 | self.nCurScroller = 0 # 翻页时的当时滑动条位置 22 | self.pageValue = 10 # 一页显示条数 23 | self.create_form() 24 | 25 | def create_form(self): 26 | pass 27 | # self.horizontalHeader().setVisible(False) # 隐藏水平表头 28 | # self.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked) # 双击编辑 29 | # self.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) 30 | # self.verticalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) 31 | 32 | def pre_page(self): 33 | 34 | max_value = self.verticalScrollBar().maximum() # 当前SCROLLER最大显示值 35 | self.nCurScroller = self.verticalScrollBar().value() # 获得当前scroller值 36 | 37 | if self.nCurScroller > 0: 38 | self.verticalScrollBar().setSliderPosition(self.nCurScroller - self.pageValue) 39 | else: 40 | self.verticalScrollBar().setSliderPosition(max_value) 41 | 42 | def next_page(self): 43 | 44 | # verticalScrollBar().setSliderPosition() 设置当前滑动条的位置 45 | # verticalScrollBar().maximum() 滑动条能移动的最大位置 46 | # verticalScrollBar().value() 获得当前滑动条的位置 47 | max_value = self.verticalScrollBar().maximum() # 当前SCROLLER最大显示值 48 | self.nCurScroller = self.verticalScrollBar().value() # 获得当前scroller值 49 | 50 | if self.nCurScroller < max_value: 51 | self.verticalScrollBar().setSliderPosition(self.pageValue + self.nCurScroller) 52 | else: 53 | self.verticalScrollBar().setSliderPosition(0) 54 | 55 | 56 | class MyQMainWindow(QMainWindow): 57 | def closeEvent(self, event): 58 | reply = QMessageBox.question(self, 'Message', "确定要退出?", 59 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 60 | if reply == QMessageBox.Yes: 61 | event.accept() 62 | sys.exit(0) 63 | else: 64 | event.ignore() 65 | 66 | 67 | class MainWindow(QMainWindow): 68 | def __init__(self): 69 | super().__init__() 70 | self.initUI() 71 | 72 | def resizeEvent(self, e): 73 | self.treeView.setGeometry(QtCore.QRect(20, 20, 281, self.height() - 100)) 74 | self.tabWidget.setGeometry(QtCore.QRect(310, 20, self.width() - 315, self.height() - 50)) 75 | 76 | try: 77 | tables = self.tabWidget.findChildren(QTableWidget, "tableWidget") 78 | for table in tables: 79 | table.setGeometry( 80 | QtCore.QRect(0, 211, self.width() - 315, self.height() - 335)) 81 | 82 | edits = self.tabWidget.findChildren(MyTextEdit, "textEdit") 83 | for edit in edits: 84 | edit.setGeometry(QtCore.QRect(0, 0, self.width() - 312, 181)) 85 | 86 | qcbs = self.tabWidget.findChildren(QComboBox, "QComboBox") 87 | for qcb in qcbs: 88 | qcb.setGeometry(QtCore.QRect(self.width() - 430, 184, 120, 25)) 89 | except: 90 | pass 91 | 92 | def initUI(self): 93 | self.setObjectName("MainWindow") 94 | self.resize(800, 600) 95 | self.setMinimumSize(QtCore.QSize(800, 600)) 96 | # MainWindow.setMaximumSize(QtCore.QSize(1416, 900)) 97 | self.centralwidget = QtWidgets.QWidget(self) 98 | self.centralwidget.setObjectName("centralwidget") 99 | self.treeView = QtWidgets.QTreeWidget(self.centralwidget) 100 | self.treeView.setGeometry(QtCore.QRect(20, 20, 281, 800)) 101 | self.treeView.setObjectName("treeView") 102 | self.treeView.setColumnCount(2) # 设置列数 103 | self.treeView.setHeaderLabels(["服务器", "value"]) # 设置标题头 104 | self.treeView.setHeaderHidden(True) # 隐藏标题 105 | self.treeView.setColumnHidden(1, True) # 隐藏vlue列 106 | self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) 107 | self.tabWidget.setEnabled(True) 108 | # self.tabWidget.setGeometry(QtCore.QRect(310, 20, 1101, 100)) 109 | font = QtGui.QFont() 110 | font.setKerning(False) 111 | self.tabWidget.setFont(font) 112 | self.tabWidget.setToolTip("") 113 | self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North) 114 | self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded) 115 | self.tabWidget.setElideMode(QtCore.Qt.ElideLeft) 116 | self.tabWidget.setUsesScrollButtons(False) 117 | self.tabWidget.setDocumentMode(True) 118 | self.tabWidget.setTabsClosable(True) 119 | self.tabWidget.setMovable(False) 120 | self.tabWidget.setTabBarAutoHide(False) 121 | self.tabWidget.setObjectName("tabWidget") 122 | self.toolBar = QtWidgets.QToolBar(self) 123 | self.toolBar.setMovable(False) 124 | self.setContextMenuPolicy(QtCore.Qt.NoContextMenu) 125 | self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) 126 | self.toolBar.setObjectName("toolBar") 127 | self.execAction = QAction(QIcon("images/exec.ico"), "运行", self.toolBar) 128 | self.history_button = QAction(QIcon("images/history.ico"), "历史命令", self.toolBar) 129 | self.toolBar.addAction(self.execAction) 130 | self.toolBar.addAction(self.history_button) 131 | self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) 132 | self.setCentralWidget(self.centralwidget) 133 | self.statusBar = QtWidgets.QStatusBar(self) 134 | self.statusBar.setObjectName("statusBar") 135 | self.setStatusBar(self.statusBar) 136 | self.menuBar = QtWidgets.QMenuBar(self) 137 | self.menuBar.setGeometry(QtCore.QRect(0, 0, 1416, 22)) 138 | self.menuBar.setObjectName("menuBar") 139 | self.menu = QtWidgets.QMenu(self.menuBar) 140 | self.menu.setObjectName("menu") 141 | self.setMenuBar(self.menuBar) 142 | self.actionNew = QtWidgets.QAction(self) 143 | self.actionNew.setObjectName("actionNew") 144 | self.menu.addAction(self.actionNew) 145 | self.menu.addSeparator() # 分隔符 146 | self.actionImport = QtWidgets.QAction(self) 147 | self.actionImport.setObjectName("actionImport") 148 | self.menu.addAction(self.actionImport) 149 | self.actionExport = QtWidgets.QAction(self) 150 | self.actionExport.setObjectName("actionExport") 151 | self.menu.addAction(self.actionExport) 152 | self.menuBar.addAction(self.menu.menuAction()) 153 | self.retranslateUi() 154 | QtCore.QMetaObject.connectSlotsByName(self) 155 | 156 | def retranslateUi(self): 157 | _translate = QtCore.QCoreApplication.translate 158 | self.setWindowTitle(_translate("InfluxDB", "InfluxDB")) 159 | # self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1")) 160 | self.menu.setTitle(_translate("server setting", "服务器设置")) 161 | self.actionNew.setText(_translate("new connect", "新建连接")) 162 | self.actionImport.setText(_translate("import setting", "导入配置")) 163 | self.actionExport.setText(_translate("export setting", "导出配置")) 164 | self.toolBar.setWindowTitle(_translate("tool bar", "工具栏")) 165 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 _*- 2 | 3 | import json 4 | import os 5 | import re 6 | import sqlite3 7 | import sys 8 | import time 9 | import functools 10 | import pandas 11 | 12 | from PyQt5 import QtCore 13 | from PyQt5.QtCore import Qt, pyqtSignal, QObject, QPoint 14 | from PyQt5.QtGui import * 15 | from PyQt5.QtWidgets import * 16 | from influxdb import InfluxDBClient 17 | 18 | from create_database import Ui_create_database 19 | from create_ui import Create_Ui_Form 20 | from history_ui import Ui_history_ui 21 | from import_ui import Import_UI 22 | from new_connect_ui import Ui_Form 23 | from ui import MainWindow 24 | from constant import sql_constant 25 | from MyTextEdit import MyTextEdit 26 | from utils import Runthread 27 | 28 | 29 | class MyQMainWindow(QMainWindow): 30 | def closeEvent(self, event): 31 | reply = QMessageBox.question(self, 'Message', "确定要退出?", 32 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 33 | if reply == QMessageBox.Yes: 34 | event.accept() 35 | sys.exit(0) 36 | else: 37 | event.ignore() 38 | 39 | 40 | class InfluxRegister: 41 | """1、每隔连接的时候到此处注册,返回数据库的专属连接用连接名识别""" 42 | clients = {} # 类属性 43 | 44 | def __init__(self): 45 | self.conn = sqlite3.connect('db/influx.db') 46 | 47 | def create_client(self, name, database=None): 48 | if name not in self.clients or bool(database): 49 | c = self.conn.cursor() 50 | cursor = c.execute( 51 | "SELECT name, address, port, user, password, ssl_switch, ID from ServerList where name='{}'".format( 52 | name)) 53 | data = cursor.fetchone() 54 | ssl_switch = True if data[5] == 2 else False 55 | client = InfluxDBClient(host=data[1], port=data[2], username=data[3], password=data[4], database=database, 56 | verify_ssl=ssl_switch, timeout=10) 57 | self.clients[name] = {"client": client, "data": data} 58 | 59 | 60 | class InfluxManage(QObject): 61 | signal = pyqtSignal() 62 | 63 | def __init__(self): 64 | super().__init__() 65 | self.app = QApplication(sys.argv) 66 | # self.MainWindow = MyQMainWindow() 67 | self.MainWindow = MainWindow() 68 | self.MainWindow.show() 69 | # self.MainWindow.setupUi(self.MainWindow) 70 | 71 | self.MainWindow.actionNew.triggered.connect(self.new_connect) 72 | self.MainWindow.actionImport.triggered.connect(self.import_connect) 73 | self.MainWindow.actionExport.triggered.connect(self.export_connect) 74 | 75 | self.new_ui = Ui_Form() 76 | self.new_QWidget = QWidget() 77 | self.new_QWidget.setWindowModality(Qt.ApplicationModal) 78 | self.new_ui.setupUi(self.new_QWidget) 79 | 80 | self.histroy_ui = Ui_history_ui() 81 | self.history_QWidget = QWidget() 82 | self.history_QWidget.setWindowModality(Qt.ApplicationModal) 83 | self.histroy_ui.setupUi(self.history_QWidget) 84 | self.histroy_ui.clear.clicked.connect(self.histroy_clear) 85 | 86 | self.import_ui_QWidget = QWidget() 87 | self.import_ui_QWidget.setWindowModality(Qt.ApplicationModal) 88 | self.import_ui = Import_UI(self.import_ui_QWidget) 89 | 90 | self.create_ui = Create_Ui_Form() 91 | self.create_QWidget = QWidget() 92 | self.create_QWidget.setWindowModality(Qt.ApplicationModal) 93 | self.create_ui.setupUi(self.create_QWidget) 94 | 95 | self.create_database_ui = Ui_create_database() 96 | self.create_database_QWidget = QWidget() 97 | self.create_database_QWidget.setWindowModality(Qt.ApplicationModal) 98 | self.create_database_ui.setupUi(self.create_database_QWidget) 99 | 100 | self.path = os.path.dirname(sys.argv[0]) 101 | self.conn = sqlite3.connect('db/influx.db') 102 | self.get_server_list() 103 | self.signal.connect(self.exec_handler) 104 | self.MainWindow.execAction.triggered.connect(lambda: self.signal.emit()) 105 | self.MainWindow.history_button.triggered.connect(self.show_history) 106 | self.MainWindow.tabWidget.tabCloseRequested.connect(self.tab_close) 107 | 108 | self.MainWindow.treeView.setContextMenuPolicy(Qt.CustomContextMenu) # 打开右键菜单的策略 109 | self.MainWindow.treeView.customContextMenuRequested.connect(self.right_click_menu) # 绑定事件 110 | 111 | self.influxDbClient = InfluxRegister() 112 | self.edit_context_menu = QMenu() # 创建对象 113 | self.edit_context_flag = False 114 | self.qt_info = functools.partial(QMessageBox.information, self.MainWindow, '提示信息') 115 | self.qt_cri = functools.partial(QMessageBox.critical, self.MainWindow, '提示信息') 116 | 117 | def histroy_clear(self): 118 | try: 119 | c = self.conn.cursor() 120 | c.execute("delete from history") 121 | c.execute("update sqlite_sequence SET seq = 0 where name ='history'") 122 | self.conn.commit() 123 | c.close() 124 | self.show_history() 125 | except Exception as e: 126 | with open("error.log", "w") as f: 127 | f.write(str(e)) 128 | print(e) 129 | 130 | def save_history(self, text): 131 | save_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 132 | c = self.conn.cursor() 133 | cursor = c.execute("select * from history") 134 | if text != "": 135 | if len(cursor.fetchall()) >= 100: 136 | c.execute("delete from history where time IN (SELECT time from history limit 1)") # 删除最老的一条 137 | text = text.replace("'", "''") 138 | sql = """INSERT INTO history (time , sql) VALUES ('{}','{}')""".format(save_time, text) # 插入新的 139 | c.execute(sql) 140 | self.conn.commit() 141 | c.close() 142 | 143 | def show_history(self): 144 | self.history_QWidget.show() 145 | c = self.conn.cursor() 146 | cursor = c.execute("SELECT * from history order by time DESC ") 147 | data = cursor.fetchall() 148 | self.histroy_ui.tableWidget.setRowCount(len(data)) # 设置行 149 | self.histroy_ui.tableWidget.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) # 添加滚动条 150 | for index, i in enumerate(data): 151 | for _index, j in enumerate(i): 152 | newItem = QTableWidgetItem(str(j)) 153 | newItem.setTextAlignment(Qt.AlignCenter) 154 | self.histroy_ui.tableWidget.setItem(index, _index, newItem) 155 | 156 | def new_connect(self): 157 | """新建窗口,添加服务器""" 158 | self.new_QWidget.show() 159 | self.new_ui.pushButton.disconnect() # 重新绑定时需要先释放绑定 160 | self.new_ui.pushButton_2.disconnect() # 重新绑定时需要先释放绑定 161 | self.new_ui.pushButton.clicked.connect(self.save_connect) 162 | self.new_ui.pushButton_2.clicked.connect(self.test_connect) 163 | 164 | def import_handler(self): 165 | conn = sqlite3.connect('db/influx.db') 166 | src = self.import_ui.browse_edit.text() 167 | try: 168 | df = pandas.read_excel(src) 169 | print(df) 170 | data = df.to_dict(orient='split') 171 | c = conn.cursor() 172 | for server_data in data["data"]: 173 | sql = """INSERT INTO ServerList (name, address, port, user, password, ssl_switch) VALUES ('{}','{}',{} 174 | ,'{}','{}',{})""".format(*server_data) 175 | c.execute(sql) 176 | conn.commit() 177 | self.qt_info("保存成功") 178 | except sqlite3.IntegrityError as e: 179 | self.qt_cri("name or ip existing") 180 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 181 | except FileNotFoundError: 182 | self.qt_cri("No such file or directory") 183 | except Exception as e: 184 | self.qt_cri(str(e)) 185 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 186 | finally: 187 | conn.close() 188 | self.MainWindow.treeView.clear() 189 | self.get_server_list() 190 | 191 | def import_connect(self): 192 | self.import_ui_QWidget.show() 193 | self.import_ui.import_button.disconnect() 194 | self.import_ui.import_button.clicked.connect(self.import_handler) 195 | 196 | def export_connect(self): 197 | c = self.conn.cursor() 198 | sql = "SELECT name, address, port, user, password, ssl_switch from ServerList" 199 | cursor = c.execute(sql) 200 | df = pandas.DataFrame(cursor, columns=["name", "ip", "port", "account", "password", "ssl(2=open,0=close)"]) 201 | filename = QFileDialog.getSaveFileName(self.MainWindow, 'save file', './template.xls') 202 | if not all(filename): 203 | return 204 | try: 205 | df.to_excel(filename[0], index=False) 206 | self.qt_info("导出成功!已导出到当前目录下的template.xls文件中") 207 | except Exception as e: 208 | self.qt_cri(str(e)) 209 | 210 | def test_connect(self): 211 | address = self.new_ui.address.text() 212 | port = self.new_ui.port.text() 213 | user = self.new_ui.user.text() 214 | password = self.new_ui.password.text() 215 | ssl = self.new_ui.checkBox.checkState() 216 | ssl_switch = True if ssl == 2 else False 217 | client = InfluxDBClient(host=address, port=port, username=user, password=password, 218 | verify_ssl=ssl_switch) 219 | try: 220 | client.get_list_database() 221 | self.qt_info("连接成功") 222 | except Exception as e: 223 | self.qt_cri(str(e)) 224 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 225 | 226 | def save_connect(self, types): 227 | """ 如果类型为True则编辑服务器""" 228 | name = self.new_ui.name.text() 229 | address = self.new_ui.address.text() 230 | port = self.new_ui.port.text() 231 | user = self.new_ui.user.text() 232 | password = self.new_ui.password.text() 233 | ssl = self.new_ui.checkBox.checkState() 234 | c = self.conn.cursor() 235 | if not user: 236 | user = "null" 237 | if not password: 238 | password = "null" 239 | 240 | if not all([name, address, port]): # 当这三个中不管哪一个为空都不能继续往下执行 241 | self.qt_cri("名称、地址、端口不可以为空") 242 | return 243 | sql = """INSERT INTO ServerList (name, address, user, port, password, ssl_switch) VALUES ('{}','{}','{}',{},'{}',{})""".format( 244 | name, address, user, port, password, ssl) 245 | if types == 1: 246 | old_name = self.MainWindow.treeView.currentItem().text(0) # 取当前节点的value值 247 | self.influxDbClient.create_client(old_name) 248 | clients = self.influxDbClient.clients.get(old_name) 249 | data = clients.get("data") 250 | ID = data[-1] 251 | sql = """UPDATE ServerList SET name="{}", address="{}", user="{}", port="{}", password="{}", ssl_switch="{}" where ID={}""".format( 252 | name, address, user, port, password, ssl, ID) 253 | try: 254 | c.execute(sql) 255 | self.conn.commit() 256 | if types: # 点击编辑时,则删除当前服务器节点,并重新添加节点后高亮 257 | self.action_handler_1(2) 258 | rootIndex = self.MainWindow.treeView.indexOfTopLevelItem(self.MainWindow.treeView.currentItem()) 259 | self.MainWindow.treeView.takeTopLevelItem(rootIndex) # 删除当前服务器节点 260 | self.get_server_list(True, ID, rootIndex) 261 | else: # 否则在保存以后刷新界面,先获取插入的最后一条数据的ID 262 | sql = """select ID from ServerList where rowid = last_insert_rowid() ;""" 263 | result = c.execute(sql) 264 | ID = result.fetchone()[0] 265 | rootIndex = self.MainWindow.treeView.topLevelItemCount() 266 | self.get_server_list(True, ID, rootIndex) 267 | self.qt_info("保存成功") 268 | self.new_QWidget.close() 269 | 270 | except sqlite3.IntegrityError as e: 271 | self.qt_cri("名称不能重复") 272 | except ValueError as e: 273 | self.qt_cri(str(e)) 274 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 275 | 276 | def get_server_list(self, types=False, ID=None, rootIndex=None): 277 | """ ssl_switch : 开启ssl2 未开启ssl, 0""" 278 | c = self.conn.cursor() 279 | sql = "SELECT name, address, port, user, password, ssl_switch from ServerList" 280 | if types: 281 | sql = "SELECT name, address, port, user, password, ssl_switch from ServerList WHERE ID={}".format(ID) 282 | cursor = c.execute(sql) 283 | for x in cursor: 284 | if types: 285 | root = QTreeWidgetItem() 286 | else: 287 | root = QTreeWidgetItem(self.MainWindow.treeView) 288 | root.setText(0, x[0]) 289 | root.setText(1, json.dumps(x)) 290 | 291 | icon = QIcon() 292 | icon.addPixmap(QPixmap("images/server_open.ico"), QIcon.Normal, QIcon.On) 293 | icon.addPixmap(QPixmap("images/server_close.ico"), QIcon.Normal, QIcon.Off) 294 | root.setIcon(0, icon) 295 | if types: 296 | self.MainWindow.treeView.insertTopLevelItem(rootIndex, root) 297 | self.MainWindow.treeView.setCurrentItem(root) 298 | 299 | self.MainWindow.treeView.doubleClicked.disconnect() 300 | self.MainWindow.treeView.doubleClicked.connect(self.double_handler) 301 | 302 | def tab_close(self, index): 303 | self.MainWindow.tabWidget.removeTab(index) 304 | 305 | def create_table(self, text, name, database, tables_name=None): 306 | tab = QWidget() 307 | tab.setObjectName("tab") 308 | MyTextEdit.constant = sql_constant 309 | if tables_name: 310 | MyTextEdit.constant.extend(tables_name) 311 | self.MainWindow.textEdit = MyTextEdit(tab) 312 | self.MainWindow.textEdit.setGeometry(QtCore.QRect(0, 0, self.MainWindow.width() - 312, 181)) 313 | self.MainWindow.textEdit.setObjectName("textEdit") 314 | self.MainWindow.textEdit.setPlainText(text) 315 | 316 | self.MainWindow.tableWidget = QTableWidget(tab) 317 | self.MainWindow.tableWidget.setGeometry( 318 | QtCore.QRect(0, 211, self.MainWindow.width() - 315, self.MainWindow.height() - 335)) 319 | self.MainWindow.tableWidget.setObjectName("tableWidget") 320 | 321 | self.MainWindow.QComboBox = QComboBox(tab) 322 | self.MainWindow.QComboBox.setGeometry(QtCore.QRect(self.MainWindow.width() - 430, 184, 120, 25)) 323 | self.MainWindow.QComboBox.setObjectName("QComboBox") 324 | 325 | self.MainWindow.tabWidget.addTab(tab, "{}.{}".format(name, database)) 326 | current_index = self.MainWindow.tabWidget.count() # 获取tab数量 327 | self.MainWindow.tabWidget.setCurrentIndex(current_index - 1) # 激活最后一个创建的tab 328 | 329 | def group_select(self, series): 330 | current_text = self.MainWindow.QComboBox.currentText() 331 | currentTableWidget = self.MainWindow.tabWidget.currentWidget().findChild(QTableWidget, "tableWidget") 332 | for x in series: 333 | tags = x.get('tags') 334 | columns = x.get('columns') 335 | values = x.get('values') 336 | for z in tags: 337 | if tags[z] == current_text: 338 | currentTableWidget.setColumnCount(len(columns)) # 设置列 339 | currentTableWidget.setRowCount(len(values)) # 设置行 340 | currentTableWidget.setHorizontalHeaderLabels(columns) # 设置标题 341 | # currentTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) # 关闭双击编辑 342 | currentTableWidget.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) # 添加滚动条 343 | for index, i in enumerate(values): 344 | for _index, j in enumerate(i): 345 | newItem = QTableWidgetItem(str(j)) 346 | newItem.setTextAlignment(Qt.AlignCenter) 347 | currentTableWidget.setItem(index, _index, newItem) 348 | return 349 | 350 | def show_table(self, tables): 351 | """传入influx数据,table显示数据""" 352 | currentTableWidget = self.MainWindow.tabWidget.currentWidget().findChild(QTableWidget, "tableWidget") 353 | flag = True 354 | try: 355 | series = tables.get("series") 356 | # print(series) 357 | if not series: 358 | currentTableWidget.setColumnCount(1) # 设置列 359 | currentTableWidget.setRowCount(1) # 设置行 360 | currentTableWidget.setHorizontalHeaderLabels(["返回结果"]) # 设置标题 361 | currentTableWidget.setItem(0, 0, QTableWidgetItem("")) 362 | return 363 | for x in series: 364 | table_name = x.get('name') 365 | columns = x.get('columns') 366 | values = x.get('values') 367 | tags = x.get('tags') 368 | if tags: 369 | if flag: 370 | self.MainWindow.QComboBox.currentIndexChanged.connect(lambda: self.group_select(series)) 371 | flag = False 372 | for n in tags: 373 | self.MainWindow.QComboBox.addItem(tags[n]) 374 | currentTableWidget.setColumnCount(len(columns)) # 设置列 375 | currentTableWidget.setRowCount(len(values)) # 设置行 376 | currentTableWidget.setHorizontalHeaderLabels(columns) # 设置标题 377 | # currentTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) # 关闭双击编辑 378 | currentTableWidget.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) # 添加滚动条 379 | for index, i in enumerate(values): 380 | for _index, data_time in enumerate(i): 381 | if _index == 0: 382 | obj = re.match( 383 | "^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]T([0-1]?[0-9]|2[0-3]):([0-5]" 384 | "[0-9]):([0-5][0-9]).+$", 385 | data_time) 386 | if obj: 387 | data_time = str(data_time).replace("T", " ") 388 | data_time = data_time.split(".")[0] 389 | newItem = QTableWidgetItem(str(data_time)) 390 | newItem.setTextAlignment(Qt.AlignCenter) 391 | currentTableWidget.setItem(index, _index, newItem) 392 | except Exception as e: 393 | self.qt_cri(str(e)) 394 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 395 | 396 | def double_handler(self): 397 | """双击服务器节点事件""" 398 | # treeView层级处理 399 | index_row = -1 400 | item = self.MainWindow.treeView.currentItem() 401 | parent = item.parent() 402 | if parent is None: 403 | index_top = self.MainWindow.treeView.indexOfTopLevelItem(item) 404 | else: 405 | index_top = self.MainWindow.treeView.indexOfTopLevelItem(parent) 406 | index_row = parent.indexOfChild(item) 407 | level = (index_top, index_row) 408 | # print(level) 409 | 410 | # 每个层级的逻辑处理 411 | if level == (-1, index_row): # 当双击表的时候操作的事件 412 | client, server_name, database_name, table_name = self.table_conn_client() 413 | text = """SELECT * FROM "{}" WHERE time > now() - 5m""".format(table_name) 414 | childCount = self.MainWindow.treeView.currentItem().parent().childCount() 415 | tables_name = set() 416 | for x in range(childCount): 417 | name = self.MainWindow.treeView.currentItem().parent().child(x).text(0) 418 | tables_name.add(name) 419 | sql = 'SHOW TAG KEYS FROM "{}"'.format(table_name) 420 | keys_data = client.query(sql) 421 | sql = 'SHOW FIELD KEYS FROM "{}"'.format(table_name) 422 | field_data = client.query(sql) 423 | key_data = [x for x in keys_data] 424 | field_data = [x for x in field_data] 425 | new_data = key_data[0] 426 | new_data.extend(field_data[0]) 427 | 428 | f = map(lambda x: {x.get('tagKey'), x.get('fieldKey')}, new_data) # 同时取tagkey和fieldKey 429 | var = functools.reduce(lambda x, y: x | y, f) # 循环获取列表中的集合,并且对之前的集合与后面的集合进行合并。reduce的功能 430 | tables_name.update(var) # 更新到主集合中 431 | 432 | self.create_table(text, server_name, database_name, tables_name) 433 | 434 | elif level == (index_top, -1): # 当双击服务器的时候显示数据库 435 | childCount = self.MainWindow.treeView.currentItem().childCount() 436 | if childCount == 0: 437 | icon = QIcon() 438 | icon.addPixmap(QPixmap("images/database_open.ico"), QIcon.Normal, QIcon.On) # 设置打开时的图片样式 439 | icon.addPixmap(QPixmap("images/database_close.ico"), QIcon.Normal, 440 | QIcon.Off) # 设置关闭的时候图片样式 441 | try: 442 | # 通过value中保存的服务器信息连接服务器并获得client 443 | name = self.MainWindow.treeView.currentItem().text(0) # 取当前节点的value值 444 | self.influxDbClient.create_client(name) 445 | clients = self.influxDbClient.clients.get(name) 446 | client = clients.get("client") 447 | database_list = client.get_list_database() 448 | for x in database_list: 449 | database_name = x.get("name") 450 | child = QTreeWidgetItem() 451 | child.setText(0, database_name) # 插入根节点 452 | child.setIcon(0, icon) 453 | self.MainWindow.treeView.currentItem().addChild(child) 454 | except Exception as e: 455 | self.qt_cri(str(e)) 456 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 457 | 458 | elif level == (index_top, index_row): # 当双击数据库的时候显示表 459 | childCount = self.MainWindow.treeView.currentItem().childCount() 460 | if childCount == 0: 461 | name = self.MainWindow.treeView.currentItem().parent().text(0) # 取服务器名 462 | database = self.MainWindow.treeView.currentItem().text(0) # 取数据库名 463 | self.influxDbClient.create_client(name, database=database) 464 | clients = self.influxDbClient.clients.get(name) 465 | client = clients.get("client") 466 | tables = client.query('show measurements;') 467 | Children = [] 468 | for x in tables: 469 | for i in x: 470 | child = QTreeWidgetItem() 471 | child.setText(0, i.get("name")) 472 | child.setIcon(0, QIcon('images/form.ico')) 473 | Children.append(child) 474 | self.MainWindow.treeView.currentItem().addChildren(Children) 475 | 476 | def status_bar_signal(self, text): 477 | self.MainWindow.statusBar.showMessage(text) 478 | 479 | def run(self): 480 | try: 481 | text_obj = self.MainWindow.tabWidget.currentWidget().findChild(QPlainTextEdit, "textEdit") 482 | old_time = time.time() * 1000 483 | tab_name = self.MainWindow.tabWidget.tabText(self.MainWindow.tabWidget.currentIndex()) 484 | data = tab_name.rsplit(sep='.', maxsplit=1) 485 | name = data[0] 486 | database = data[1] 487 | self.influxDbClient.create_client(name, database=database) 488 | clients = self.influxDbClient.clients.get(name) 489 | client = clients.get("client") 490 | tables = client.query(text_obj.toPlainText()) 491 | now_time = time.time() * 1000 492 | self.show_table(tables.raw) # 将查询结果传到show_table,并显示数据到前端 493 | self.MainWindow.statusBar.showMessage("执行完毕,耗时:{}毫秒".format(int(now_time - old_time))) 494 | self.MainWindow.statusBar.setStyleSheet("color:green") 495 | self.save_history(text=text_obj.toPlainText()) 496 | except AttributeError: 497 | self.MainWindow.statusBar.setStyleSheet("color:red") 498 | self.MainWindow.statusBar.showMessage("没有选择数据库,请先选择数据库") 499 | except Exception as e: 500 | self.qt_cri(str(e)) 501 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 502 | 503 | def exec_handler(self): 504 | self.MainWindow.statusBar.showMessage("执行中...") 505 | self.MainWindow.statusBar.setStyleSheet("color:red") 506 | self.run_thread = Runthread() # 步骤2. 主线程连接子线,同时传递一个值给子线程 507 | self.run_thread.signal.connect(self.run) # 自定义信号连接 508 | self.run_thread.start() # 步骤3 子线程开始执行run函数 509 | 510 | def table_conn_client(self): 511 | server_name = self.MainWindow.treeView.currentItem().parent().parent().text(0) 512 | database_name = self.MainWindow.treeView.currentItem().parent().text(0) 513 | table_name = self.MainWindow.treeView.currentItem().text(0) 514 | 515 | self.influxDbClient.create_client(server_name, database=database_name) 516 | clients = self.influxDbClient.clients.get(server_name) 517 | client = clients.get("client") 518 | return client, server_name, database_name, table_name 519 | 520 | def select(self, i): 521 | client, name, database, db = self.table_conn_client() 522 | sql = 'SHOW TAG VALUES FROM "{}" WITH KEY = "{}"'.format(db, i) 523 | new_tables = client.query(sql) 524 | self.show_table(new_tables.raw) 525 | 526 | def create_form(self, client): 527 | """创建表的点击事件""" 528 | json_body = self.create_ui.textEdit.toPlainText() 529 | try: 530 | json_body = json.loads(json_body) 531 | client.write_points(json_body) 532 | self.qt_info("创建成功") 533 | self.action_handler_2(0) 534 | except Exception as e: 535 | self.qt_cri(str(e)) 536 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 537 | 538 | def create_database(self, client): 539 | database = self.create_database_ui.lineEdit.text() 540 | try: 541 | client.create_database(database) # 创建数据库 542 | QMessageBox.information(self.MainWindow, 'Message', "创建成功") 543 | self.action_handler_1(2) 544 | self.double_handler() 545 | 546 | except Exception as e: 547 | self.qt_cri(str(e)) 548 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 549 | 550 | def action_handler_1(self, types): 551 | """服务器层操作: 552 | 1.创建数据库 553 | 2.关闭连接 554 | 3.编辑服务器 555 | 4.删除服务器 556 | """ 557 | name = self.MainWindow.treeView.currentItem().text(0) 558 | self.influxDbClient.create_client(name) 559 | clients = self.influxDbClient.clients.get(name) 560 | client = clients.get("client") 561 | if types == 1: 562 | self.create_database_QWidget.show() 563 | self.create_database_ui.pushButton.disconnect() 564 | self.create_database_ui.pushButton.clicked.connect(lambda: self.create_database(client)) 565 | 566 | if types == 2: 567 | client.close() 568 | del self.influxDbClient.clients[name] 569 | child = self.MainWindow.treeView.currentItem().childCount() 570 | while child: 571 | now_child = self.MainWindow.treeView.currentItem().child(0) 572 | self.MainWindow.treeView.currentItem().removeChild(now_child) 573 | child -= 1 574 | self.MainWindow.treeView.currentItem().setIcon(0, QIcon('images/server_close.ico')) 575 | 576 | if types == 3: 577 | data = clients.get("data") 578 | self.new_QWidget.show() 579 | self.new_QWidget.setWindowTitle("编辑连接") 580 | self.new_ui.name.setText(data[0]) 581 | self.new_ui.address.setText(data[1]) 582 | self.new_ui.port.setValue(data[2]) 583 | self.new_ui.user.setText(data[3]) 584 | self.new_ui.password.setText(data[4]) 585 | ssl_switch = True if data[5] == 2 else False 586 | self.new_ui.checkBox.setChecked(ssl_switch) 587 | self.new_ui.pushButton.disconnect() # 重新绑定时需要先释放绑定 588 | self.new_ui.pushButton_2.disconnect() # 重新绑定时需要先释放绑定 589 | self.new_ui.pushButton.clicked.connect(lambda: self.save_connect(True)) 590 | self.new_ui.pushButton_2.clicked.connect(self.test_connect) 591 | 592 | if types == 4: 593 | # 删除数据库中的信息 594 | data = clients.get("data") 595 | sql = 'delete from ServerList where id={}'.format(data[-1]) 596 | reply = QMessageBox.question(self.MainWindow, 'Message', "确定要删除?", 597 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 598 | if reply == QMessageBox.Yes: 599 | try: 600 | c = self.conn.cursor() 601 | c.execute(sql) 602 | self.conn.commit() 603 | self.qt_info("删除成功") 604 | self.action_handler_1(2) 605 | rootIndex = self.MainWindow.treeView.indexOfTopLevelItem(self.MainWindow.treeView.currentItem()) 606 | self.MainWindow.treeView.takeTopLevelItem(rootIndex) # 删除当前服务器节点 607 | except Exception as e: 608 | self.qt_cri(str(e)) 609 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 610 | 611 | def action_handler_2(self, types): 612 | """ 613 | 数据库层操作 614 | types : 0 刷新窗口, 1 新建表, 2 删除数据库 615 | """ 616 | name = self.MainWindow.treeView.currentItem().parent().text(0) 617 | database = self.MainWindow.treeView.currentItem().text(0) 618 | self.influxDbClient.create_client(name, database=database) 619 | clients = self.influxDbClient.clients.get(name) 620 | client = clients.get("client") 621 | if types == 0: 622 | child = self.MainWindow.treeView.currentItem().childCount() 623 | while child: 624 | now_child = self.MainWindow.treeView.currentItem().child(0) 625 | self.MainWindow.treeView.currentItem().removeChild(now_child) 626 | child -= 1 627 | tables = client.query('show measurements;') 628 | for x in tables: 629 | for i in x: 630 | child = QTreeWidgetItem() 631 | child.setText(0, i.get("name")) 632 | child.setIcon(0, QIcon('images/form.ico')) 633 | self.MainWindow.treeView.currentItem().addChild(child) 634 | if types == 1: 635 | self.create_QWidget.show() 636 | self.create_ui.pushButton.disconnect() 637 | self.create_ui.pushButton.clicked.connect(lambda: self.create_form(client)) 638 | if types == 2: 639 | reply = QMessageBox.question(self.MainWindow, 'Message', "确定要删除数据库: {}".format(database), 640 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 641 | if reply == QMessageBox.Yes: 642 | try: 643 | client.drop_database(database) # 删除数据库 644 | self.qt_info("删除成功") 645 | current_item = self.MainWindow.treeView.currentItem() # 获取当前节点 646 | self.MainWindow.treeView.currentItem().parent().removeChild(current_item) # 删除父目录下当前节点 647 | except Exception as e: 648 | self.qt_cri(str(e)) 649 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 650 | 651 | def action_handler_3(self, types): 652 | """表单层操作""" 653 | client, name, database, db = self.table_conn_client() 654 | self.influxDbClient.create_client(name, database=database) 655 | clients = self.influxDbClient.clients.get(name) 656 | client = clients.get("client") 657 | 658 | if types == 0: 659 | sql = 'SHOW TAG KEYS FROM "{}"'.format(db) 660 | tables = client.query(sql) 661 | self.create_table(sql, name, database) 662 | self.show_table(tables.raw) 663 | elif types == 1: 664 | tables = client.query('SHOW TAG KEYS FROM "{}"'.format(db)) 665 | self.create_table("", name, database) 666 | switch = True 667 | for x in tables: 668 | for i in x: 669 | for z in i: 670 | if switch: 671 | sql = 'SHOW TAG VALUES FROM "{}" WITH KEY = "{}"'.format(db, i[z]) 672 | new_tables = client.query(sql) 673 | self.show_table(new_tables.raw) 674 | switch = False 675 | self.MainWindow.QComboBox.addItem(str(i[z])) 676 | self.MainWindow.QComboBox.currentIndexChanged[str].connect(self.select) 677 | elif types == 2: 678 | text = 'SHOW FIELD KEYS FROM "{}"'.format(db) 679 | tables = client.query(text) 680 | self.create_table(text, name, database) 681 | self.show_table(tables.raw) 682 | elif types == 3: 683 | client, name, database, db = self.table_conn_client() 684 | reply = QMessageBox.question(self.MainWindow, 'Message', "确定要删除?", 685 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 686 | if reply == QMessageBox.Yes: 687 | try: 688 | client.query('drop measurement {}'.format(db)) 689 | self.qt_cri("删除成功") 690 | current_item = self.MainWindow.treeView.currentItem() 691 | self.MainWindow.treeView.currentItem().parent().removeChild(current_item) 692 | except Exception as e: 693 | self.qt_cri(str(e)) 694 | print('错误所在的行号:', e.__traceback__.tb_lineno) 695 | else: 696 | raise ValueError 697 | 698 | def right_click_menu(self, pos): 699 | index_row = -1 700 | item = self.MainWindow.treeView.currentItem() 701 | if not item: 702 | contextMenu = QMenu() # 创建对象 703 | new_conn = contextMenu.addAction(u'新建连接') # 添加动作 704 | new_conn.triggered.connect(self.new_connect) 705 | contextMenu.exec_(self.MainWindow.treeView.mapToGlobal(pos)) # 随指针的位置显示菜单 706 | contextMenu.show() # 显示 707 | return 708 | try: 709 | parent = item.parent() 710 | except Exception as e: 711 | self.qt_cri(str(e)) 712 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 713 | return 714 | if parent is None: 715 | index_top = self.MainWindow.treeView.indexOfTopLevelItem(item) 716 | else: 717 | index_top = self.MainWindow.treeView.indexOfTopLevelItem(parent) 718 | index_row = parent.indexOfChild(item) 719 | level = (index_top, index_row) 720 | if level == (-1, index_row): # 当右击表的时候 721 | try: 722 | contextMenu = QMenu() # 创建对象 723 | actionA = contextMenu.addAction(u'显示标签键') # 添加动作 724 | actionB = contextMenu.addAction(u'显示标签值') 725 | actionC = contextMenu.addAction(u'显示字段键') 726 | actionD = contextMenu.addAction(u'删除表') 727 | actionA.triggered.connect(lambda: self.action_handler_3(0)) 728 | actionB.triggered.connect(lambda: self.action_handler_3(1)) 729 | actionC.triggered.connect(lambda: self.action_handler_3(2)) 730 | actionD.triggered.connect(lambda: self.action_handler_3(3)) 731 | contextMenu.exec_(self.MainWindow.treeView.mapToGlobal(pos)) # 随指针的位置显示菜单 732 | contextMenu.show() # 显示 733 | except Exception as e: 734 | print(e, '错误所在的行号:', e.__traceback__.tb_lineno) 735 | elif level == (index_top, -1): # 当右击服务器的时候 736 | contextMenu = QMenu() # 创建对象 737 | actionA = contextMenu.addAction(u'创建数据库') # 添加动作 738 | actionB = contextMenu.addAction(u'关闭连接') 739 | 740 | actionC = contextMenu.addAction(u'编辑服务器') # 添加动作 741 | actionD = contextMenu.addAction(u'删除服务器') # 添加动作 742 | 743 | actionA.triggered.connect(lambda: self.action_handler_1(1)) 744 | actionB.triggered.connect(lambda: self.action_handler_1(2)) 745 | actionC.triggered.connect(lambda: self.action_handler_1(3)) 746 | actionD.triggered.connect(lambda: self.action_handler_1(4)) 747 | 748 | contextMenu.exec_(self.MainWindow.treeView.mapToGlobal(pos)) # 随指针的位置显示菜单 749 | contextMenu.show() # 显示 750 | elif level == (index_top, index_row): # 当右击数据库的时候 751 | contextMenu = QMenu() # 创建对象 752 | actionA = contextMenu.addAction(u'刷新') # 添加动作 753 | actionB = contextMenu.addAction(u'创建表') # 添加动作 754 | actionC = contextMenu.addAction(u'删除数据库') 755 | actionA.triggered.connect(lambda: self.action_handler_2(0)) 756 | actionB.triggered.connect(lambda: self.action_handler_2(1)) 757 | actionC.triggered.connect(lambda: self.action_handler_2(2)) 758 | contextMenu.exec_(self.MainWindow.treeView.mapToGlobal(pos)) # 随指针的位置显示菜单 759 | contextMenu.show() # 显示 760 | 761 | 762 | if __name__ == '__main__': 763 | ui = InfluxManage() 764 | ui.MainWindow.show() # 显示主框 765 | sys.exit(ui.app.exec_()) 766 | --------------------------------------------------------------------------------