0

I have a QTableWidget with which I would like to update from a QLinEdit embedded into a context menu. Now, in the QLinEdit a server name is entered, when the key is pressed the program scans MySQL database to see if the server name is in it, if it is, it updates the QTableWidgetwith the data from the server name table, if it is not found, it gives an error messageBox.

What I can not do is connect the context menu QLinEdit to update the QTableWidget.

connecting QTableWidget to context menu:

self.table1.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.table1.customContextMenuRequested.connect(self.handleHeaderMenu)

contextmenu:

    def handleHeaderMenu(self, pos):
        self.custom_choice = QtGui.QLineEdit()
        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)
        action = self.menu.exec_(QtGui.QCursor.pos())
        self.connect(self.custom_choice, QtCore.SIGNAL("returnPressed()"),self.refreshdata)

Data fetcher/scanner:

def get_data(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()
        print new_user_name,table_name
        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 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 = 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} 
Bless
  • 5,052
  • 2
  • 40
  • 44

1 Answers1

0

The design of your handleHeaderMenu is a bit off. One of the main issues with the way it is currently structured is that you connect a signal to the QLineEdit after the popup menu has already finished. So you would miss that signal.

action = self.menu.exec_(QtGui.QCursor.pos())
self.connect(self.custom_choice, 
                QtCore.SIGNAL("returnPressed()"),
                self.refreshdata)

QMenu.exec_() is a blocking call. It starts the event loop for the menu and waits for it to finish. Once it closes and returns the QAction that was selected, you then make a connection. I will get into a correction for this after my next point...

The menu you are building from scratch each time doesn't have to be saved to member attributes and used externally. There are two ways to go about doing a custom popup menu. If its primarily static, then you can build it once in your class init, or make it it's own class, and then just reuse the instance. Or in your case, you could build it up each time which is fine. But instead of relying on persistant references to the components of the menu and using a signal, why not just build it temporarily, and explicitly handle the results?

def handleHeaderMenu(self, pos):
    menu = QtGui.QMenu()
    menu.setStyleSheet("""
        QMenu::item {
            background-color: #264F7D;
            color: white; 
            font-weight:bold;}
    """)
    text = menu.addAction("Choose Server to Monitor:")
    text.setEnabled(False)
    menu.addSeparator()

    actionJuliet = menu.addAction('Juliet')
    actionJulietleft = menu.addAction('JulietLeft')
    actionPong = menu.addAction('Pong')
    actionHulk = menu.addAction('Hulk')

    wac = QtGui.QWidgetAction(menu)
    custom_choice = QtGui.QLineEdit()
    custom_choice.setPlaceholderText("Server")
    wac.setDefaultWidget(custom_choice)
    menu.addAction(wac)

    menu.setActiveAction(wac)
    custom_choice.returnPressed.connect(wac.trigger)

    action = menu.exec_(QtGui.QCursor.pos())
    if action:
        if action == wac:
            self.tx = str(custom_choice.text()).strip()
        else:
            self.tx = str(action.text())

        self.refreshdata()

def refreshdata(self):
    print self.tx

Here we just create a bunch of temp widgets for the menu that will get garbage collected. After showing the menu, we check the returned action and then manually set our table attribute, and call refresh. Also, we needed to set the signal from the custom QLineEdit to trigger its widget action internally.

Lastly, is it really necessary to do 8 sql queries and a whole bunch of data reorganizing every time you want to load this data? This could be highly simplified:

def get_data(self):

    table_check = """
        SELECT table_name FROM information_schema.tables 
        WHERE table_schema = %s AND table_name = %s 
        """

    table_name = self.tx
    count = self.model.execute(table_check, (self.theDatabaseName, table_name))
    if not count:
        # warn the user that the table name does not exist
        warn_user_of_bad_table()
        return

    sql = """
        SELECT 
            computer_name, idle_time, files_opened, 
            active_time, session_type, cpu, avg_disk_queue
        FROM %s 
        """ % table_name

    count = self.model.execute(sql)
    if not count:
        warn_database_error()
        return

    results = self.model.fetchall()
    user, idle , files, active, session, cpu, disk = zip(*results)
    self.lista = user
    self.listb = disk
    self.listc = cpu
    self.listd = active
    self.liste = files
    self.listf = session
    self.listg = idle  
    self.mystruct2 = {
        'A' : self.lista, 
        'B' : self.listb, 
        'C' : self.listc,
        'E' : self.liste,
        'D' : self.listd,
        'F' : self.listf,
        'G' : self.listg
    }

You only need two queries here. The first really simple one to check if the table exists, by using the scheme, instead of parsing through a big SHOW TABLES output. And the second which gets all your data in one query (as a bunch of rows, and then uses zip to regroup them into columns.

jdi
  • 90,542
  • 19
  • 167
  • 203
  • But then how do I update the actual table, if I pass it to the tablewidget as iniitial data, the initialdata reference is before any of these functions so it gives me an error saying that there are no such variables as the ones defined in handleheadermenu? –  Aug 17 '12 at 16:10
  • Im not sure I understand your question. You should not need to access anything from that popup menu. All it is supposed to do is save the chosen value to `self.tx` and then call your refresh method. The refresh method would then look at that value and do your sql query, which can then update your table. – jdi Aug 17 '12 at 17:36
  • Yes, but how do I pass the dictionary, mystruct2 to the table itself? –  Aug 17 '12 at 17:38
  • I just assumed your callables were in the order you wanted for your actual code. If refresh should not be called first, then have the popup call getdata and then call refresh – jdi Aug 17 '12 at 17:44
  • I have that currently set but the popup is in the __init__ function and that is why it is giving me errors. –  Aug 17 '12 at 17:49
  • You are starting a popup in the init? Thats odd. A popup is normally executed by a use context operation. Well make sure you put it after everything else in your class is set up. Or call it with a QTimer.singleShot(1, func) – jdi Aug 17 '12 at 17:58
  • This is how the mystruct2 dictionary is initialized, `self.initialData = self.get_data() self.table1 = MyTableStatus(self.initialData,150,4)`, howevert this is in the __init__ function. –  Aug 17 '12 at 18:17
  • If you notice, get_data does not return anything. All you would need to do is call `self.get_data()`, and then do: `MyTableStatus(self. mystruct2,150,4)` – jdi Aug 17 '12 at 18:27
  • Yes, but if i call `self.get_data()` before the contextmenu function it gives me an error, if i call `self.get_data()` after the table variable then `mystruct2` is not defined. –  Aug 17 '12 at 18:41
  • You have a design issue. I assume its because you have not set `self.tx` to some default table name. So either set that to something in your init first, and then call get_data, dont do any initial data, and just ask the user to pick a table name when the app is shown – jdi Aug 17 '12 at 18:47
  • Is there anyway that I can initially have the table set data, but then when the user triggers teh contexmenu signal it erases and sets it with the table of choice? –  Aug 17 '12 at 19:11
  • Yes. Set the self.tx to a default table name early in your init. Then call get_data and refresh. – jdi Aug 17 '12 at 19:16
  • Ok, it works, but now when I change my contextmenu option, it doesn't delete the previous records. I clear the dict but it still does the same think. –  Aug 17 '12 at 19:53
  • Depends how you are updating the table. You chose to use a TableWidget and manage a completely separate data structure. Make sure you clear the dict, all those lists, and clear out and delete all the TableWidgetItems from the table. – jdi Aug 17 '12 at 19:56