I am trying to update one cell from a mysql database in my table but i am unable to pass the global variable to the cell. Currently I am getting an integer from a mysql database, then i try to globally define, then i pass the varaible to mystruct (the structure of the table), and finally i apply the mystruct to the table, and gives me the error AttributeError: 'Window' object has no attribute 'struct1', i know why since the self.mystruct1 is first used in __init__. Is there an alternative. Please look at the code below to understand.
*Note position does not matter
import sys
from PyQt4.QtGui import QTableWidget
from PyQt4 import QtGui,QtCore
import MySQLdb as mdb
import time
class Window(QtGui.QDialog,object):
def get_data_status(self):
self.model.execute("""SELECT cpu FROM table
WHERE date = (SELECT MAX(date) FROM table)""")
rows_status = self.model.fetchone()
self.listc1 = ['%s' % rows_status]#['%s %s' % self.rows_status]
self.lista1 = 'Juliet','Julietleft','Pong','Hulk'
self.listb1 = 'None','None','None','None'
self.mystruct1 = {'A':self.lista1, 'B':self.listb1, 'C':self.listc1}
print self.mystruct1
return self.mystruct1
# this is only for the temp time test
def new_data_status(self):
self.update_ready_status.emit()
update_ready_status = QtCore.pyqtSignal()
def __init__(self,parent=None):
super(Window, self).__init__()
self.layout = QtGui.QVBoxLayout(self)
self.db = mdb.connect('server','user','user','db')
self.model = self.db.cursor()
self.table1 = MyTableStatus(Window.get_data_status(self),145,4)
self.table1.repaint()
self.table1.reset()
self.layout.addWidget(self.table1)
self.update_ready_status.connect(self.get_data_status)
self.timer_status = QtCore.QTimer()
self.timer_status.timeout.connect(self.new_data_status)
# check every half-second
self.timer_status.start(1000*2)
class MyTableStatus(QTableWidget):
def sizeHint(self):
width = 0
for i in range(self.columnCount()):
width += self.columnWidth(i)
width += self.verticalHeader().sizeHint().width()
width += self.verticalScrollBar().sizeHint().width()
width += self.frameWidth()*2
return QtCore.QSize(width,self.height())
def __init__(self, thestruct,*args):
QTableWidget.__init__(self, *args)
self.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
self.data = thestruct
self.setHorizontalHeaderLabels(['Server', 'Avg. Disk Queue','CPU Load',"Status"])
self.setmydata()
QTableWidget.setSortingEnabled(self,True)
def setmydata(self):
for n, key in enumerate(self.data):
for m, item in enumerate(self.data[key]):
newitem = QtGui.QTableWidgetItem(item)
self.setItem(m, n, newitem)
def main():
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create("plastique"))
main_window = Window()
main_window.repaint()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Manual trigger (code 2):
import sys
from PyQt4.QtGui import QTableWidget
from PyQt4 import QtGui,QtCore,Qt
import MySQLdb as mdb
import time
class Window(QtGui.QDialog):
def __init__(self,parent=None):
super(Window, self).__init__()
self.custom_choice = QtGui.QLineEdit()
self.layout = QtGui.QVBoxLayout(self)
self.db = mdb.connect('serv','user','pass','db')
self.model = self.db.cursor()
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.updateAllViews)
self.layout.addWidget(self.button)
self.initialData = self.get_data_status()
self.table1 = MyTableStatus(self.initialData, 145, 4)
self.layout.addWidget(self.table1)
# check every half-second
def handleHeaderMenu(self, pos):
self.menu = QtGui.QMenu()
self.custom_choice.setPlaceholderText("Server")
self.wac = QtGui.QWidgetAction(self.menu)
self.wac.setDefaultWidget(self.custom_choice)
self.menu.setStyleSheet("QMenu::item {background-color: #264F7D;color: white; font-weight:bold;}")
self.menu.addAction("Choose Server to Monitor:")
self.menu.addSeparator()
self.actionJuliet = self.menu.addAction('Juliet')
self.actionJulietleft = self.menu.addAction('JulietLeft')
self.actionPong = self.menu.addAction('Pong')
self.actionHulk = self.menu.addAction('Hulk')
self.actionCustom = self.menu.addAction(self.wac)
self.connect(self.custom_choice, QtCore.SIGNAL("returnPressed()"),self.updateAllViews)
action = self.menu.exec_(QtGui.QCursor.pos())
if action == self.actionPong:
print("pong")
def get_data_status(self):
self.tx = self.custom_choice.text()
self.model.execute("show TABLES;")
table_array = []
table_names = self.model.fetchall()
for lines in table_names:
lines = str(lines)
lines = lines.strip("()""''"",")
table_array.append(lines)
if any("%s" % self.tx in s for s in table_array):
table_name = self.tx
self.model.execute("""SELECT computer_name
FROM %s""" % (table_name))
new_user_name = self.model.fetchall()
self.model.execute("""SELECT idle_time
FROM %s""" % (table_name))
new_idle = self.model.fetchall()
self.model.execute("""SELECT files_opened
FROM %s""" % (table_name))
new_files = self.model.fetchall()
self.model.execute("""SELECT active_time
FROM %s""" % (table_name))
new_active = self.model.fetchall()
self.model.execute("""SELECT session_type
FROM %s""" % (table_name))
new_session = self.model.fetchall()
# self.model.execute("""SELECT number_of_machines
# FROM %s WHERE date = (SELECT MAX(date)
# FROM %s""" % (table_name,table_name))
#new_machines = self.model.fetchall()
self.model.execute("""SELECT cpu
FROM %s""" % (table_name))
new_cpu_load = self.model.fetchall()
self.model.execute("""SELECT avg_disk_queue
FROM %s""" % (table_name))
new_disk_queue_load = self.model.fetchall()
new_data_user = [item0[0] for item0 in new_user_name]
new_data_idle = [item1[0] for item1 in new_idle]
new_data_files = [item2[0] for item2 in new_files]
new_data_active = [item3[0] for item3 in new_active]
new_data_session = [item4[0] for item4 in new_session]
new_data_cpu_load = [item5[0] for item5 in new_cpu_load]
new_data_disk_queue_load = [item6[0] for item6 in new_disk_queue_load]
# self.lista.append(new_data_user)
# self.listb.append(new_data_idle)
# self.listc.append(new_data_files)
# self.listd.append(new_data_active)
# self.liste.append(new_data_session)
# self.listf.append(new_data_cpu_load)
# self.listg.append(new_data_disk_queue_load)
self.lista = new_data_user
self.listb = new_data_disk_queue_load
self.listc = new_data_cpu_load
self.listd = new_data_active
self.liste = new_data_files
self.listf = new_data_session
self.listg = new_data_idle
self.mystruct2 = {'A':self.lista, 'B':self.listb, 'C':self.listc,'E':self.liste,'D':self.listd,'F':self.listf,'G':self.listg}
else:
self.NotFound()
return self.mystruct2
def updateAllViews(self):
_ = self.get_data_status()
self.updateTable()
def updateTable(self):
self.table1.updateFromDict(self.mystruct1)
class MyTableStatus(QTableWidget):
def __init__(self, thestruct, *args):
QTableWidget.__init__(self, *args)
self.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
self.setHorizontalHeaderLabels(['Server', 'Avg. Disk Queue','CPU Load',"Status"])
self.setSortingEnabled(False)
self.data = {}
self.setmydata()
def updateFromDict(self, aDict):
self.data.clear()
self.data.update(aDict)
self.setmydata()
def setmydata(self):
for n, key in enumerate(self.data):
for m, item in enumerate(self.data[key]):
newitem = QtGui.QTableWidgetItem(item)
self.setItem(m, n, newitem)
def main():
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create("plastique"))
main_window = Window()
main_window.repaint()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The logic in this code is a bit messy, but I can see your problem with the data not updating.
There is an initial database pull, and then you pass self.mystruct1 to your custom Table to show the data for the first time. On subsequent triggers, you then overwrite that dictionary in the main window, thinking that somehow the Table will have the same reference. What is happening is the Window has the new dictionary, and the Table is sitting there with the original object.
Even if you were to just clear the dict, and update its values, as opposed to overwriting it each time, the Table would still not know the dictionary changed. You would need to connect a signal to the table itself to refresh its data, or, call something directly on the table. Simply naming the attribute model doesn't give it the same functionality as a QModel.
This is a little bit of a side-note, but python convention usually puts the __init__ at the top of the class so people reading it can immediately see what sets up your class before then seeing its methods.
To fix this, first clear out some cruft. You don't need a signal to a slot that emits another signal in this case. It isn't doing anything beyond making it more confusing. Just connect a signal directly to a slot on the Table that will perform an update. Also get rid of the repaint and reset calls in your main window on the table.
You can take two paths to providing the data to the Table. Either you can directly update the data model on the Table from your window and then tell it to refresh on that, or, you can pass the new data over the signal and let the Table handle it...
class Window(QtGui.QDialog):
def __init__(self,parent=None):
super(Window, self).__init__()
...
initialData = self.get_data_status()
self.table1 = MyTableStatus(initialData, 145, 4)
...
self.timer_status = QtCore.QTimer()
self.timer_status.timeout.connect(self.updateAllViews)
# check every half-second
self.timer_status.start(1000*2)
def get_data_status(self):
...
self.mystruct1 = {'A':self.lista1, 'B':self.listb1, 'C':self.listc1}
return self.mystruct1
def updateAllViews(self):
_ = self.get_data_status()
self.updateTable()
def updateTable(self):
self.table1.updateFromDict(self.mystruct1)
class MyTableStatus(QTableWidget):
def __init__(self, thestruct, *args):
QTableWidget.__init__(self, *args)
self.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
self.setHorizontalHeaderLabels(['Server', 'Avg. Disk Queue','CPU Load',"Status"])
self.setSortingEnabled(True)
self.data = {}
self.setmydata(thestruct)
def updateFromDict(self, aDict):
self.data.clear()
self.data.update(aDict)
self.setmydata()
def setmydata(self):
for n, key in enumerate(self.data):
for m, item in enumerate(self.data[key]):
newitem = QtGui.QTableWidgetItem(item)
self.setItem(m, n, newitem)
You can give your table initial data, but you also need to set it up to be updated by future database pulls. Here we simply connect the timer to a method that updates the local data, and then refreshes the various views you are using, including the table.
The Table now has a method that can take a dict, and update its own internal data structure.
A slight variation on this approach would be to emit the new data structure in a signal, and just fire that when your local data structure changes in the Window...
class Window(QtGui.QDialog):
update_ready = QtCore.pyqtSignal(dict)
def __init__(self,parent=None):
...
# call a generate update and emit wrapper
self.timer_status.timeout.connect(self.refreshData)
# connect each view with a slot that expects a dict
self.update_ready.connect(self.table1.updateFromDict)
...
def refreshData(self):
new_data = self.get_data_status()
self.update_ready.emit(new_data)
In this example, you just let the signal deliver the new data structure to the view on a slot that expects a dict (from the previous example).
There are a few things going on here:
You don't need the global declaration in your init method (textedit and rows are not global)
You're seeing the error you're seeing from the line: MyTableStatus(self.mystruct1, 145, 4) because you haven't defined the self.mystruct1 variable yet - you define it in the get_data_status method, which isn't called until the signal is emitted. You'll generally want to define your class members before doing anything else (just good practice)
I'm not really sure why you have the time.sleep(2) call in there, the processing of the signal should happen pretty quickly in Qt
You have to watch out when doing things like self.layout = QtGui.QVBoxLayout(self). The layout symbol is actually an inherited QWidget method (self.layout()). Setting this can potentially break Qt internals.
Generally, unless you are trying to connect other widgets to the update_ready_status signal, you don't really need it
Anyway, I'm not 100% sure what you are trying to do - but I hope that helps!
Related
This question already has answers here:
Python convert tuple to string
(5 answers)
Closed 3 years ago.
Apologies if my title is unclear; I'm trying to retrieve some data from an SQLLite database (which is working) and show it to the user on a window in PyQt5. So far, I've managed to retrieve the result and print in Python, but when trying to add/view the results in the window Python simply crashes.
I've inserted the code which isn't working below. (The indentation has gone slightly funny when copying it, but it is perfectly fine in my file).
class Profile(QWidget):
def __init__(self, Type):
super().__init__()
self.window = QWidget()
self.window.setGeometry(100,100, 350, 400)
self.window.setWindowTitle("Welcome")
self.window.show()
self.Choices()
def Choices(self):
self.layout = QGridLayout()
c.execute('''SELECT username, teamname FROM Users WHERE username = ?''',
(user,))
result = c.fetchone()
print(result)
print(user)
self.TeamInfo = QLabel(result)
self.layout.addWidget(self.TeamInfo)
self.window.setLayout(self.layout)
self.window.show()
user is a global variable in a previous window (the log in page) to avoid the user having to reenter their username. This section is not included, as that is not the problem. All the other buttons in the class are working - it is just this particular section. Any help as to how to solve this is greatly appreciated, I figure the problem is the line self.TeamInfo = QLabel(result) but I don't have the PyQT5 knowledge on how to solve this.
Edit: I have included a screenshot of the error message I'm getting.
If I understand right you are trying to add a text to QLabel, right?
According to the documentation adding text to it is done by: QLabel().setText(result).
Edit: Could you please try this and tell me what the compiler is complaining about?
label = QLabel()
label.setText(result)
self.layout.addWidget(label)
Try it:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sqlite3
class Profile(QWidget):
def __init__(self, Type=None):
super().__init__()
self.window = QWidget()
self.window.setGeometry(100,100, 350, 400)
self.window.setWindowTitle("Welcome")
self.Choices()
def Choices(self):
self.layout = QGridLayout()
# c.execute('''SELECT username, teamname FROM Users WHERE username = ?''',
# (user,))
# result = c.fetchone()
#
user = "Nick"
try:
self.conn = sqlite3.connect("database.db")
self.c = self.conn.cursor()
# result = self.c.execute("SELECT * from students WHERE name='{}'".format(user) )
result = self.c.execute("SELECT * from students WHERE name=?", (user, ))
row = result.fetchone() # <---
print("\nrow->", type(row), row)
serachresult = "Rollno : "+str(row[0])+'\n'+"Name : "+str(row[1])+'\n'+"Branch : "+str(row[2])+'\n'+"Sem : "+str(row[3])+'\n'+"Address : "+str(row[4])
QMessageBox.information(QMessageBox(), 'Successful', serachresult)
self.conn.commit()
self.c.close()
self.conn.close()
except Exception:
QMessageBox.warning(QMessageBox(), 'Error', 'Could not Find student from the database.')
print(result)
print(user)
self.TeamInfo = QLabel(", ".join([ str(i) for i in row ])) # <---
self.layout.addWidget(self.TeamInfo)
self.window.setLayout(self.layout)
self.window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Profile()
main.show()
sys.exit(app.exec_())
PyQt5 - QComboBox populated with QQueryModel including Id; Name; ..etc fields. Using Name as QComboBox.modelColumn. Hope to retrive id field from the clicked item.
I shifted the ModelColumn back and forth without doing any good. Also I accessed the QQueryModel.record to find that it is on the first record always, not on the current one.
import MySQL_Connector
import sys
from PyQt5.QtCore import QVariant, Qt
from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel,QSqlQuery , QSqlTableModel, QSqlError, QSqlQueryModel, QSqlQuery
from PyQt5.QtWidgets import QApplication, QMainWindow
from MainUI import Ui_MainWindow
class QConnectionError(Exception):
pass
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
#setup Qsql databaase objects
cnn = MySQL_Connector.MysqlConnection('config.ini')
con_string = cnn.read_db_config()[1]
try:
db = QSqlDatabase.addDatabase("QMYSQL")
db.setHostName(con_string['host'])
db.setUserName(con_string['user'])
db.setDatabaseName(con_string['database'])
db.setPassword(con_string['password'])
ok = db.open()
if not ok:
raise QConnectionError("Connection failed--- Is the server running?")
except QConnectionError as err:
print("You'll have to wait until a connection is established")
return
finally:
if db.isOpen():
db.close()
self.qdb = db
self.qdb.open()
# set combobox
self.comboQuery = combo_query(self.qdb)
self.comboModel = QSqlQueryModel()
self.comboModel.setQuery(self.comboQuery)
self.comboBox.setModel(self.comboModel)
self.comboBox.setModelColumn(1)
self.comboBox.activated[int].connect(self.do_action)
#populate textView
self.query = test(self.qdb)
self.model = QSqlQueryModel()
self.model.setQuery(self.query)
self.model.setHeaderData(0,Qt.Horizontal, "ID")
self.model.setHeaderData(1, Qt.Horizontal, "Nombre")
self.tableView.rowHeight(2)
self.tableView.fontMetrics()
self.tableView.setModel(self.model)
self.show()
if self.qdb.isOpen():
self.qdb.close()
def do_action(self, str): #Experimenting
print(str, type(str))
self.tableView.selectRow(5)
def main():
app = QApplication(sys.argv)
window = MainWindow()
app.exec()
Need advise on how to go forward. All the database I'm using is based on ID fields with I need to further query for whatever the reason. Maybe a tools change? A different approach.
QCombobox has two signals suitable for your needs:
activated() is sent when the user chooses an item, even if the choice is not changed, see QComboBox.activated()
currentIndexChanged() is sent if the current index is changed, either by users choice or programmatically, see QComboBox.currentIndexChanged()
Both signals pass the current index.
Using this index you get the needed data by QSqlQuery.data().
Here a simple example using sqlite3, i think you can adapt it to your database:
import sqlite3
from PyQt5 import QtWidgets, QtSql
class MyWidget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.database = QtSql.QSqlDatabase('QSQLITE')
self.database.setDatabaseName('tc.db')
self.database.open()
self.dataModel = QtSql.QSqlQueryModel()
self.dataModel.setQuery('select id, name from items', self.database)
self.comboBox = QtWidgets.QComboBox(self)
self.comboBox.setModel(self.dataModel)
self.comboBox.setModelColumn(1)
self.comboBox.currentIndexChanged.connect(self.do_action)
# self.comboBox.activated.connect(self.do_action)
def do_action(self, i):
id = self.dataModel.data(self.dataModel.index(i, 0)) # self.dataModel.index(row, column)
name = self.dataModel.data(self.dataModel.index(i, 1))
print(i, 'id: ', id, 'name: ', name)
qApp = QtWidgets.QApplication([])
widget = MyWidget()
widget.show()
qApp.exec_()
here the dump of the database tc.db:
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "items" (
"id" INTEGER PRIMARY KEY NOT NULL,
"name" TEXT NOT NULL
);
INSERT INTO items VALUES(0,'abcde');
INSERT INTO items VALUES(1,'fghijk');
INSERT INTO items VALUES(2,'lmnop');
INSERT INTO items VALUES(3,'qrstuv');
COMMIT;
I am new to python and pyqt/pyside ...
i make customwidget class consist of 2 label (title & desc) which is example instance to add to Listwidget later ...
here is the complete clean code (pyside maya)
import PySide.QtCore as qc
import PySide.QtGui as qg
class CustomQWidget (qg.QWidget):
def __init__ (self, parent = None):
super(CustomQWidget, self).__init__(parent)
self.textQVBoxLayout = qg.QVBoxLayout()
self.titleLabel = qg.QLabel()
self.description = qg.QLabel()
self.textQVBoxLayout.addWidget(self.titleLabel)
self.textQVBoxLayout.addWidget(self.description)
self.setLayout(self.textQVBoxLayout)
def setTitle (self, text):
self.titleLabel.setText(text)
def setDescription (self, text):
self.description.setText(text)
class example_ui(qg.QDialog):
def __init__(self):
qg.QDialog.__init__(self)
self.myQListWidget = qg.QListWidget(self)
self.myQListWidget.currentItemChanged.connect(self.getTitleValue)
self.myQListWidget.setGeometry(qc.QRect(0,0,200,300))
# make instance customwidget item (just one)------
instance_1 = CustomQWidget()
instance_1.setTitle('First title')
instance_1.setDescription('this is a sample desc')
myQListWidgetItem = qg.QListWidgetItem(self.myQListWidget)
myQListWidgetItem.setSizeHint(instance_1.sizeHint())
self.myQListWidget.addItem(myQListWidgetItem)
self.myQListWidget.setItemWidget(myQListWidgetItem, instance_1)
def getTitleValue(self,val):
# i make assume something like below but didnt work
# print (self.myQListWidget.currentItem.titleLabel.text()
return 0
dialog = example_ui()
dialog.show()
now at getTitleValue function how do i get Title and desc value when i change selection ?
You should remember that the list items and corresponding widgets are not the same. Luckily, QListWidget tracks them and gives you access to the displayed widget if you provide the list item:
class example_ui(qg.QDialog):
def getTitleValue(self,val):
# parameter val is actually the same as self.myQListWidget.currentItem
selected_widget = self.myQListWidget.itemWidget(val)
print selected_widget.titleLabel.text()
return 0
Side note: I had to add a main loop in order for the app to be executed at all:
import sys # to give Qt access to parameters
# ... class definitions etc. ...
app = qg.QApplication(sys.argv)
dialog = example_ui()
dialog.show()
exec_status = app.exec_() # main loop
I get this behavior only on linux. On windows this problem does not happen.
I have a table using the model/view framework. One of the columns is editable, and when I enter it to change the data the old data is still visible in the background while I edit the data in the foreground.
I'm not sure what is causing this, I've tried flipping various settings, but I've been unable to change the behavior.
Maybe a simpler question that will still help me: I'm correct to be looking in the view code for this issue correct? Would the model possibly have anything to do with this? Do I need to set any currently editing flags in the model?
Assuming that the view is where the issue is here is most of the view logic I'm using. There are some caveats to the following code: I'm injecting common code used between QTableViews and QTreeViews, so there are a few functions this class has that are no explicitly listed as methods:
from __future__ import absolute_import, division, print_function
from guitool.__PYQT__ import QtCore, QtGui
from guitool import api_item_view
from guitool.guitool_decorators import signal_, slot_
API_VIEW_BASE = QtGui.QTableView
class APITableView(API_VIEW_BASE):
rows_updated = signal_(str, int)
contextMenuClicked = signal_(QtCore.QModelIndex, QtCore.QPoint)
API_VIEW_BASE = API_VIEW_BASE
def __init__(view, parent=None):
API_VIEW_BASE.__init__(view, parent)
api_item_view.injectviewinstance(view)
view._init_table_behavior()
view._init_header_behavior()
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
view.customContextMenuRequested.connect(view.on_customMenuRequested)
#---------------
# Initialization
#---------------
def _init_table_behavior(view):
view.setCornerButtonEnabled(False)
view.setWordWrap(True)
view.setSortingEnabled(True)
view.setShowGrid(True)
# Selection behavior #view.setSelectionBehavior(QtGui.QAbstractItemView.SelectColumns)
view.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems)
view._defaultEditTriggers = QtGui.QAbstractItemView.AllEditTriggers
view.setEditTriggers(view._defaultEditTriggers)
view.setIconSize(QtCore.QSize(64, 64))
def _init_header_behavior(view):
""" Header behavior """
# Row Headers
verticalHeader = view.verticalHeader()
verticalHeader.setVisible(True)
#verticalHeader.setSortIndicatorShown(True)
verticalHeader.setHighlightSections(True)
verticalHeader.setResizeMode(QtGui.QHeaderView.Interactive)
verticalHeader.setMovable(True)
# Column headers
horizontalHeader = view.horizontalHeader()
horizontalHeader.setVisible(True)
horizontalHeader.setStretchLastSection(True)
horizontalHeader.setSortIndicatorShown(True)
horizontalHeader.setHighlightSections(True)
# Column Sizes
# DO NOT USE ResizeToContents. IT MAKES THINGS VERY SLOW
#horizontalHeader.setResizeMode(QtGui.QHeaderView.ResizeToContents)
#horizontalHeader.setResizeMode(QtGui.QHeaderView.Stretch)
horizontalHeader.setResizeMode(QtGui.QHeaderView.Interactive)
#horizontalHeader.setCascadingSectionResizes(True)
# Columns moveable
horizontalHeader.setMovable(True)
#---------------
# Qt Overrides
#---------------
def setModel(view, model):
""" QtOverride: Returns item delegate for this index """
api_item_view.setModel(view, model)
def keyPressEvent(view, event):
assert isinstance(event, QtGui.QKeyEvent)
API_VIEW_BASE.keyPressEvent(view, event)
if event.matches(QtGui.QKeySequence.Copy):
#print('Received Ctrl+C in View')
view.copy_selection_to_clipboard()
#print ('[view] keyPressEvent: %s' % event.key())
def mouseMoveEvent(view, event):
assert isinstance(event, QtGui.QMouseEvent)
API_VIEW_BASE.mouseMoveEvent(view, event)
def mousePressEvent(view, event):
assert isinstance(event, QtGui.QMouseEvent)
API_VIEW_BASE.mousePressEvent(view, event)
#print('no editing')
view.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
def mouseReleaseEvent(view, event):
assert isinstance(event, QtGui.QMouseEvent)
#print('editing ok')
view.setEditTriggers(view._defaultEditTriggers)
API_VIEW_BASE.mouseReleaseEvent(view, event)
def clearSelection(view, *args, **kwargs):
print('[table_view] clear selection')
API_VIEW_BASE.clearSelection(view, *args, **kwargs)
#---------------
# Slots
#---------------
#slot_(str, int)
def on_rows_updated(view, tblname, num):
# re-emit the model signal
view.rows_updated.emit(tblname, num)
#slot_(QtCore.QPoint)
def on_customMenuRequested(view, pos):
index = view.indexAt(pos)
view.contextMenuClicked.emit(index, pos)
# ----
# Injected funcs from api_item_view
#register_view_method
def infer_delegates(view, **headers):
""" Infers which columns should be given item delegates """
get_thumb_size = headers.get('get_thumb_size', None)
col_type_list = headers.get('col_type_list', [])
num_cols = view.model().columnCount()
num_duplicates = int(num_cols / len(col_type_list))
col_type_list = col_type_list * num_duplicates
for colx, coltype in enumerate(col_type_list):
if coltype in qtype.QT_PIXMAP_TYPES:
if VERBOSE:
print('[view] colx=%r is a PIXMAP' % colx)
thumb_delegate = APIThumbDelegate(view, get_thumb_size)
view.setItemDelegateForColumn(colx, thumb_delegate)
elif coltype in qtype.QT_BUTTON_TYPES:
if VERBOSE:
print('[view] colx=%r is a BUTTON' % colx)
button_delegate = APIButtonDelegate(view)
view.setItemDelegateForColumn(colx, button_delegate)
else:
if VERBOSE:
print('[view] colx=%r does not have a delgate' % colx)
#register_view_method
def set_column_persistant_editor(view, column):
""" Set each row in a column as persistant """
num_rows = view.model.rowCount()
print('view.set_persistant: %r rows' % num_rows)
for row in range(num_rows):
index = view.model.index(row, column)
view.view.openPersistentEditor(index)
The item-delegate editor needs to paint its own background:
editor.setAutoFillBackground(True)
Obviously, this has to be fixed within the custom delegate classes (APIThumbDelegate and/or APIButtonDelegate) which create the editor widgets (i.e. via their createEditor functions).
I have a simple PyQt4 program that takes in 3 Particulars (Name,Gender & Address) whenever i clicked on OK button and save it as a binary file (3 particulars are hard coded in program for testing purpose). Then later will load that information back and display it in QTableWidget.
This is the layout of my program:
It has 2 scripts: DContainer.py and Data_Main.py
Dcontainer.py
import bisect
from PyQt4 import QtCore
class Person(object):
def __init__(self, Name = None, Gender = None , Address = None ):
self.Name = Name
self.Gender = Gender
self.Address = Address
class PersonContainer(object):
def __init__(self):
self.__fname = QtCore.QString("mydatabase.mqb")
self.__persons = []
self.__personFromId = {}
def __iter__(self):
for pair in iter(self.__persons):
yield pair[1]
def __len__(self):
return len(self.__persons)
def Clear(self):
self.__persons = []
self.__personFromId ={}
def add(self,person):
if id(person)in self.__personFromId:
return False
key = person.Name
bisect.insort_left(self.__persons, [key,person])
self.__personFromId[id(person)] = person
return True
def save(self):
fh = QtCore.QFile(self.__fname)
if not fh.open(QtCore.QIODevice.WriteOnly):
raise IOError , unicode(fh.errorString())
stream = QtCore.QDataStream(fh)
for key, person in self.__persons:
stream << person.Name << person.Gender << person.Address
def load(self):
fh = QtCore.QFile(self.__fname)
if not fh.open(QtCore.QIODevice.ReadOnly):
raise IOError , unicode(fh.errorString())
stream = QtCore.QDataStream(fh)
while not stream.atEnd():
Name = QtCore.QString()
Gender = QtCore.QString()
Address = QtCore.QString()
stream >> Name >> Gender >> Address
self.add(Person(Name,Gender,Address))
Data_Main.py
import sys
from PyQt4 import QtCore,QtGui
import DContainer
class MainDialog(QtGui.QDialog):
def __init__(self, parent = None):
super(MainDialog,self).__init__(parent)
self.InitGui()
self.persons = DContainer.PersonContainer()
self.Update()
def InitGui(self):
buttonbox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
self.table = QtGui.QTableWidget()
layout = QtGui.QVBoxLayout()
layout.addWidget(self.table)
layout.addWidget(buttonbox)
self.setLayout(layout)
self.connect(buttonbox.button(buttonbox.Ok), QtCore.SIGNAL("clicked()"),self.OK)
def OK(self):
NewPerson = DContainer.Person(QtCore.QString('This is another test'),QtCore.QString('Male'),QtCore.QString('Strand Road'))
self.persons.add(NewPerson)
self.persons.save()
self.Update()
def Update(self):
self.table.clear()
self.persons.load()
self.table.setRowCount(len(self.persons))
self.table.setColumnCount(3)
for row,person in enumerate(self.persons):
item = QtGui.QTableWidgetItem(person.Name)
self.table.setItem(row,0,item)
def Main():
app = QtGui.QApplication(sys.argv)
dialog = MainDialog()
dialog.show()
app.exec_()
if __name__ == "__main__":
Main()
My Problem is whenever i clicked on OK button, it create multiple table entries
After second click
It should not create multiple table entries as i have used
if id(person)in self.__personFromId:
return False
in my Add method in Dcontainer.py.
Rightfully, it should only show one item in the table unless i give the new person object with different name.
What is causing the problem?
The PersonContainer.add method is called twice when you click the OK button:
Directly from the MainDialog.OK method
Indirectly from the MainDialog.Update method, with self.persons.load()
You can add an optional argument to the Update method to trigger the call to load:
def Update(self, load=False):
self.table.clear()
if load:
self.persons.load()
And call this method with load set to True in the __init__ method:
def __init__(self, parent = None):
super(MainDialog,self).__init__(parent)
self.InitGui()
self.persons = DContainer.PersonContainer()
self.Update(True)
By the way, the old style signal/slot is no longer supported with PyQt5. This is how to write in the new style:
buttonbox.accepted.connect(self.OK)
buttonbox.rejected.connect(self.reject)