试图开发一种自动化工具,该工具为现有的Peewee(my favorite ORM)模型创建一个PyQt5 QTableWidget和一个QFormLayout上的输入对话框。
好吧-首先,我写了peewee模拟:
import logging
import sys
from PyQt5.QtWidgets import \
QApplication, QWidget, QVBoxLayout, \
QTableWidget, QTableWidgetItem
from PyQt5.QtCore import Qt
class MockedField:
"""to simulate Peewee fields"""
def __init__(self, name):
self.name = name
class MockedModel:
"""to simulate peewee model"""
columns = [MockedField("name"),
MockedField("firstname"),
MockedField("nickname"),
MockedField("email")]
@classmethod
def get_columnlist(cls):
return cls.columns
之后,使用Qt5小部件的视图的基类。 他们 modelclass arg用于继承的peewee模型 由具有方法 get_columnlist()的类 BaseModel 组成。此方法返回所有peewee字段信息。
class _QtPeewee(QWidget):
def __init__(self, modelclass, **opts):
"""Options:
- parent (default=None)
- minheight (default=600)
- minwidth (default=1200)
"""
parent = opts.get("parent", None)
super().__init__(parent)
self._minheight = opts.get("minheight", 600)
self._minwidth = opts.get("minwidth", 1200)
self.options = opts
self.model = modelclass
self.fields = modelclass.get_columnlist()
self.fieldnames = [s.name.upper() for s in self.fields]
def _setup_layout(self):
logging.debug("<{}._setup_layout>".format(
self.__class__.__name__))
self.setMinimumHeight(self._minheight)
self.setMinimumWidth(self._minwidth)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
def _setup_widgets(self):
raise RuntimeError("to implement in subclass!")
def _connect_evt_with_handler(self):
raise RuntimeError("to implement in subclass!")
# UI
def setup(self):
logging.debug("<{}.setup()>".format(self.__class__.__name__))
self._setup_layout()
self._setup_widgets()
self._connect_evt_with_handler()
然后使用QTableWidget通过peewee代表数据库数据的Widget:
class QtPeeweeTable(_QtPeewee):
def __init__(self, modelclass, **opts):
"""further Opts:
- datasets_per_page (default=15): -1 => shows all
for the paginate(pageno, datasets_per_page)
method of peewee:
http://docs.peewee-orm.com/en/latest/peewee/api.html#Query.paginate
"""
super().__init__(modelclass, **opts)
self._datasets_per_page = opts.get("datasets_per_page", 30)
self.tbl = None
def _setup_table(self) -> QTableWidget:
logging.debug("<{}._setup_table()>".format(
self.__class__.__name__))
colnames = self.fieldnames
self.tbl = tbl = QTableWidget(
self._datasets_per_page, len(colnames))
tbl.setHorizontalHeaderLabels(colnames)
for r in range(self._datasets_per_page):
for c in range(len(colnames)):
tbl.setItem(r, c, QTableWidgetItem(""))
return tbl
def _setup_widgets(self):
logging.debug("<{}._setup_widgets()>".format(
self.__class__.__name__))
tbl = self._setup_table()
# table for ctx menu
tbl.verticalHeader().setContextMenuPolicy(
Qt.CustomContextMenu)
tbl.horizontalHeader().setContextMenuPolicy(
Qt.CustomContextMenu)
self.layout.addWidget(self.tbl)
def _connect_evt_with_handler(self):
logging.debug("<{}._connect_evt_with_handler()>".format(
self.__class__.__name__))
# TODO
然后我尝试对其进行简单测试-令我惊讶的是,没有出现Qt5窗口??
def _test_mockmodel():
view = QtPeeweeTable(modelclass=MockedModel)
view.setup()
view.show()
def main_not_running():
logging.basicConfig(level=logging.DEBUG)
app = QApplication(sys.argv)
_test_mockmodel()
sys.exit(app.exec_())
但是如果我在主要功能中实施测试,则窗口将启动:
def main_running():
logging.basicConfig(level=logging.DEBUG)
app = QApplication(sys.argv)
view = QtPeeweeTable(modelclass=MockedModel)
view.setup()
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main_not_running()
# main_running()
我认为Qt5 app 对象必须在启动Qt进程的函数中-但是我没有在Qt文档中了解到它。如果我在未运行的测试功能中插入 app 对象,则窗口将启动:
def _test_mockmodel(app):
view = QtPeeweeTable(modelclass=MockedModel)
view.setup()
view.show()
sys.exit(app.exec_())
def main_not_running():
logging.basicConfig(level=logging.DEBUG)
app = QApplication(sys.argv)
_test_mockmodel(app)