1

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()
  • I wish people new to PyQt would stop using globals. it seems to be the common case. – jdi Aug 08 '12 at 05:44
  • I see a bunch of code setting up signals and a database cursor, and then you give the data once to your custom table, but nothing in here sends more data to your table. Any reason you are going this route instead of using QtSql? This can all be done and managed with a qsql model/view – jdi Aug 08 '12 at 15:52
  • Some machines to do not support qmysql, and i am trying to avoid it. Is there anyway else that I can do it without using qmysql (I know how to do it with qmysql). –  Aug 08 '12 at 16:37

2 Answers2

3

The logic in this code is a bit messy, but I can see your problem with the data not updating.

  1. 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.
  2. 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.
  3. 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).

jdi
  • 90,542
  • 19
  • 167
  • 203
  • Is there a way that I can have default data and then update the data on a contextmenu click? I can upload the code if necessary. Thanks! –  Aug 13 '12 at 22:03
  • Yeah, what I want to do is on a contextmenu click on the qtablewidget I want the table to update, but I want to have defualt data before a contextmenu option is chosen. –  Aug 13 '12 at 22:14
  • Then just load the data you want during the `__init__`. If you need help with a context menu, that should probably be a new question so I don't go editing this with completely unrelated info – jdi Aug 13 '12 at 22:21
  • I already have the context menu, now i just need to connect everything together but I am unable to, i am putting the code addtiton to the original question. –  Aug 14 '12 at 14:45
  • You have made your code examples far too long and messy. You should only be including small specific examples of your problem. People dont want to have to read your entire application. This is what I meant about not updating the question with new questions. Its going to confuse this already accepted answer. You should just revert it, then make a new question with a really small snippet, asking how to properly connect a context menu to update a table. – jdi Aug 14 '12 at 15:23
  • I have created a new question, here, http://stackoverflow.com/questions/11959555/updating-a-qtablewidget-from-contextmenu-linedit-pyqt –  Aug 15 '12 at 14:26
0

There are a few things going on here:

  1. You don't need the global declaration in your init method (textedit and rows are not global)
  2. 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)
  3. 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
  4. 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.
  5. 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!

Eric Hulser
  • 3,912
  • 21
  • 20
  • I think point #3 is wrong. That is valid PyQt syntax. It parents the layout to the window. The rest are very good points. – jdi Aug 08 '12 at 05:39
  • I am trying to update one cell in my table every two seconds from a mysql database. The database gets upadted with data and I am trying to update the one cell in my table with the new data. But i am not sure how to approach it on a QTableWidget. –  Aug 08 '12 at 13:52
  • @user1582983: you already have a qtimer set to fire every two seconds. The sleep is just going to completely freeze your main gui thread every two seconds, for two more seconds. – jdi Aug 08 '12 at 14:47
  • That should not of been there, but I have put an updated version of the code, now the data from mysql is shown but does not update. –  Aug 08 '12 at 14:59