QDialog 较为高级一点的demo
QDialog 较为高级一点的demo
原理参考前面 我转载的 QDialog 的简介,这个地方直接放代码,版权声明不在我,但是代码有开源证书,所以我就随便改了,把真正的功能实现都隐藏了,只是用 print
来代表这个功能的确被触发了。
源程序 在其中一个文件夹里 https://github.com/IDArlingTeam/IDArling
代码比较长,不过经过我剪辑之后,可以当单个脚本直接运行,能出来挺好看的界面 。
[email protected]:~/c_study/python3_study/idarling_study/ida_test$ cat setting.py
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#!/usr/bin/env python
# coding=utf-8
import datetime
from functools import partial
import logging
import sys
from PyQt5.QtCore import QRegExp, Qt # noqa: I202
from PyQt5.QtGui import QIcon, QRegExpValidator
from PyQt5.QtWidgets import (
QMainWindow,
QApplication,
QCheckBox,
QColorDialog,
QComboBox,
QDialog,
QFormLayout,
QGridLayout,
QGroupBox,
QHBoxLayout,
QHeaderView,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QSpinBox,
QTableWidget,
QTableWidgetItem,
QTabWidget,
QVBoxLayout,
QWidget,
)
global_config = {'level': 10, 'keep': {u'cnt': 4, u'intvl': 15, u'idle': 240}, 'user': {u'color': 0, u'notifications': True, u'name': u'unnamed'}, 'cursors': {u'navbar': True, u'funcs': True, u'disasm': True}, 'servers': [{u'host': u'101.5.253.48', u'no_ssl': False, u'port': 31013}]}
class SettingsDialog(QDialog):
"""
The dialog allowing an user to configure the plugin. It has multiple tabs
used to group the settings by category (general, network, etc.).
"""
def __init__(self):
super(SettingsDialog, self).__init__()
#self._plugin = plugin
# General setup of the dialog
#self._plugin.logger.debug("Showing settings dialog")
self.setWindowTitle("Settings")
#icon_path = self._plugin.plugin_resource("settings.png")
icon_path = "./images/settings.png"
self.setWindowIcon(QIcon(icon_path))
print("dialogs.py class SettingsDialog(QDialog) __init__---->self.windowFlags()---->"),#zj_debug
print(self.windowFlags())#zj_debug
print("dialogs.py class SettingsDialog(QDialog) __init__---->Qt.WindowCloseButtonHint---->"),#zj_debug
print(Qt.WindowCloseButtonHint)#zj_debug
self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
window_widget = QWidget(self)
window_layout = QVBoxLayout(window_widget)
tabs = QTabWidget(window_widget)
window_layout.addWidget(tabs)
# "General Settings" tab
tab = QWidget(tabs)
layout = QFormLayout(tab)
layout.setFormAlignment(Qt.AlignVCenter)
tabs.addTab(tab, "General Settings")
user_widget = QWidget(tab)
user_layout = QHBoxLayout(user_widget)
layout.addRow(user_widget)
# User color
self._color_button = QPushButton("")
self._color_button.setFixedSize(50, 30)
def color_button_activated(_):
self._set_color(qt_color=QColorDialog.getColor().rgb())
self._color = global_config["user"]["color"]
self._set_color(ida_color=self._color)
self._color_button.clicked.connect(color_button_activated)
user_layout.addWidget(self._color_button)
# User name
self._name_line_edit = QLineEdit()
name = global_config["user"]["name"]
self._name_line_edit.setText(name)
user_layout.addWidget(self._name_line_edit)
text = "Disable all user cursors"
self._disable_all_cursors_checkbox = QCheckBox(text)
layout.addRow(self._disable_all_cursors_checkbox)
navbar_checked = not global_config["cursors"]["navbar"]
funcs_checked = not global_config["cursors"]["funcs"]
disasm_checked = not global_config["cursors"]["disasm"]
all_checked = navbar_checked and funcs_checked and disasm_checked
self._disable_all_cursors_checkbox.setChecked(all_checked)
def state_changed(state):
enabled = state == Qt.Unchecked
self._disable_navbar_cursors_checkbox.setChecked(not enabled)
self._disable_navbar_cursors_checkbox.setEnabled(enabled)
self._disable_funcs_cursors_checkbox.setChecked(not enabled)
self._disable_funcs_cursors_checkbox.setEnabled(enabled)
self._disable_disasm_cursors_checkbox.setChecked(not enabled)
self._disable_disasm_cursors_checkbox.setEnabled(enabled)
self._disable_all_cursors_checkbox.stateChanged.connect(state_changed)
style_sheet = """QCheckBox{ margin-left: 20px; }"""
text = "Disable navigation bar user cursors"
self._disable_navbar_cursors_checkbox = QCheckBox(text)
layout.addRow(self._disable_navbar_cursors_checkbox)
self._disable_navbar_cursors_checkbox.setChecked(navbar_checked)
self._disable_navbar_cursors_checkbox.setEnabled(not all_checked)
self._disable_navbar_cursors_checkbox.setStyleSheet(style_sheet)
text = "Disable functions window user cursors"
self._disable_funcs_cursors_checkbox = QCheckBox(text)
layout.addRow(self._disable_funcs_cursors_checkbox)
self._disable_funcs_cursors_checkbox.setChecked(funcs_checked)
self._disable_funcs_cursors_checkbox.setEnabled(not all_checked)
self._disable_funcs_cursors_checkbox.setStyleSheet(style_sheet)
text = "Disable disassembly view user cursors"
self._disable_disasm_cursors_checkbox = QCheckBox(text)
layout.addRow(self._disable_disasm_cursors_checkbox)
self._disable_disasm_cursors_checkbox.setChecked(disasm_checked)
self._disable_disasm_cursors_checkbox.setEnabled(not all_checked)
self._disable_disasm_cursors_checkbox.setStyleSheet(style_sheet)
text = "Allow other users to send notifications"
self._notifications_checkbox = QCheckBox(text)
layout.addRow(self._notifications_checkbox)
checked = global_config["user"]["notifications"]
self._notifications_checkbox.setChecked(checked)
# Log level
debug_level_label = QLabel("Logging level: ")
self._debug_level_combo_box = QComboBox()
self._debug_level_combo_box.addItem("CRITICAL", logging.CRITICAL)
self._debug_level_combo_box.addItem("ERROR", logging.ERROR)
self._debug_level_combo_box.addItem("WARNING", logging.WARNING)
self._debug_level_combo_box.addItem("INFO", logging.INFO)
self._debug_level_combo_box.addItem("DEBUG", logging.DEBUG)
#self._debug_level_combo_box.addItem("TRACE", logging.TRACE)
level = global_config["level"]
index = self._debug_level_combo_box.findData(level)
self._debug_level_combo_box.setCurrentIndex(index)
layout.addRow(debug_level_label, self._debug_level_combo_box)
# "Network Settings" tab
tab = QWidget(tabs)
layout = QVBoxLayout(tab)
tab.setLayout(layout)
tabs.addTab(tab, "Network Settings")
top_widget = QWidget(tab)
layout.addWidget(top_widget)
top_layout = QHBoxLayout(top_widget)
self._servers = list(global_config["servers"])
self._servers_table = QTableWidget(len(self._servers), 2, self)
top_layout.addWidget(self._servers_table)
for i, server in enumerate(self._servers):
# Server host and port
item = QTableWidgetItem("%s:%d" % (server["host"], server["port"]))
item.setData(Qt.UserRole, server)
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
#if self._plugin.network.server == server:
if server:
item.setFlags((item.flags() & ~Qt.ItemIsSelectable))
self._servers_table.setItem(i, 0, item)
# Server has SSL enabled?
checkbox = QTableWidgetItem()
state = Qt.Unchecked if server["no_ssl"] else Qt.Checked
checkbox.setCheckState(state)
checkbox.setFlags((checkbox.flags() & ~Qt.ItemIsEditable))
checkbox.setFlags((checkbox.flags() & ~Qt.ItemIsUserCheckable))
# if self._plugin.network.server == server:
if server:
checkbox.setFlags((checkbox.flags() & ~Qt.ItemIsSelectable))
self._servers_table.setItem(i, 1, checkbox)
self._servers_table.setHorizontalHeaderLabels(("Servers", ""))
horizontal_header = self._servers_table.horizontalHeader()
horizontal_header.setSectionsClickable(False)
horizontal_header.setSectionResizeMode(0, QHeaderView.Stretch)
horizontal_header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
self._servers_table.verticalHeader().setVisible(False)
self._servers_table.setSelectionBehavior(QTableWidget.SelectRows)
self._servers_table.setSelectionMode(QTableWidget.SingleSelection)
self._servers_table.itemClicked.connect(self._server_clicked)
self._servers_table.itemDoubleClicked.connect(
self._server_double_clicked
)
self._servers_table.setMaximumHeight(100)
buttons_widget = QWidget(top_widget)
buttons_layout = QVBoxLayout(buttons_widget)
top_layout.addWidget(buttons_widget)
# Add server button
self._add_button = QPushButton("Add Server")
self._add_button.clicked.connect(self._add_button_clicked)
buttons_layout.addWidget(self._add_button)
# Edit server button
self._edit_button = QPushButton("Edit Server")
self._edit_button.setEnabled(False)
self._edit_button.clicked.connect(self._edit_button_clicked)
buttons_layout.addWidget(self._edit_button)
# Delete server button
self._delete_button = QPushButton("Delete Server")
self._delete_button.setEnabled(False)
self._delete_button.clicked.connect(self._delete_button_clicked)
buttons_layout.addWidget(self._delete_button)
bottom_widget = QWidget(tab)
bottom_layout = QFormLayout(bottom_widget)
layout.addWidget(bottom_widget)
# TCP Keep-Alive settings
keep_cnt_label = QLabel("Keep-Alive Count: ")
self._keep_cnt_spin_box = QSpinBox(bottom_widget)
self._keep_cnt_spin_box.setRange(0, 86400)
self._keep_cnt_spin_box.setValue(global_config["keep"]["cnt"])
self._keep_cnt_spin_box.setSuffix(" packets")
bottom_layout.addRow(keep_cnt_label, self._keep_cnt_spin_box)
keep_intvl_label = QLabel("Keep-Alive Interval: ")
self._keep_intvl_spin_box = QSpinBox(bottom_widget)
self._keep_intvl_spin_box.setRange(0, 86400)
self._keep_intvl_spin_box.setValue(
global_config["keep"]["intvl"]
)
self._keep_intvl_spin_box.setSuffix(" seconds")
bottom_layout.addRow(keep_intvl_label, self._keep_intvl_spin_box)
keep_idle_label = QLabel("Keep-Alive Idle: ")
self._keep_idle_spin_box = QSpinBox(bottom_widget)
self._keep_idle_spin_box.setRange(0, 86400)
self._keep_idle_spin_box.setValue(global_config["keep"]["idle"])
self._keep_idle_spin_box.setSuffix(" seconds")
bottom_layout.addRow(keep_idle_label, self._keep_idle_spin_box)
# Buttons commons to all tabs
actions_widget = QWidget(self)
actions_layout = QHBoxLayout(actions_widget)
# Cancel = do not save the changes and close the dialog
def cancel(_):
self.reject()
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(cancel)
actions_layout.addWidget(cancel_button)
# Reset = reset all settings from all tabs to default values
reset_button = QPushButton("Reset")
reset_button.clicked.connect(self._reset)
actions_layout.addWidget(reset_button)
# Save = save the changes and close the dialog
def save(_):
self._commit()
self.accept()
save_button = QPushButton("Save")
save_button.clicked.connect(save)
actions_layout.addWidget(save_button)
window_layout.addWidget(actions_widget)
# Do not allow the user to resize the dialog
self.setFixedSize(
window_widget.sizeHint().width(), window_widget.sizeHint().height()
)
def _set_color(self, ida_color=None, qt_color=None):
"""Sets the color of the user color button."""
# IDA represents colors as 0xBBGGRR
if ida_color is not None:
r = ida_color & 255
g = (ida_color >> 8) & 255
b = (ida_color >> 16) & 255
# Qt represents colors as 0xRRGGBB
if qt_color is not None:
r = (qt_color >> 16) & 255
g = (qt_color >> 8) & 255
b = qt_color & 255
ida_color = r | g << 8 | b << 16
qt_color = r << 16 | g << 8 | b
# Set the stylesheet of the button
css = "QPushButton {background-color: #%06x; color: #%06x;}"
self._color_button.setStyleSheet(css % (qt_color, qt_color))
self._color = ida_color
def _server_clicked(self, _):
self._edit_button.setEnabled(True)
self._delete_button.setEnabled(True)
def _server_double_clicked(self, _):
print("_server_double_clicked(self, _)")
"""
item = self._servers_table.selectedItems()[0]
server = item.data(Qt.UserRole)
# If not the current server, connect to it
if (
not self._plugin.network.connected
or self._plugin.network.server != server
):
self._plugin.network.stop_server()
self._plugin.network.connect(server)
self.accept()
"""
def _add_button_clicked(self, _):
print("_add_button_clicked(self, _)")
#dialog = ServerInfoDialog(self._plugin, "Add server")
#dialog.accepted.connect(partial(self._add_dialog_accepted, dialog))
#dialog.exec_()
def _edit_button_clicked(self, _):
print("_edit_button_clicked(self, _)")
"""
item = self._servers_table.selectedItems()[0]
server = item.data(Qt.UserRole)
dialog = ServerInfoDialog(self._plugin, "Edit server", server)
dialog.accepted.connect(partial(self._edit_dialog_accepted, dialog))
dialog.exec_()
"""
def _delete_button_clicked(self, _):
print("_delete_button_clicked(self, _)")
"""
item = self._servers_table.selectedItems()[0]
server = item.data(Qt.UserRole)
self._servers.remove(server)
self._plugin.save_config()
self._servers_table.removeRow(item.row())
self.update()
"""
def _add_dialog_accepted(self, dialog):
"""Called when the dialog to add a server is accepted."""
server = dialog.get_result()
self._servers.append(server)
row_count = self._servers_table.rowCount()
self._servers_table.insertRow(row_count)
new_server = QTableWidgetItem(
"%s:%d" % (server["host"], server["port"])
)
new_server.setData(Qt.UserRole, server)
new_server.setFlags(new_server.flags() & ~Qt.ItemIsEditable)
self._servers_table.setItem(row_count, 0, new_server)
new_checkbox = QTableWidgetItem()
state = Qt.Unchecked if server["no_ssl"] else Qt.Checked
new_checkbox.setCheckState(state)
new_checkbox.setFlags((new_checkbox.flags() & ~Qt.ItemIsEditable))
new_checkbox.setFlags(new_checkbox.flags() & ~Qt.ItemIsUserCheckable)
self._servers_table.setItem(row_count, 1, new_checkbox)
self.update()
def _edit_dialog_accepted(self, dialog):
"""Called when the dialog to edit a server is accepted."""
server = dialog.get_result()
item = self._servers_table.selectedItems()[0]
self._servers[item.row()] = server
item.setText("%s:%d" % (server["host"], server["port"]))
item.setData(Qt.UserRole, server)
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
checkbox = self._servers_table.item(item.row(), 1)
state = Qt.Unchecked if server["no_ssl"] else Qt.Checked
checkbox.setCheckState(state)
self.update()
def _reset(self, _):
"""Resets all the form elements to their default value."""
config = self._plugin.default_config()
self._name_line_edit.setText(config["user"]["name"])
self._set_color(ida_color=config["user"]["color"])
navbar_checked = not config["cursors"]["navbar"]
funcs_checked = not config["cursor"]["funcs"]
disasm_checked = not config["cursor"]["disasm"]
all_checked = navbar_checked and funcs_checked and disasm_checked
self._disable_all_cursors_checkbox.setChecked(all_checked)
self._disable_navbar_cursors_checkbox.setChecked(navbar_checked)
self._disable_navbar_cursors_checkbox.setEnabled(not all_checked)
self._disable_funcs_cursors_checkbox.setChecked(funcs_checked)
self._disable_funcs_cursors_checkbox.setEnabled(not all_checked)
self._disable_disasm_cursors_checkbox.setChecked(disasm_checked)
self._disable_disasm_cursors_checkbox.setEnabled(not all_checked)
checked = config["user"]["notifications"]
self._notifications_checkbox.setChecked(checked)
index = self._debug_level_combo_box.findData(config["level"])
self._debug_level_combo_box.setCurrentIndex(index)
del self._servers[:]
self._servers_table.clearContents()
self._keep_cnt_spin_box.setValue(config["keep"]["cnt"])
self._keep_intvl_spin_box.setValue(config["keep"]["intvl"])
self._keep_idle_spin_box.setValue(config["keep"]["idle"])
def _commit(self):
"""Commits all the changes made to the form elements."""
print("Commits all the changes made to the form elements.")
name = self._name_line_edit.text()
if global_config["user"]["name"] != name:
old_name = global_config["user"]["name"]
#self._plugin.network.send_packet(UpdateUserName(old_name, name))
global_config["user"]["name"] = name
if global_config["user"]["color"] != self._color:
name = global_config["user"]["name"]
old_color = global_config["user"]["color"]
#packet = UpdateUserColor(name, old_color, self._color)
#self._plugin.network.send_packet(packet)
global_config["user"]["color"] = self._color
#self._plugin.interface.widget.refresh()
all_ = self._disable_all_cursors_checkbox.isChecked()
checked = self._disable_navbar_cursors_checkbox.isChecked()
global_config["cursors"]["navbar"] = not all_ and not checked
checked = self._disable_funcs_cursors_checkbox.isChecked()
global_config["cursors"]["funcs"] = not all_ and not checked
checked = self._disable_disasm_cursors_checkbox.isChecked()
global_config["cursors"]["disasm"] = not all_ and not checked
checked = self._notifications_checkbox.isChecked()
global_config["user"]["notifications"] = checked
index = self._debug_level_combo_box.currentIndex()
level = self._debug_level_combo_box.itemData(index)
self._plugin.logger.setLevel(level)
global_config["level"] = level
global_config["servers"] = self._servers
cnt = self._keep_cnt_spin_box.value()
global_config["keep"]["cnt"] = cnt
intvl = self._keep_intvl_spin_box.value()
global_config["keep"]["intvl"] = intvl
idle = self._keep_idle_spin_box.value()
global_config["keep"]["idle"] = idle
#if self._plugin.network.client:
# self._plugin.network.client.set_keep_alive(cnt, intvl, idle)
#self._plugin.save_config()
class DialogDemo(QMainWindow):
def __init__(self,parent=None):
super(DialogDemo, self).__init__(parent)
#设置主界面的标题及初始大小
self.setWindowTitle('Dialog例子')
self.resize(350,300)
#创建按钮,注意()内的self必不可少,用于加载自身的一些属性设置
self.btn=QPushButton(self)
#设置按钮的属性:文本,移动位置,链接槽函数
self.btn.setText('弹出对话框')
self.btn.move(50,50)
self.btn.clicked.connect(self.showdialog)
def showdialog(self):
#创建QDialog对象
dialog=SettingsDialog()
#创建按钮到新创建的dialog对象中
#btn=QPushButton('ok',dialog)
#移动按钮,设置dialog的标题
#btn.move(50,50)
#dialog.setWindowTitle("Dialog")
#设置窗口的属性为ApplicationModal模态,用户只有关闭弹窗后,才能关闭主界面
dialog.setWindowModality(Qt.ApplicationModal)
dialog.exec_()
if __name__ == '__main__':
app=QApplication(sys.argv)
demo=DialogDemo()
demo.show()
sys.exit(app.exec_())
代码写完了,我们 run 一下程序
[email protected]:~/c_study/python3_study/idarling_study/ida_test$ python3 -i setting.py
dialogs.py class SettingsDialog(QDialog) __init__---->self.windowFlags()---->
<PyQt5.QtCore.Qt.WindowFlags object at 0x7f9233f4f7b8>
dialogs.py class SettingsDialog(QDialog) __init__---->Qt.WindowCloseButtonHint---->
134217728
_add_button_clicked(self, _) #代表我点击了这个操作
_add_button_clicked(self, _)
_edit_button_clicked(self, _)
_delete_button_clicked(self, _)
这个例子总算单独复盘下了这个界面,也算获益不少,仅供自己学习 。