按照计划开始Python3学习最后2个主题:简单的数据库连接和多线程。目标也很简单,能够实现一些简单的任务,需要时能够完成自己的工作处理就行。至于流行的Python来做网络爬虫等估计可预见的时间内用不到,暂时不学。原来准备学习的通信,也不学了。后面会用数据库和excel配合,完成一个实操的小工具,然后Python的学习就暂时告一段落了。虽然懂得不多,但基本算入门了吧。
一、多线程
前面用Timer实验了一个例子程序。还算比较简单。直接贴代码上来。后面再逐步完善。
import time,datetime import threading,timer
def timer_start(): print("I'm timer1111,") t=threading.Timer(1,test_func,("Parameter1",)) #启动一个新线程 t.start() while True: time.sleep(5) strTime = datetime.datetime.now().strftime('%Y-%M-%D %H:%M:%S') print("thread main:",strTime) def test_func(msg1): print("I'm test_func,",msg1) while True: time.sleep(2) strTime = datetime.datetime.now().strftime('%Y-%M-%D %H:%M:%S') print("thread 2",strTime)
两个线程同时守候运行。
二、使用数据库表(参考下面的一个实例进行学习)
【原创】python3+PyQt5 使用数据库表视图_basisworker_新浪博客http://blog.sina.com.cn/s/blog_c22e36090102x3o2.html
(一) 数据库操作
2.1 建立数据库连接,打开文件:
from PyQt5.QtSql import (QSqlDatabase, QSqlQuery, QSqlRelation, QSqlRelationalDelegate, QSqlRelationalTableModel, QSqlTableModel)
app = QApplication(sys.argv) filename = os.path.join(os.path.dirname(__file__), "assets.db") create = not QFile.exists(filename) db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName(filename) if not db.open(): QMessageBox.warning(None, "Asset Manager", ("Database Error: {0}" .format(db.lastError().text()))) sys.exit(1)
2.2创建数据表:
def createFakeData(): import random print("Dropping tables...") query = QSqlQuery() query.exec_("DROP TABLE assets") query.exec_("DROP TABLE logs") query.exec_("DROP TABLE actions") query.exec_("DROP TABLE categories") QApplication.processEvents() print("Creating tables...") query.exec_("""CREATE TABLE actions ( id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, name VARCHAR(20) NOT NULL, description VARCHAR(40) NOT NULL)""") query.exec_("""CREATE TABLE categories ( id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, name VARCHAR(20) NOT NULL, description VARCHAR(40) NOT NULL)""") query.exec_("""CREATE TABLE assets ( id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, name VARCHAR(40) NOT NULL, categoryid INTEGER NOT NULL, room VARCHAR(4) NOT NULL, FOREIGN KEY (categoryid) REFERENCES categories)""") query.exec_("""CREATE TABLE logs ( id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, assetid INTEGER NOT NULL, date DATE NOT NULL, actionid INTEGER NOT NULL, FOREIGN KEY (assetid) REFERENCES assets, FOREIGN KEY (actionid) REFERENCES actions)""") QApplication.processEvents() #填充数据 print("Populating tables...") query.exec_("INSERT INTO actions (name, description) " "VALUES ('Acquired', 'When installed')") query.exec_("INSERT INTO actions (name, description) " "VALUES ('Broken', 'When failed and unusable')") query.exec_("INSERT INTO actions (name, description) " "VALUES ('Repaired', 'When back in service')") query.exec_("INSERT INTO actions (name, description) " "VALUES ('Routine maintenance', " "'When tested, refilled, etc.')") query.exec_("INSERT INTO categories (name, description) VALUES " "('Computer Equipment', " "'Monitors, System Units, Peripherals, etc.')") query.exec_("INSERT INTO categories (name, description) VALUES " "('Furniture', 'Chairs, Tables, Desks, etc.')") query.exec_("INSERT INTO categories (name, description) VALUES " "('Electrical Equipment', 'Non-computer electricals')") today = QDate.currentDate()
2.3填充数据的部分操作:使用Oracle格式的命名变量法
query.prepare("INSERT INTO assets (name, categoryid, room) " "VALUES (:name, :categoryid, :room)") logQuery = QSqlQuery() logQuery.prepare("INSERT INTO logs (assetid, date, actionid) " "VALUES (:assetid, :date, :actionid)") assetid = 1
(给name、category、room赋值)
query.bindValue(":name", name) query.bindValue(":categoryid", category) query.bindValue(":room", room) query.exec_()
logQuery.bindValue(":assetid", assetid) when = today.addDays(-random.randint(7, 1500)) # when=when.toString() logQuery.bindValue(":date", when.toString("yyyy-MM-dd")) logQuery.bindValue(":actionid", ACQUIRED) logQuery.exec_()
2.4遍历数据库:
print("Assets:") query.exec_("SELECT id, name, categoryid, room FROM assets " "ORDER by id") categoryQuery = QSqlQuery() while query.next(): id = query.value(0) name = str(query.value(1)) categoryid = query.value(2) room = str(query.value(3)) categoryQuery.exec_("SELECT name FROM categories " "WHERE id = {0}".format(categoryid)) category = "{0}".format(categoryid) if categoryQuery.next(): category = str(categoryQuery.value(0)) print("{0}: {1} [{2}] {3}".format(id, name, category, room)) QApplication.processEvents()
(二)MVC机制与数据库操作
model完成数据源的数据读写;视图view会向model请求打算显示的数据项;对于每个数据项,view都会把数据项个painter传递给委托(delegate),要求绘制该项。 相应地,如果用户开始编辑操作,view会要求delegate提供一个Editor,用户接收了编辑后,更新的数据就会回传给Model。模型中的每个数据项都可以通过唯一的QModelIndex加以识别。每个QModelIndex独有三个重要属性:行、列和父对象。列表只用到行;表格用到行、列;分层模型如树会用到全部三个属性。
一些PyQt的窗口部件,例如QListWidget、QTableWidget、QTreeWidget,都带有模型和委托的视图。可以通过简便项窗口部件方便地进行控制;QListView、QTableView、QTreeView,则需要与外部窗口部件一起工作,或者使用内置的QStringListModel、QDirModel、QSqlTableModel一起工作,这些统称为自定义模型。
2.5简便项窗口部件
QTableWiget的操作为例,很简单,就是表格操作,每个格子就是一个Item。
可参考:QTableWidget详解(样式、右键菜单、表头塌陷、多选等) - 莫水千流 - 博客园 https://www.cnblogs.com/zhoug2020/p/3789076.html
例子中,Qt UserRole就是组件角色,在复杂系统中使用的比较多,原因也很简单,系统复杂,为了组件区分方便。 在Qt中很多类是可以给他添加角色值的,比如说QComboBox中的setItemData()与QStandardItemModel中的setData()这两个函数,都是在Index位置上添加角色值。在图书的实例中和id就是hash值绑定,作为索引使用。
写入时设置:item.setData(Qt.UserRole, id(ship)) 使用时首先获取,然后使用:currentTableShip() { return self.ships.ship( item.data(Qt.UserRole))} ship = self.currentTableShip() 相应的python3+PyQt5验证代码都可以在下面博客找到: 【原创】python3+PyQt5 使用三种不同的简便项窗口部件显示数据_basisworker_新浪博客http://blog.sina.com.cn/s/blog_c22e36090102x3l0.html
需要说明,实际验证结果:QTimer.singleShot(0, self.initialLoad)实际上可以去掉,直接调用self.initialLoad()就可以,否则会刚运行起来就死掉。 另外,前面哪部分就是ships.pyw的内容,不需要再去找原书的源代码了,对应要简单修改一下代码。2018/06/23
2.6自定义模型的操作方法
针对已有的QAbstractModel,要实现的方法:
1)对实现只读模型很有必要的方法 data() rowCount() columnCount() headerData()
2) 实现可编辑模型很有必要的方法 flags() setData() insertRows() removeRows()
2.7自定义委托
用于只读型的委托,唯一必须重新实现的是paint()
对于可编辑型的,必须重新实现createEditor() setEditorData() commitAndCloseEditor()
self.tableView2 = QTableView()
tableLabel2.setBuddy(self.tableView2)
self.tableView2.setModel(self.model)
self.tableView2.setItemDelegate(ships.ShipDelegate(self)) #数据的展示和编辑由delegate来完成
实际工作时,只要用户确认了编辑结果,编辑器(delegate)的数据就必须回写到模型中,模型通知视图,该项内容已经改变,而显示该项的视图会要求对显示的数据进行更新。
GUI快速编程这本书中的例子比较简单,对照起来,自己刷新数据的简便项窗口部件方式觉得更好一些,有复杂逻辑的估计还是用自定义模型的MVC更好吧。如果你实现的是一个最基本的取表格数据,写到表格中显示的,那就基本只要关联设置一下,其它什么都不用做。
相应的python3+PyQt5验证代码都可以在下面博客找到:
【原创】python3+PyQt5 使用自定义委托控制数据项的展示和编辑 _basisworker_新浪博客http://blog.sina.com.cn/s/blog_c22e36090102x3m5.html
【原创】python3+PyQt5 使用自定义模型保存数据并通过不同视图形式展示数据_basisworker_新浪博客http://blog.sina.com.cn/s/blog_c22e36090102x3ly.html2.8数据库MVC设置的示例:
def __init__(self):
super(MainForm, self).__init__()#配置Model
self.assetModel = QSqlRelationalTableModel(self) # QSqlRelationalTableModel继承自QSqlTableModel,并且对其进行了扩展,提供了对外键的支持。
self.assetModel.setTable("assets")#关联数据表
self.assetModel.setRelation(CATEGORYID,Q SqlRelation("categories", "id", "name")) #设置外键
self.assetModel.setSort(ROOM, Qt.AscendingOrder)#排序
self.assetModel.setHeaderData(ID, Qt.Horizontal,"ID")
self.assetModel.setHeaderData(NAME, Qt.Horizontal,"Name")
self.assetModel.setHeaderData(CATEGORYID, Qt.Horizontal,"Category")
self.assetModel.setHeaderData(ROOM, Qt.Horizontal,"Room")
self.assetModel.select()
#配置View
self.assetView = QTableView()
self.assetView.setModel(self.assetModel)#Model
self.assetView.setItemDelegate(AssetDelegate(self))#Controller,delegate委托
self.assetView.setSelectionMode(QTableView.SingleSelection)#只能单选
self.assetView.setSelectionBehavior(QTableView.SelectRows)# 设置选择行为时每次选择一行
self.assetView.setColumnHidden(ID, True) # don't show
self.assetView.resizeColumnsToContents()
assetLabel = QLabel("A&ssets")
assetLabel.setBuddy(self.assetView)
#log的和Asset的类似
self.logModel = QSqlRelationalTableModel(self)
self.logModel.setTable("logs")
self.logModel.setRelation(ACTIONID, QSqlRelation("actions", "id", "name"))
self.logModel.setSort(DATE, Qt.AscendingOrder)
self.logModel.setHeaderData(DATE, Qt.Horizontal, "Date")
self.logModel.setHeaderData(ACTIONID, Qt.Horizontal,"Action")
self.logModel.select()
self.logView = QTableView()
self.logView.setModel(self.logModel)
self.logView.setItemDelegate(LogDelegate(self))
self.logView.setSelectionMode(QTableView.SingleSelection)
self.logView.setSelectionBehavior(QTableView.SelectRows)
self.logView.setColumnHidden(ID, True)
self.logView.setColumnHidden(ASSETID, True)
self.logView.resizeColumnsToContents()
self.logView.horizontalHeader().setStretchLastSection(True)
logLabel = QLabel("&Logs")
logLabel.setBuddy(self.logView)
。。。。。。
#binding events with slots
self.assetView.selectionModel().currentRowChanged.connect(self.assetChanged) #行改变链接到self.assetChanged
。。。。。。
def assetChanged(self, index):
if index.isValid():
record = self.assetModel.record(index.row())
#通过id将asset 和log关联起来
id = record.value("id")
self.logModel.setFilter("assetid = {0}".format(id))
else:
self.logModel.setFilter("assetid = -1")
#self.logModel.reset() # workaround for Qt <= 4.3.3/SQLite bug
#self.logModel.beginResetModel()
self.logModel.select()
self.logView.horizontalHeader().setVisible( self.logModel.rowCount() > 0)
if PYQT_VERSION_STR < "4.1.0":
self.logView.setColumnHidden(ID, True)
self.logView.setColumnHidden(ASSETID, True)
#self.logModel.endResetModel()