How to focus QTableWidget

I’ve got a regular Table Widget on my main screen (QWidget) and added to a Vertical layout. To navigate to other displays in my application I’m clearing out interior widgets and sub layouts.

What I’ve noticed is I can redisplay my table and show a current selection for that table without any issues but I’m unable to reset focus to the table for some reason. The only work around I came up with is to use pynput and manually issue a mouse click to a certain position on the screen but this is not bullet proof.

Table: self.serviceTable.setFocus()

Thanks in advance

Hi @weave
First up, to do this sort of page navigation you usually don’t need to delete & recreate the UI. You can instead use the QStackedWidget widget. This has “pages” that behave like tabs in that you can switch between the various states by selecting (through code) which page to show, although no tabs are visible.

Without seeing the code/what you’ve tried it’s difficult to know what to suggest. But focus is usually manually triggered by using .setFocus(). For example, see this demo code –

from PyQt6.QtWidgets import QApplication, QTableWidget, QVBoxLayout, QTableWidgetItem, QWidget, QPushButton, QLineEdit
from PyQt6.QtCore import QItemSelectionModel


class Window(QWidget):
    
    def __init__(self):
        super().__init__()
        
        self.table = QTableWidget()
        self.table.setColumnCount(1)
        self.table.setRowCount(10)
        
        # Add some rows.
        for n in range(10):
            i = QTableWidgetItem(str(n))
            self.table.setItem(n, 0, i)
        
        self.input = QLineEdit()
        
        self.button = QPushButton("Press to focus table")
        self.button.pressed.connect(self.set_table_focus)
        
        layout = QVBoxLayout()
        layout.addWidget(self.table)
        layout.addWidget(self.input)
        layout.addWidget(self.button)
        
        self.setLayout(layout)
        
    def set_table_focus(self):
        self.table.setFocus()
    

app = QApplication([])
w = Window()
w.show()
app.exec()

If you click somewhere in the table to set the selection, then click out into the line edit, the selected items will become grayed-out. If you click the button the focus will return to the able & they’ll turn blue.

Can you give some example code that you’re using so I can try & reproduce the issue?

Thanks for the response Martin !

It must be something I’m doing that’s throwing off the setfocus. Below is a copy of my method that displays page contents (builds the table). The very last part of that method is where I attempt to refocus the table with pynput with the else for that section resetting focus when all else fails.

def set_page_contents(self):

  # Configure Page Widgets

  if self.animation_finished:

      self.animation_finished = False

      if self.page == "Home":

          # Page Title

          titleLbl = QLabel()
          image = QPixmap("./images/services_title.png")
          titleLbl.setPixmap(image)
          titleLbl.setFixedHeight(80)
          titleLbl.setAlignment(Qt.AlignmentFlag.AlignCenter)

          self.child_layout.addWidget(titleLbl)

          # Service Schedule

          self.serviceTable = QTableWidget()

          self.serviceTable.setColumnCount(4)
          self.serviceTable.setHorizontalHeaderLabels(["Address","City","Type","Status"])
          self.serviceTable.horizontalHeader().setFixedHeight(40)
          self.serviceTable.horizontalHeader().setStyleSheet("font-size: 18px; font-weight: bold;"
                                                             "background-color: #4198e3")

          self.serviceTable.setColumnWidth(0, 220)
          self.serviceTable.setColumnWidth(1, 120)
          self.serviceTable.setColumnWidth(2, 150)
          self.serviceTable.setColumnWidth(3, 90)

          self.serviceTable.verticalHeader().setVisible(False)
          self.serviceTable.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)    # make readonly
          self.serviceTable.setStyleSheet("QTableWidget::item:selected{background: #0000ff;}")

          # Check Service Schedule Data

          if self.service_schedule_data != None:

              if len(self.service_schedule_data) > 0:

                  self.serviceTable.setRowCount(len(self.service_schedule_data))

                  rowCtr = 0
                  self.rowImgs = []

                  for rec in self.service_schedule_data:

                      if rowCtr % 2 > 0:
                          rowBkgrnd = QtGui.QColor("#d0d7ea")
                      else:
                          rowBkgrnd = QtGui.QColor("#ececee")

                      # Cell Contents

                      self.serviceTable.setItem(rowCtr,0,QTableWidgetItem(rec[2]))
                      self.serviceTable.item(rowCtr,0).setBackground(rowBkgrnd)
                      self.serviceTable.setItem(rowCtr,1,QTableWidgetItem(rec[3].center(22)))
                      self.serviceTable.item(rowCtr,1).setBackground(rowBkgrnd)
                      self.serviceTable.setItem(rowCtr,2,QTableWidgetItem(rec[4]))
                      self.serviceTable.item(rowCtr,2).setBackground(rowBkgrnd)

                      rowImg = QLabel()

                      if rowCtr % 2 > 0:
                          rowImg.setStyleSheet("background-color: #d0d7ea")
                      else:
                          rowImg.setStyleSheet("background-color: #ececee")

                      image = QPixmap(self.get_image(rec[4], rec[6])).scaled(90,40)
                      rowImg.setPixmap(image)
                      rowImg.setAlignment(Qt.AlignmentFlag.AlignCenter)

                      self.serviceTable.setCellWidget(rowCtr,3,rowImg)
                      self.serviceTable.setRowHeight(rowCtr,40)

                      self.rowImgs.append(rowImg)

                      rowCtr += 1

                  self.serviceTable.cellClicked.connect(self.show_service_selection)
                  self.serviceTable.itemSelectionChanged.connect(self.show_service_selection)

                  self.serviceTable.setCurrentIndex(QModelIndex())

                  if len(self.service_schedule_data) > 6:
                      self.serviceTable.setFixedSize(598,284)
                  else:
                      servtHeight = 40 * len(self.service_schedule_data)
                      servtHeight += 40

                      self.serviceTable.setFixedSize(598,servtHeight)

              else:

                  self.serviceTable.setFixedSize(598,40)

          self.child_layout.addWidget(self.serviceTable)


          # Page Buttons


          if self.selected_service_schedule_record == -1:

              self.mouse_pos = ()

              if self.selected_address_record > -1:
                  self.selected_address_record = -1

              self.addrBtn = QPushButton()

              self.addrBtn.setObjectName("addrBtn")
              self.addrBtn.setFlat(True)
              self.addrBtn.setIcon(QIcon("./images/address_up.png"))
              self.addrBtn.setFixedSize(64,64)
              self.addrBtn.setIconSize(QSize(64,64))

              self.addrBtn.pressed.connect(lambda but=self.addrBtn: self.show_pressed(but))
              self.addrBtn.released.connect(lambda but=self.addrBtn: self.show_released(but))

              self.child_buttons.addWidget(self.addrBtn)

              self.addsBtn = QPushButton()

              self.addsBtn.setObjectName("addsBtn")
              self.addsBtn.setFlat(True)
              self.addsBtn.setFixedSize(64,64)
              self.addsBtn.setIcon(QIcon("./images/add_up.png"))
              self.addsBtn.setIconSize(QSize(64,64))

              self.addsBtn.pressed.connect(lambda but=self.addsBtn: self.show_pressed(but))
              self.addsBtn.released.connect(lambda but=self.addsBtn: self.show_released(but))

              self.child_buttons.addWidget(self.addsBtn)

              self.exitBtn = QPushButton()

              self.exitBtn.setObjectName("exitBtn")
              self.exitBtn.setFlat(True)
              self.exitBtn.setFixedSize(64, 64)
              self.exitBtn.setIcon(QIcon("./images/exitapp1.png"))
              self.exitBtn.setIconSize(QSize(64,64))

              self.exitBtn.pressed.connect(lambda but=self.exitBtn: self.show_pressed(but))
              self.exitBtn.released.connect(lambda but=self.exitBtn: self.show_released(but))

              self.child_buttons.addWidget(self.exitBtn)

          else:

              self.update_mouse_pos = False
              self.serviceTable.selectRow(self.selected_service_schedule_row)

              self.addrBtn = QPushButton()

              self.addrBtn.setObjectName("addrBtn")
              self.addrBtn.setFlat(True)
              self.addrBtn.setIcon(QIcon("./images/address_up.png"))
              self.addrBtn.setFixedSize(64, 64)
              self.addrBtn.setIconSize(QSize(64, 64))

              self.addrBtn.pressed.connect(lambda but=self.addrBtn: self.show_pressed(but))
              self.addrBtn.released.connect(lambda but=self.addrBtn: self.show_released(but))

              self.child_buttons.addWidget(self.addrBtn)

              self.addsBtn = QPushButton()

              self.addsBtn.setObjectName("addsBtn")
              self.addsBtn.setFlat(True)
              self.addsBtn.setFixedSize(64, 64)
              self.addsBtn.setIcon(QIcon("./images/add_up.png"))
              self.addsBtn.setIconSize(QSize(64, 64))

              self.addsBtn.pressed.connect(lambda but=self.addsBtn: self.show_pressed(but))
              self.addsBtn.released.connect(lambda but=self.addsBtn: self.show_released(but))

              self.child_buttons.addWidget(self.addsBtn)

              self.delBtn = QPushButton()

              self.delBtn.setObjectName("delBtn")
              self.delBtn.setFlat(True)
              self.delBtn.setFixedSize(64, 64)
              self.delBtn.setIcon(QIcon("./images/remove_up.png"))
              self.delBtn.setIconSize(QSize(64, 64))

              self.delBtn.pressed.connect(lambda but=self.delBtn: self.show_pressed(but))
              self.delBtn.released.connect(lambda but=self.delBtn: self.show_released(but))

              self.child_buttons.addWidget(self.delBtn)

              self.editBtn = QPushButton()

              self.editBtn.setObjectName("editBtn")
              self.editBtn.setFlat(True)
              self.editBtn.setFixedSize(64, 64)
              self.editBtn.setIcon(QIcon("./images/detail_edit_up.png"))
              self.editBtn.setIconSize(QSize(64, 64))

              self.editBtn.pressed.connect(lambda but=self.editBtn: self.show_pressed(but))
              self.editBtn.released.connect(lambda but=self.editBtn: self.show_released(but))

              self.child_buttons.addWidget(self.editBtn)

              self.exitBtn = QPushButton()

              self.exitBtn.setObjectName("exitBtn")
              self.exitBtn.setFlat(True)
              self.exitBtn.setFixedSize(64, 64)
              self.exitBtn.setIcon(QIcon("./images/exitapp1.png"))
              self.exitBtn.setIconSize(QSize(64, 64))

              self.exitBtn.pressed.connect(lambda but=self.exitBtn: self.show_pressed(but))
              self.exitBtn.released.connect(lambda but=self.exitBtn: self.show_released(but))

              self.child_buttons.addWidget(self.exitBtn)

          self.page_refreshed = False


      elif self.page == "Addresses":

          titleLbl = QLabel()
          image = QPixmap("./images/addresses_title.png")
          titleLbl.setPixmap(image)
          titleLbl.setFixedHeight(80)
          titleLbl.setAlignment(Qt.AlignmentFlag.AlignCenter)

          self.child_layout.addWidget(titleLbl)

          self.addrBtn = QPushButton()

          self.addrBtn.setObjectName("srvcBtn")
          self.addrBtn.setFlat(True)
          self.addrBtn.setIcon(QIcon("./images/home_page_up.png"))
          self.addrBtn.setIconSize(QSize(64,64))

          self.addrBtn.pressed.connect(lambda but=self.addrBtn: self.show_pressed(but))
          self.addrBtn.released.connect(lambda but=self.addrBtn: self.show_released(but))

          self.child_buttons.addWidget(self.addrBtn)

          self.exitBtn = QPushButton()

          self.exitBtn.setObjectName("exitBtn")
          self.exitBtn.setFlat(True)
          self.exitBtn.setIcon(QIcon("./images/exitapp1.png"))
          self.exitBtn.setIconSize(QSize(64,64))

          self.exitBtn.pressed.connect(lambda but=self.exitBtn: self.show_pressed(but))
          self.exitBtn.released.connect(lambda but=self.exitBtn: self.show_released(but))

          self.child_buttons.addWidget(self.exitBtn)

          self.page_refreshed = False


      if self.child_buttons_layout_added == False:
          self.child_layout.addLayout(self.child_buttons)
          self.child_buttons_layout_added = True


      if self.page == "Home":

          if self.selected_service_schedule_record > -1:

              if self.mouse_pos != ():

                  row = self.mouse_pos[1]

                  if row in range(494,704):

                      if self.image_changed:
                          self.mouse_pos = (row,700)
                          self.image_changed = False

                      self.on_screen_mouse.position = self.mouse_pos

                      #print(self.mouse_pos)

                      self.on_screen_mouse.click(Button.left)
                      self.on_screen_mouse.release(Button.left)

                      self.mouse_pos = ()

                  else:

                      self.mouse_pos = ()

                      self.serviceTable.setFocus()

I am aware of the stacked widget and did consider using it initially but the way I designed my app I needed to be able to dynamically create widgets depending on the situation and didn’t feel a stacked widget would work for me.

1 Like

Thanks for the code. It’s difficult to know without seeing the rest (i.e. what state the application is by the time that line is reached).

First I’d say try adding debug calls to make sure that line is being reached based on the logic around it. Once that’s confirmed, it may be the fact that you’ve just added the table that focusing isn’t working. The widgets you create & insert aren’t really activated until returning to the event loop.

You can see if this is the issue by postponing the call using a timeout.

from PySide6.QtCore import QTimer

QTimer.singleshot(0, self.serviceTable.setFocus)

way I designed my app I needed to be able to dynamically create widgets depending on the situation

That’s totally fine, it all depends on what you’re building. You might find it helpful to split the pages into separate functions to avoid the deep nesting, then you can do something like e.g.

if self.page == "Home":
    self.setup_home_ui()

Thanks Martin

Maybe it’s a call back … they tend to confuse me from time to time. I sometimes use a flag (self.whatever = True) to prevent functions from doing stuff too early. I’ll keep at it and let everyone know if I nail it.

Don