I have a Qlistview with some items. I want to set selection at first item at startup of window. selectionModel().selectedRows() returns the selectet item. But
QAbstractItemModel().setCurrentIndex(0) does't select the item. How could that be done like setSelection(INDEX).
self.listView = QtWidgets.QListView()
self.entry = QtGui.QStandardItemModel()
self.listView.setModel(self.entry)
----------
self.listView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.listView.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
self.listView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
----------
self.listView.QAbstractItemModel().setCurrentIndex(0) #<------ Not really working
selection = self.listView.selectionModel().selectedRows()
print(selection)
If you want to select an item then you must use the select() method of selectionModel():
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.listView = QtWidgets.QListView(
editTriggers=QtWidgets.QAbstractItemView.NoEditTriggers,
selectionMode=QtWidgets.QAbstractItemView.SingleSelection,
selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
)
self.entry = QtGui.QStandardItemModel()
self.listView.setModel(self.entry)
for letter in list("abcdefghijklmnopqrstuvwxyz"):
it = QtGui.QStandardItem(letter)
self.entry.appendRow(it)
ix = self.entry.index(0, 0)
sm = self.listView.selectionModel()
sm.select(ix, QtCore.QItemSelectionModel.Select)
self.setCentralWidget(self.listView)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Related
I have embedded a QPushbutton in a Qwidget in a QListView:
QPushbutton >> QWidget >> QListView
list_widget = QWidget()
list_widget.layout(QHBoxLayout())
btn = QPushButton()
btn.pressed.connect(clicked)
list_widget.layout().addWidget(QPushButton())
list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
def clicked():
row = list_view.selectedIndexes()
The problem is now list_view.selectedIndexes() does not return the row of the pressed button, when pressed.
This seems to work only when the QPushbutton is embedded in the QListView directly: QPushbutton >> QListView.
Does anyone have an idea how to delegate the focus of the pushbutton to the QListView?
When you click on the button it is not transmitted to the QListView because the button consumes it and does not transmit it to other widgets so if you want to obtain the row it must be obtained indirectly, a possible solution is to use the geometry for it you must obtain the sender , in this case the button, and then for its topleft to global positions, then convert it to a local position with respect to the viewport of QListView, using that position with the method indexAt() you get the QModelIndex.
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_model = QtGui.QStandardItemModel(200, 1)
self.list_view = QtWidgets.QListView()
self.list_view.setModel(self.list_model)
self.setCentralWidget(self.list_view)
for row in range(self.list_model.rowCount()):
list_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(list_widget)
btn = QtWidgets.QPushButton(str(row))
btn.pressed.connect(self.clicked)
hlay.addWidget(btn)
hlay.setContentsMargins(0, 0, 0, 0)
self.list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
#QtCore.pyqtSlot()
def clicked(self):
btn = self.sender()
gp = btn.mapToGlobal(QtCore.QPoint())
lp = self.list_view.viewport().mapFromGlobal(gp)
ix = self.list_view.indexAt(lp)
print("row", ix.row())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Another much simpler way is to pass the row as an argument using functools.partial():
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_model = QtGui.QStandardItemModel(200, 1)
self.list_view = QtWidgets.QListView()
self.list_view.setModel(self.list_model)
self.setCentralWidget(self.list_view)
for row in range(self.list_model.rowCount()):
list_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(list_widget)
btn = QtWidgets.QPushButton(str(row))
btn.pressed.connect(partial(self.clicked, row))
hlay.addWidget(btn)
hlay.setContentsMargins(0, 0, 0, 0)
self.list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
#QtCore.pyqtSlot(int)
def clicked(self, row):
print(row)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Or using a lambda method:
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_model = QtGui.QStandardItemModel(200, 1)
self.list_view = QtWidgets.QListView()
self.list_view.setModel(self.list_model)
self.setCentralWidget(self.list_view)
for row in range(self.list_model.rowCount()):
list_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(list_widget)
btn = QtWidgets.QPushButton(str(row))
btn.pressed.connect(lambda *args, row=row: self.clicked(row))
hlay.addWidget(btn)
hlay.setContentsMargins(0, 0, 0, 0)
self.list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
#QtCore.pyqtSlot(int)
def clicked(self, row):
print(row)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
In my case I prefer to use partials since you do not need to write a lot of logic and it's thread-safe.
I am using PyQt5 and using PyCharm. How can I align all cells under one column to center? The code below seems to be working but for only one cell which is the header. What should I change or add?
item3 = QtWidgets.QTableWidgetItem('Item Name')
item3.setTextAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter)
self.tableWidget.setHorizontalHeaderItem(2, item3)
A simple way to establish the alignment of a column is through the delegates:
import sys
from PyQt5 import QtCore, QtWidgets
class AlignDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super(AlignDelegate, self).initStyleOption(option, index)
option.displayAlignment = QtCore.Qt.AlignCenter
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.tableWidget = QtWidgets.QTableWidget(15, 6)
self.setCentralWidget(self.tableWidget)
for i in range(self.tableWidget.rowCount()):
for j in range(self.tableWidget.columnCount()):
it = QtWidgets.QTableWidgetItem("{}-{}".format(i, j))
self.tableWidget.setItem(i, j, it)
delegate = AlignDelegate(self.tableWidget)
self.tableWidget.setItemDelegateForColumn(2, delegate)
# for all columns:
# self.tableWidget.setItemDelegate(delegate)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
I'm using the QPushButton to load the UI. First -> Jumin -> Department -> next -> next I want to create the UI in order. The problem is that I can not load the third Department into the QMainwindow window. I do not know why
When you create a widget in QVBoxLayout, it changes the size of the widget according to the wallpaper like wxpython layout (wx.all). Can not change the position (move) and size (resize) by automatic centering?
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
def FirstUI(self):
self.btn1 = QPushButton('test1', self)
self.btn1.move(50, 50)
self.btn1.clicked.connect(self.btn1_click)
def JuminUI(self):
self.ju1 = QLineEdit('13')
self.btn2 = QPushButton('^^^^^^^^^^')
self.ju_text = QLabel('asd')
self.jumim_layout = QVBoxLayout()
self.jumim_layout.addWidget(self.ju_text)
self.jumim_layout.addWidget(self.ju1)
self.jumim_layout.addWidget(self.btn2)
self.centralWidget().setLayout(self.jumim_layout)
self.btn2.clicked.connect(self.btn2_click)
def DepartmentUI(self):
self.depart_layout = QVBoxLayout()
self.depart_layout.addWidget(QPushButton('sdfsdf'))
self.centralWidget().setLayout(self.depart_layout)
def btn1_click(self):
self.btn1.deleteLater()
self.JuminUI()
def btn2_click(self):
self.ju1.deleteLater()
self.btn2.deleteLater()
self.ju_text.deleteLater()
self.DepartmentUI()
if __name__ == "__main__":
app = QApplication(sys.argv)
fream = MainWindow()
fream.show()
app.exec_()
creating and removing widgets is almost always a bad idea, and your code falls into those bad ideas, it's always best to hide the widgets and for that you should use the QStackedWidget, what QStackedWidget does is just make a widget visible on all widgets that you have been assigned by changing the currentIndex.
import sys
from functools import partial
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.center_widget = QtWidgets.QStackedWidget()
self.setCentralWidget(self.center_widget)
self.FirstUI()
self.JuminUI()
self.DepartmentUI()
def FirstUI(self):
widget = QtWidgets.QWidget()
self.btn1 = QtWidgets.QPushButton('test1', widget)
self.btn1.move(50, 50)
self.center_widget.addWidget(widget)
self.btn1.clicked.connect(partial(self.center_widget.setCurrentIndex, 1))
def JuminUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
self.ju1 = QtWidgets.QLineEdit('13')
self.btn2 = QtWidgets.QPushButton('^^^^^^^^^^')
self.ju_text = QtWidgets.QLabel('asd')
lay.addWidget(self.ju_text)
lay.addWidget(self.ju1)
lay.addWidget(self.btn2)
self.center_widget.addWidget(widget)
self.btn2.clicked.connect(partial(self.center_widget.setCurrentIndex, 2))
def DepartmentUI(self):
widget = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(widget)
lay.addWidget(QtWidgets.QPushButton('sdfsdf'))
self.center_widget.addWidget(widget)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
fream = MainWindow()
fream.show()
sys.exit(app.exec_())
Basic Layout of the Code
Here, every time the user clicks on the PushButton 'Press me', a new CheckBox will be generated.
from PyQt5 import QtWidgets, QtGui, QtCore
count = 1
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.vlayout = QtWidgets.QVBoxLayout()
self.pushButton1 = QtWidgets.QPushButton("Press me", self)
self.pushButton1.clicked.connect(self.addCheckbox(count))
self.pushButton2 = QtWidgets.QPushButton("OK", self)
self.vlayout.addWidget(self.pushButton1)
self.vlayout.addWidget(self.pushButton2)
self.setLayout(self.vlayout)
def addCheckbox(self,count):
global count
self.vlayout.addWidget(str(count),QtWidgets.QCheckBox())
count = count +1
application = QtWidgets.QApplication(sys.argv)
window = Window()
window.setWindowTitle('Hello')
window.resize(250, 180)
window.show()
sys.exit(application.exec_())
What I wish to do?
Now you will have unique check boxes, each with a different number, I want to add further functionality.
Every time the user chooses specific checkboxes, I want to know which checkbox did the user click after he clicks the PushButton OK.
Eg: I click on checkbox 1 -> OK -> print 1 on the screen
How can I do this?
PS: We need to consider the possibility that the user never clicks Press me, so no ckeckboxes will be generated and straightaway clicks on OK
Just use a list to store the QCheckBox, and verify by iterating.
from PyQt5 import QtWidgets, QtGui, QtCore
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.vlayout = QtWidgets.QVBoxLayout(self)
self.pushButton1 = QtWidgets.QPushButton("Press me")
self.pushButton1.clicked.connect(self.addCheckbox)
self.pushButton2 = QtWidgets.QPushButton("OK")
self.pushButton2.clicked.connect(self.onClicked)
self.vlayout.addWidget(self.pushButton1)
self.vlayout.addWidget(self.pushButton2)
self.checkboxes = []
def addCheckbox(self):
checkbox = QtWidgets.QCheckBox()
self.checkboxes.append(checkbox)
self.vlayout.addWidget(checkbox)
def onClicked(self):
for i, checkbox in enumerate(self.checkboxes):
if checkbox.isChecked():
print("print {} on the screen".format(i))
if __name__ == '__main__':
import sys
application = QtWidgets.QApplication(sys.argv)
window = Window()
window.setWindowTitle('Hello')
window.resize(250, 180)
window.show()
sys.exit(application.exec_())
I have a Qmenu that I am creating by loading a list with Qsettings and I am trying to be able to remove items from the menu by loading the list in a QListQWidget and deleting the selected items. Currently I am able to delete the menu items in the list widget and it removes them from qsettings as well but I can't figure out how to remove the items from the menu without restarting. I have tried various things such as removeAction etc but haven't been able to figure it out.
Here is my code:
import functools
import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, \
QApplication, QAction, QMenu, QListWidgetItem, \
QListWidget, QGridLayout
class MainWindow(QWidget):
settings = QtCore.QSettings('test_org', 'my_app')
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QHBoxLayout()
self.menu_btn = QPushButton()
self.menu = QMenu()
self.add_menu = self.menu.addMenu("Menu")
self.menu_btn.setMenu(self.menu)
self.open_list_btn = QPushButton('open list')
self.load_items = self.settings.value('menu_items', [])
for item in self.load_items:
self.action = QAction(item[0], self)
self.action.setData(item)
self.add_menu.addAction(self.action)
self.action.triggered.connect(functools.partial(self.menu_clicked, self.action))
self.layout.addWidget(self.menu_btn)
self.layout.addWidget(self.open_list_btn)
self.setLayout(self.layout)
self.open_list_btn.clicked.connect(self.open_window)
def open_window(self):
self.create_menu_item = List()
self.create_menu_item.show()
def menu_clicked(self, item):
itmData = item.data()
print(itmData)
class List(QWidget):
settings = QtCore.QSettings('test_org', 'my_app')
def __init__(self, parent=None):
super(List, self).__init__(parent)
self.menu_items = self.settings.value('menu_items', [])
self.layout = QGridLayout()
self.list = QListWidget()
self.remove_btn = QPushButton('Remove')
self.layout.addWidget(self.list, 1, 1, 1, 1)
self.layout.addWidget(self.remove_btn, 2, 1, 1, 1)
self.setLayout(self.layout)
self.remove_btn.clicked.connect(self.remove_items)
for item in self.menu_items:
self.item = QListWidgetItem()
self.item.setText(str(item[0]))
self.list.addItem(self.item)
def remove_items(self):
self.menu_items = self.settings.value('menu_items', [])
del self.menu_items[self.list.currentRow()]
self.settings.setValue('menu_items', self.menu_items)
listItems = self.list.selectedItems()
if not listItems: return
for item in listItems:
self.list.takeItem(self.list.row(item))
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
Does anyone have any ideas?
EDIT:
This is the structure of the list in QSettings. I load the menu with this and I load the QlistWidget with this. I am trying to get the menu to remove the items as well when I remove them for the QListWidget.
mylist = ['item_name',['itemdata1', 'itemdata2', 'itemdata3'],
'item_name2',['itemdata1', 'itemdata2', 'itemdata3'],
'item_name3',['itemdata1', 'itemdata2', 'itemdata3']]
I think the data structure that you are using is incorrect because when I execute your code it generates twice as many QActions, the structure I propose is a dictionary where the keys are the name of the QAction and the value of the list of data:
{
'item0': ['itemdata00', 'itemdata01', 'itemdata02'],
'item1': ['itemdata10', 'itemdata11', 'itemdata12'],
...
}
To build the initial configuration use the following script:
create_settings.py
from PyQt5 import QtCore
if __name__ == '__main__':
settings = QtCore.QSettings('test_org', 'my_app')
d = {}
for i in range(5):
key = "item{}".format(i)
value = ["itemdata{}{}".format(i, j) for j in range(3)]
d[key] = value
settings.setValue('menu_items', d)
print(d)
settings.sync()
On the other hand I think that the widget that you want to handle the destruction of QActions should take over the corresponding QMenu as I show below:
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
layout = QtWidgets.QHBoxLayout(self)
menu_btn = QtWidgets.QPushButton()
open_list_btn = QtWidgets.QPushButton('open list')
layout.addWidget(menu_btn)
layout.addWidget(open_list_btn)
menu = QtWidgets.QMenu()
menu_btn.setMenu(menu)
self.menu_manager = MenuManager("menu_items", "Menu")
menu.addMenu(self.menu_manager.menu)
self.menu_manager.menu.triggered.connect(self.menu_clicked)
open_list_btn.clicked.connect(self.menu_manager.show)
def menu_clicked(self, action):
itmData = action.data()
print(itmData)
class MenuManager(QtWidgets.QWidget):
def __init__(self, key, menuname, parent=None):
super(MenuManager, self).__init__(parent)
self.settings = QtCore.QSettings('test_org', 'my_app')
self.key = key
self.layout = QtWidgets.QVBoxLayout(self)
self.listWidget = QtWidgets.QListWidget()
self.remove_btn = QtWidgets.QPushButton('Remove')
self.layout.addWidget(self.listWidget)
self.layout.addWidget(self.remove_btn)
self.remove_btn.clicked.connect(self.remove_items)
self.menu = QtWidgets.QMenu(menuname)
load_items = self.settings.value(self.key, [])
for name, itemdata in load_items.items():
action = QtWidgets.QAction(name, self.menu)
action.setData(itemdata)
self.menu.addAction(action)
item = QtWidgets.QListWidgetItem(name)
item.setData(QtCore.Qt.UserRole, action)
self.listWidget.addItem(item)
def remove_items(self):
for item in self.listWidget.selectedItems():
it = self.listWidget.takeItem(self.listWidget.row(item))
action = it.data(QtCore.Qt.UserRole)
self.menu.removeAction(action)
self.sync_data()
def sync_data(self):
save_items = {}
for i in range(self.listWidget.count()):
it = self.listWidget.item(i)
action = it.data(QtCore.Qt.UserRole)
save_items[it.text()] = action.data()
self.settings.setValue(self.key, save_items)
self.settings.sync()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I got it figured out. I am not sure of a better way but I did it using object names.
In the MainWindow I set objectNames to self.action using the first item of each list in the list of lists inside the for loop like this:
self.action.setObjectName(item[0])
Then I created this function in the MainWindow class:
def remove_menu_item(self, value):
self.add_menu.removeAction(self.findChild(QAction, value))
Then I added this:
w.remove_menu_item(item.text())
To the remove function in the List class to get the same first item in the list of lists which is now the objectName for the QActions.