0

I load the data into qtableview using a looping that makes the select every 2 seconds. It happens that the screen is locked while the select runs and the data is loaded in the view, each select brings the updated data, but I can't click on the radiobuttons that are locked. I don't know how to implement a way to update the view using dataChanged.emit(), maybe I don't even need to run the select so many times, I looked for some information in English but I couldn't understand a way that I can implement. What I want is that it doesn't lock so that I, through the visualization of the view data (server available), can mark the radiobuttons to update the servers.

https://i.stack.imgur.com/pfikz.png

Model class code:

class TableModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data
        
    def data(self, index, role):
        if role == Qt.TextAlignmentRole:
            value = self._data[index.row()][index.column()]

            if isinstance(value, int) or isinstance(value, float):
                return Qt.AlignHCenter

        if role == Qt.DisplayRole:

            return self._data[index.row()][index.column()]


    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])

main class code:

class Ui_Form(object):
    def setupUi(self, Form):

    self.pushButton_query.clicked.connect(self.executa_query)
    
    def executa_query(self):
        flag = 0
        while flag <= execucao:
            self.select_dados()
            time.sleep(segundos)
            flag += 1
            if flag == execucao:
                self.status_execucao = True

    def select_dados(self):
        dados = []
        sql = """
              SELECT * FROM(
                SELECT (SELECT COUNT(1)
                          FROM GUIA_ITEM G
                         WHERE G.GUIA_COD_ID = ID_GUIA),
                        S.*
                  FROM SAPIA_LOG S
                 WHERE DATA > TRUNC(SYSDATE) - 1
                 ORDER BY 2 DESC)
               WHERE ROWNUM <= 20
              """
        self.cur.execute(sql)
        for x in self.cur:
            dados.append([int(x[0]),int(x[2]),int(x[2]),x[3].strftime("%d/%m/%Y %H:%M:%S"),x[4],x[5],x[6],x[7]])

        self.model = TableModel(dados)
        self.tableView.setModel(self.model)
        self.tableView.resizeColumnsToContents()
        self.tableView.resizeRowsToContents()
        QApplication.processEvents()
MJAGO
  • 35
  • 5
  • Blocking functions should *never* be used in UI environments, as they block the event loop. If you want to call a function at timed intervals, use [QTimer](https://doc.qt.io/qt-5/qtimer.html). That said, instead of using a custom model for the query consider [QSqlQueryModel](https://doc.qt.io/qt-5/qsqlquerymodel.html) instead. Finally, editing pyuic generated files is considered bad practice. See the official guidelines about [using Designer](https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html) – musicamante Dec 14 '21 at 21:21
  • Thank you @musicamante, I really used QTimer and it worked perfectly, thanks again for the tips, I tried to generate the driver for Oracle (QOCI) but I couldn't, many attempts with error in the compilation that I can't solve so I'm using cx_Oracle. I'll also read about the guidelines for using qtdesigner and rewrite the code following best practices. – MJAGO Dec 17 '21 at 17:55

1 Answers1

1

This happens because only a single thread is being used. A Single Thread can't handle both GUI and Looping tasks simultaneously. So you should make another Thread using QThread. Here is an example.

from PyQt5.QtCore import QThread, pyqtSignal, QAbstractTableModel
from PyQt5.QtWidgets import QPushButton

class Worker(QThread):
    # pyqtSignal(type)
    progress_signal = pyqtSignal()
    finished = pyqtSignal()
  
    def __init__(self, parent=None):
      super(Worker, self).__init__(parent)

    def run(self):
      # Do Task
      main_class = MainClass()
      something = main_class.do_something()
      self.progress_signal.emit(something)
      # End
      self.finished.emit()
    
class MainClass():
    def do_something(self):
      return something

class GUIClass(QAbstractTableModel):
    def __init__(self):
        super().__init__()
        self.ui()
    def ui(self):
        # add layout ...
        # ...

        self.pushButton_query = QPushButton()
        self.pushButton_query.clicked.connect(self.main)

    def main(self):
      self.thread = QThread(parent=self)
      self.worker = Worker()
      self.worker.moveToThread(self.thread)
      self.thread.start()

      self.thread.started.connect(self.worker.run)
      self.thread.finished.connect(self.thread.deleteLater)
      self.worker.finished.connect(self.worker.deleteLater)

      self.worker.progress_signal(self.result_from_worker)
      # or use lambda
    
      self.worker.finished.connect(lambda: self.something)
      self.thread.quit()
      # self.thread.exit()

    def result_from_worker(self):
        # do something
        pass  

BG Park
  • 169
  • 7
  • 2
    Some important notes: 1. `progress_signal` is a signal, it's not callable, so `self.worker.progress_signal(...)` will throw an execption (it should be `self.worker.progress_signal.connect(...)`); 2. an item model should not be responsible of creating or "owning" widgets; 3. your imports are missing QAbstractTableModel and QPushButton; 4. `QThread.quit()` is identical to `QThread.exit()`; 5. the lambda connected to `finished` is missing the parentheses for `self.something`, but since it doesn't have arguments, using a lambda is pointless, just do `self.worker.finished.connect(self.something)`; – musicamante Dec 15 '21 at 02:44
  • 2
    6. exiting the thread just after starting it is really not a good idea, as it would potentially prevent the connected function to actually process at all; 7. unless an event loop is actually required, subclassing from QThread is usually considered better and easier (at least for Python): they are *not* the same thing, and that's an important aspect to consider; 8. the creation of an object that overwrites a (possibly) previously set instance attribute could lead to a crash or, at least, an unworking thread, as the target has been deleted in the meantime; 9. standard indentation uses 4 spaces; – musicamante Dec 15 '21 at 02:56
  • @musicamante Thank you for the all kindly answer :-) It's my first answer. I didn't use IDE and just typing it for this answer. So there are many mistakes. I will consider these comments carefully :) – BG Park Dec 15 '21 at 03:04
  • no harm done, I'm just pointing out problems for the sake of quality of answers :-) There's no problem in writing the whole code from mind (on the contrary, it's a very good exercise!), but you also need extra care exactly because you're not using an IDE that could help you in noticing problems and you're probably not able to test the code anyway due to the way the question and the answer were put. Remember that you could always [edit] your answer *anytime* and improve it by considering the points above. – musicamante Dec 15 '21 at 03:23