QComboBox using QQueryModel, getting the id field (not display) from the clicked - python

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;

Related

PyQT5 query.next() doesn't seem to be working

I'm trying to build a little GUI application in Python using PyQT5 that takes orders and save them into a database.
On inserting the data, it seems to be going in but I'm never going inside the loop when trying to retrieve
Here's the code:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
import sys
from PyQt5.uic import loadUiType
ui, _ = loadUiType('jkp.ui')
# Declaring global variables to be used
global name, quantity, regular, xlsize, cheeze, ham
class MainApp(QMainWindow, ui):
def __init__(self, parent=None):
super(MainApp, self).__init__(parent)
QMainWindow.__init__(self)
self.setupUi(self)
self.initUi()
self.HandleButtons()
def initUi(self):
# Creating the database
con = QSqlDatabase.addDatabase('QSQLITE')
con.setDatabaseName('clients.sqlite')
if not con.open():
print('Unable to connect to the database')
sys.exit(1)
#
createTableQuery = QSqlQuery()
createTableQuery.exec("""
CREATE TABLE orders IF NOT EXISTS (
name VARCHAR (100) NOT NULL,
quantity INTEGER NOT NULL,
regular BOOLEAN,
xlsize BOOLEAN,
cheeze BOOLEAN,
ham BOOLEAN,
)
""")
def HandleButtons(self):
# connecting buttons to functions
self.pushButton_5.clicked.connect(self.Inserted)
self.pushButton_2.clicked.connect(self.ShowOrders)
def Inserted(self):
# Getting values from Gui and puttin em in variables
name = self.lineEdit.text()
quantity = self.spinBox.value()
regular = self.checkBox_3.isChecked()
xlsize = self.checkBox_4.isChecked()
cheeze = self.checkBox.isChecked()
ham = self.checkBox_2.isChecked()
# Insertion
query = QSqlQuery()
try:
query.exec(f"""
INSERT INTO orders (name, quantity, regular, xlsize, cheeze, ham)
VALUES('{name}','{quantity}','{regular}','{xlsize}','{cheeze}','{ham}',)
""")
print('inserted !')
except:
print('failed')
def ShowOrders(self):
query = QSqlQuery()
query.first()
print('so far so good') ### works till here ###
while query.next():
print('do we get here ?')
print(
query.value(name),
query.value(quantity),
query.value(regular),
query.value(xlsize),
query.value(cheeze),
query.value(ham),
)
def main():
app = QApplication(sys.argv)
window = MainApp()
window.show()
app.exec_()
if __name__ == '__main__':
main()
So upon executing the Gui appears the values entered are stored inside the variable, the insertion says 'inserted' but later on I don't know where the issue
You have the following errors:
You have syntax errors in SQL statements.
Qt does not throw exceptions when some function has an internal error but uses other methods (like returning a boolean).
It is not necessary to use global variables (also the use that the OP gives it is confusing).
class MainApp(QMainWindow, ui):
def __init__(self, parent=None):
super(MainApp, self).__init__(parent)
self.setupUi(self)
self.init_ui()
self.handle_buttons()
def init_ui(self):
# Creating the database
con = QSqlDatabase.addDatabase("QSQLITE")
con.setDatabaseName("clients.sqlite")
if not con.open():
print("Unable to connect to the database")
sys.exit(1)
#
query = QSqlQuery()
if not query.exec(
"""
CREATE TABLE IF NOT EXISTS orders (
name VARCHAR (100) NOT NULL,
quantity INTEGER NOT NULL,
regular BOOLEAN,
xlsize BOOLEAN,
cheeze BOOLEAN,
ham BOOLEAN
)
"""
):
print(query.lastError().text())
def handle_buttons(self):
# connecting buttons to functions
self.pushButton_5.clicked.connect(self.insert)
self.pushButton_2.clicked.connect(self.show_orders)
def insert(self):
# Getting values from Gui and puttin em in variables
name = self.lineEdit.text()
quantity = self.spinBox.value()
regular = self.checkBox_3.isChecked()
xlsize = self.checkBox_4.isChecked()
cheeze = self.checkBox.isChecked()
ham = self.checkBox_2.isChecked()
# Insertion
query = QSqlQuery()
query.prepare(
"INSERT INTO orders (name, quantity, regular, xlsize, cheeze, ham) VALUES (?, ?, ?, ?, ?, ?)"
)
query.addBindValue(name)
query.addBindValue(quantity)
query.addBindValue(regular)
query.addBindValue(xlsize)
query.addBindValue(cheeze)
query.addBindValue(ham)
if not query.exec_():
print(query.lastError().text())
print("failed")
else:
print("inserted !")
def show_orders(self):
query = QSqlQuery("SELECT * FROM orders")
record = query.record()
while query.next():
print("row")
for i in range(record.count()):
print(f"{record.fieldName(i)}: {query.value(i)}")

Python crashing when trying to view data in PyQT5 [duplicate]

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_())

PyQt: QTableView + QSqlTableModel - Copy and Paste All Selected Rows or Columns into Notepad or Excel

To start, I've already looked at ekhumoro's code on a nearly similar subject Here. However, when I attempt to implement this code, I am getting a different result. Instead of copying and pasting all the I selected, it only copies the first cell of the selected row or rows respectively. I need users to be able to select multiple rows or columns and paste those values in either excel or notepad. Any ideas?
GUI:
Code:
from PyQt4 import QtCore, QtGui, QtSql
import sys
import sqlite3
import time
import csv
import Search # This file holds our MainWindow and all design related things
# it also keeps events etc that we defined in Qt Designer
import os
try:
from PyQt4.QtCore import QString
except ImportError:
QString = str
class TableEditor(QtGui.QMainWindow, Search.Search_MainWindow):
def __init__(self, tableName, parent=None):
# Explaining super is out of the scope of this article
# So please google it if you're not familar with it
# Simple reason why we use it here is that it allows us to
# access variables, methods etc in the design.py file
super(self.__class__, self).__init__()
self.setupUi(self) # This is defined in design.py file automatically
# It sets up layout and widgets that are defined
self.model = QtSql.QSqlTableModel(self)
self.model.setTable('CAUTI')
self.model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
self.model.select()
self.model.setHeaderData(0, QtCore.Qt.Horizontal, "MRN")
self.model.setHeaderData(1, QtCore.Qt.Horizontal, "Last Name")
self.model.setHeaderData(2, QtCore.Qt.Horizontal, "First Name")
self.model.setHeaderData(3, QtCore.Qt.Horizontal, "Date of Event")
self.model.setHeaderData(4, QtCore.Qt.Horizontal, "Facility")
self.model.setHeaderData(5, QtCore.Qt.Horizontal, "Unit")
self.model.setHeaderData(6, QtCore.Qt.Horizontal, "User")
#self.tableView.verticalHeader().setVisible(False)
self.tableView.setModel(self.model)
self.setWindowTitle("HAI Table")
self.tableView.setColumnWidth(0,100)
self.tableView.setColumnWidth(1,100)
self.tableView.setColumnWidth(2,100)
self.tableView.setColumnWidth(3,100)
self.tableView.setColumnWidth(4,100)
self.tableView.setColumnWidth(5,100)
self.tableView.setColumnWidth(6,83)
self.tableView.setSortingEnabled(True)
self.tableView.setDropIndicatorShown(True)
self.tableView.setAcceptDrops(True)
self.tableView.setDragEnabled(True)
self.tableView.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.horizontalHeader().setMovable(True)
self.tableView.horizontalHeader().setDragEnabled(True)
self.clip = QtGui.QApplication.clipboard()
## Note: ** When I unblock this line of code, I get an error.
#self.tableView.installEventFilters(self)
self.tableView.horizontalHeader().setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.verticalHeader().setMovable(True)
self.tableView.verticalHeader().setDragEnabled(True)
self.tableView.verticalHeader().setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.setSelectionBehavior(QtGui.QTableView.SelectRows)
self.submitButton.clicked.connect(self.submit)
self.revertButton.clicked.connect(self.model.revertAll)
self.quitButton.clicked.connect(self.close)
self.tableView.horizontalHeader().setSortIndicatorShown(True)
self.tableView.horizontalHeader().setClickable(True)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.KeyPress and
event.matches(QtGui.QKeySequence.Copy)):
self.copySelection()
return True
return super(Window, self).eventFilter(source, event)
def copySelection(self):
selection = self.tableView.selectedIndexes()
if selection:
rows = sorted(index.row() for index in selection)
columns = sorted(index.column() for index in selection)
rowcount = rows[-1] - rows[0] + 1
colcount = columns[-1] - columns[0] + 1
table = [[''] * colcount for _ in range(rowcount)]
for index in selection:
row = index.row() - rows[0]
column = index.column() - columns[0]
table[row][column] = index.data()
stream = io.StringIO()
csv.writer(stream).writerows(table)
QtGui.qApp.clipboard().setText(stream.getvalue())
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDropEnabled
def main():
app = QtGui.QApplication(sys.argv)
#app.setStyle( "Plastique" )
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('HAI.db')
editor = TableEditor('CAUTI')
editor.show()
app.exec_()
if __name__ == '__main__':
main()
Your implementation is broken in several ways, so the code you copied never gets executed. As a result, the table's built-in copy function is used, which does not handle multiple selected items.
My original code for copying multiple table items is here. I think you should be able to get your example working by changing the following lines:
from PyQt4 import QtCore, QtGui, QtSql
# fix imports
import sys, io
...
class TableEditor(QtGui.QMainWindow, Search.Search_MainWindow):
def __init__(self, tableName, parent=None):
# do not ever use self.__class__ here
super(TableEditor, self).__init__()
...
# install event-filter properly
self.tableView.installEventFilter(self)
def eventFilter(self, source, event):
...
# call super properly
return super(TableEditor, self).eventFilter(source, event)
Obviously, I cannot run your actual example code, so there may be other errors that I haven't spotted.
This is much faster :
def copySelection(self):
clipboardString = StringIO()
selectedIndexes = self.ui.tableView.selectedIndexes()
if selectedIndexes:
countList = len(selectedIndexes)
for r in range(countList):
current = selectedIndexes[r]
displayText = current.data(QtCore.Qt.DisplayRole)
if r+1 < countList:
next_ = selectedIndexes[r+1]
if next_.row() != current.row():
displayText += ("\n")
else:
displayText += ("\t")
clipboardString.write(displayText)
pyperclip.copy(clipboardString.getvalue())

QComboBox with autocompletion works in PyQt4 but not in PySide

I've got a combo box with a custom completer that worked fine in PyQt4, but isn't working in PySide.
I have verified that the new completer is replacing the QComboBox's built in completer because inline completion is no longer occurring. However when run with PySide, the completer doesn't popup with a filtered list of options.
I've also tried ensuring that all text is all str or all unicode to avoid differences between the PyQt API 1 with QStrings and PySide's use of Python unicode types. Changing the text types has had no effect on either PyQt or PySide's behavior (PyQt keeps working, PySide doesn't work).
Here is my code:
from PySide import QtCore
from PySide import QtGui
#from PyQt4 import QtCore
#from PyQt4 import QtGui
class AdvComboBox(QtGui.QComboBox):
def __init__(self, parent=None):
super(AdvComboBox, self).__init__(parent)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setEditable(True)
# add a filter model to filter matching items
self.pFilterModel = QtGui.QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.pFilterModel.setSourceModel(self.model())
# add a completer, which uses the filter model
self.completer = QtGui.QCompleter(self.pFilterModel, self)
# always show all (filtered) completions
self.completer.setCompletionMode(QtGui.QCompleter.UnfilteredPopupCompletion)
self.setCompleter(self.completer)
# connect signals
def filter(text):
print "Edited: ", text, "type: ", type(text)
self.pFilterModel.setFilterFixedString(str(text))
self.lineEdit().textEdited[unicode].connect(filter)
self.completer.activated.connect(self.on_completer_activated)
# on selection of an item from the completer, select the corresponding item from combobox
def on_completer_activated(self, text):
print "activated"
if text:
print "text: ", text
index = self.findText(str(text))
print "index: ", index
self.setCurrentIndex(index)
# on model change, update the models of the filter and completer as well
def setModel(self, model):
super(AdvComboBox, self).setModel(model)
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
# on model column change, update the model column of the filter and completer as well
def setModelColumn(self, column):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
super(AdvComboBox, self).setModelColumn(column)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
combo = AdvComboBox()
names = ['bob', 'fred', 'bobby', 'frederick', 'charles', 'charlie', 'rob']
# fill the standard model of the combobox
combo.addItems(names)
combo.setModelColumn(0)
combo.resize(300, 40)
combo.show()
sys.exit(app.exec_())
I figured it out while writing the question...
It appears that while the PySide QCompleter documentation lists an option to initialize the QCompleter with a model and a parent, it isn't actually working.
The solution is to set the model of the completer after it is initialized.
Here is the working code:
from PySide import QtCore
from PySide import QtGui
class AdvComboBox(QtGui.QComboBox):
def __init__(self, parent=None):
super(AdvComboBox, self).__init__(parent)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setEditable(True)
# add a filter model to filter matching items
self.pFilterModel = QtGui.QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.pFilterModel.setSourceModel(self.model())
# add a completer
self.completer = QtGui.QCompleter(self)
#Set the model that the QCompleter uses
# - in PySide doing this as a separate step worked better
self.completer.setModel(self.pFilterModel)
# always show all (filtered) completions
self.completer.setCompletionMode(QtGui.QCompleter.UnfilteredPopupCompletion)
self.setCompleter(self.completer)
# connect signals
def filter(text):
print "Edited: ", text, "type: ", type(text)
self.pFilterModel.setFilterFixedString(str(text))
self.lineEdit().textEdited[unicode].connect(filter)
self.completer.activated.connect(self.on_completer_activated)
# on selection of an item from the completer, select the corresponding item from combobox
def on_completer_activated(self, text):
print "activated"
if text:
print "text: ", text
index = self.findText(str(text))
print "index: ", index
self.setCurrentIndex(index)
# on model change, update the models of the filter and completer as well
def setModel(self, model):
super(AdvComboBox, self).setModel(model)
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
# on model column change, update the model column of the filter and completer as well
def setModelColumn(self, column):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
super(AdvComboBox, self).setModelColumn(column)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
combo = AdvComboBox()
names = ['bob', 'fred', 'bobby', 'frederick', 'charles', 'charlie', 'rob']
# fill the standard model of the combobox
combo.addItems(names)
combo.setModelColumn(0)
combo.resize(300, 40)
combo.show()
sys.exit(app.exec_())

Updating QtableWidget pyqt

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!

Categories

Resources