PYQT4, ListView: How to get selected rows using QStandardItemModel - python

I want to use ListView in Pyqt4 to display some items with a checkbox in front of each item.
And, I want to get those selected items,but return value of self.ui.listView.selectedIndexes() is None, I really don’t know what to do to get what I want.
My codes are as following:
#coding=utf-8
from loadtsklist import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os
class MyLoadTskList(QDialog):
def __init__(self):
QDialog.__init__(self)
self.initTaskList()
def initTaskList(self):
global connectserver
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.btsure.clicked.connect(self.test)
tsklist = [u'北京',u'南京', u'海南', u'青岛', u'西安']
model = QStandardItemModel()
for task in tsklist:
print(task)
item = QStandardItem(QString(task))
check = Qt.Unchecked
item.setCheckState(check)
item.setCheckable(True)
model.appendRow(item)
self.ui.listView.setModel(model)
def test(self):
print len(self.ui.listView.selectedIndexes())
print "hello this is LoadTskList"
app = QApplication(sys.argv)
tsk = MyLoadTskList()
tsk.show()
app.exec_()
Could someone please tell me how to do? thanks in advance!

Firstly, your code for loading the list can be made more efficient, like this:
model = QStandardItemModel(self)
self.ui.listView.setModel(model)
for task in tsklist:
item = QStandardItem(task)
item.setCheckable(True)
model.appendRow(item)
And then to get the checked items, you need another loop, like this:
def test(self):
model = self.ui.listView.model()
for row in range(model.rowCount()):
item = model.item(row)
if item.checkState() == QtCore.Qt.Checked:
print('Row %d is checked' % row)

Related

How can I change the text of the edit line and add and subtract it?

I have two edit lines that when a letter from a dictionary is typed in it, it returns a number, and this number is added to the next edit line number and printed on a label. Now I want the previous number to be reduced and the new number to be added when I change one of the edit lines.
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QLabel
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.uic import loadUiType
oroh, _ = loadUiType("oroh.ui")
class OrOh(QWidget, oroh):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
self.lin_sh1.returnPressed.connect(self.shift1)
self.lin_sh2.returnPressed.connect(self.shift2)
def shift1(self):
shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
sh1 = self.lin_sh1.text()
if sh1 in shifts.keys():
a = shifts[sh1]
print(a)
else:
a = 0
print(a)
self.lbl_shifts.setText(f"{a}")
def shift2(self):
shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
a = self.lbl_shifts.text()
sh2 = self.lin_sh2.text()
if sh2 in shifts.keys():
b = shifts[sh2]
else:
b = 0
result = int(a) + int(b)
self.lbl_shifts.setText(f"{result}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = OrOh()
window.show()
app.exec_()
Since the keys can use more than one character, a possible solution is to use the textChanged(text) signal and call the function to compute the shifts afterwards.
The fact, though, is that the two existing functions are actually flawed: they almost do the same thing, but don't consider the two fields as they should.
I suggest you to merge those functions and use the textChanged to "reset" the value only when a valid key is entered. Note that this implies a logic that is not immediate to understand (even for the user) and might also cause some confusion
class OrOh(QWidget, oroh):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
self.shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
self.lin_sh1.returnPressed.connect(self.computeShift)
self.lin_sh2.returnPressed.connect(self.computeShift)
self.lin_sh1.textChanged.connect(self.checkText)
self.lin_sh2.textChanged.connect(self.checkText)
def checkText(self):
lin1 = self.lin_sh1.text()
lin2 = self.lin_sh2.text()
if self.lbl_shifts.text() and (not lin1 or not lin2):
self.computeShift()
def computeShift(self):
lin1 = self.lin_sh1.text()
lin2 = self.lin_sh2.text()
if ((lin1 in self.shifts and not lin2) or
(lin2 in self.shifts and not lin1) or
(lin1 in self.shifts and lin2 in self.shifts)):
shift = self.shifts.get(lin1, 0) + self.shifts.get(lin2, 0)
self.lbl_shifts.setText(str(shift))
I would suggest you to add some validation logic instead, or at least always use the textChanged signal. A simple solution would be to change the text color whenever the text entered is not in the dictionary, and eventually clear it when the focus changes:
class OrOh(QWidget, oroh):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
self.shifts = {"m":7, "s":7, "v":7, "t":7, "e":3, "le":5, "ld":12,
"n":12, "ln":14, "mn":19, "en":17, "me":10, "f":24}
self.lin_sh1.textChanged.connect(self.computeShift)
self.lin_sh2.textChanged.connect(self.computeShift)
self.lin_sh1.editingFinished.connect(lambda: self.checkField(self.lin_sh1))
self.lin_sh2.editingFinished.connect(lambda: self.checkField(self.lin_sh2))
def checkField(self, field):
if not field.text() in self.shifts:
field.setText('')
def computeShift(self):
shift = 0
for field in (self.lin_sh1, self.lin_sh2):
value = self.shifts.get(field.text(), 0)
if field.text() and not value:
field.setStyleSheet('color: red;')
else:
field.setStyleSheet('')
shift += self.shifts.get(field.text(), 0)
self.lbl_shifts.setText(str(shift))
Another possibility would be to use an editable QComboBox (with the insert policy set to NoInsert) and check the shift only when the inserted text is in the list.

How to connect QTreeWidget and QStackedWidget in PyQt4?

I'm sorry but just a beginner of Python.
I just want to change index of QStackedWidget by the item click of QTreeWidget. I searched for the tutorials of SIGNAL and SLOT online, but just cannot solve the problem.
The parameters in QTreeWidget signal and QStackedWidget slot are not fitted.
self.connect(qtree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), stack, QtCore.SLOT("setCurrentIndex(int)"))
And I tried this:
qtree.itemClicked.connect(stack.setCurrentIndex)
It just showed the error:
TypeError: setCurrentIndex(self, int): argument 1 has unexpected type 'QTreeWidgetItem'
I think there may be a method, but I cannot find on the network.
Like this:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class StockDialog(QDialog):
def __init__(self,parent=None):
super(StockDialog,self).__init__(parent)
mainSplitter=QSplitter(Qt.Horizontal)
treewidget = QTreeWidget(mainSplitter)
treewidget.setHeaderLabels(["Tree"])
treeroot = QTreeWidgetItem(treewidget, ["Stack"])
treeitem1 = QTreeWidgetItem(["WorkSpace"])
treeitem2 = QTreeWidgetItem(["About"])
treeroot.addChild(treeitem1)
treeroot.addChild(treeitem2)
stack=QStackedWidget(mainSplitter)
stack.setFrameStyle(QFrame.Panel|QFrame.Raised)
stackworkspace=StackWorkSpace()
stackabout=StackAbout()
stack.addWidget(stackworkspace)
stack.addWidget(stackabout)
closePushButton=QPushButton(self.tr("Close"))
self.connect(treewidget,
SIGNAL("itemClicked(int)"),
stack,SLOT("setCurrentIndex(int)"))
self.connect(closePushButton,
SIGNAL("clicked()"),
self,SLOT("close()"))
layout=QVBoxLayout(self)
layout.addWidget(mainSplitter)
layout.addWidget(closePushButton)
self.setLayout(layout)
class StackWorkSpace(QWidget):
def __init__(self,parent=None):
super(StackWorkSpace,self).__init__(parent)
widget1=QTextEdit(self.tr("WorkSpace"))
widget2=QTextEdit(self.tr("WorkSpace"))
layout=QGridLayout(self)
layout.addWidget(widget1,0,0)
layout.addWidget(widget2,0,1)
class StackAbout(QDialog):
def __init__(self,parent=None):
super(StackAbout,self).__init__(parent)
self.setStyleSheet("background: red")
app=QApplication(sys.argv)
main=StockDialog()
main.show()
app.exec_()
When change the QTreeWidget to the QListWidget in StockDialog class, it works.
class StockDialog(QDialog):
def __init__(self,parent=None):
super(StockDialog,self).__init__(parent)
mainSplitter=QSplitter(Qt.Horizontal)
listwidget=QListWidget(mainSplitter)
listwidget.insertItem(0,self.tr("WorkSpace"))
listwidget.insertItem(1,self.tr("About"))
stack=QStackedWidget(mainSplitter)
stack.setFrameStyle(QFrame.Panel|QFrame.Raised)
stackworkspace=StackWorkSpace()
stackabout=StackAbout()
stack.addWidget(stackworkspace)
stack.addWidget(stackabout)
closePushButton=QPushButton(self.tr("Close"))
self.connect(listwidget,
SIGNAL("currentRowChanged(int)"),
stack,SLOT("setCurrentIndex(int)"))
self.connect(closePushButton,
SIGNAL("clicked()"),
self,SLOT("close()"))
layout=QVBoxLayout(self)
layout.addWidget(mainSplitter)
layout.addWidget(closePushButton)
self.setLayout(layout)
Now, I want to do this with QTreeWidget, how can I do?
The strategy to solve this problem is to save the index information associated with each widget in the QTreeWidgetItem. QTreeWidgetItem has the setData() method that allows us to save information in the item and in this case we will save the index. The index is returned every time you add a widget to QStackedWidget through addWidget(), so in summary we will do the following:
treeitem1.setData(0, Qt.UserRole, stack.addWidget(stackworkspace))
treeitem2.setData(0, Qt.UserRole, stack.addWidget(stackabout))
After connecting the itemClicked signal of QTreeWidget, this returns the column and the item pressed, with this information we obtain the QStackedWidget index for it we recover the data saved through the function data():
treewidget.itemClicked.connect(lambda item, column: stack.setCurrentIndex(item.data(column, Qt.UserRole))
if item.data(column, Qt.UserRole) is not None else None)
The necessary code can be found in the following section:
class StockDialog(QDialog):
def __init__(self, parent=None):
super(StockDialog, self).__init__(parent)
mainSplitter = QSplitter(Qt.Horizontal)
treewidget = QTreeWidget(mainSplitter)
treewidget.setHeaderLabels(["Tree"])
treeroot = QTreeWidgetItem(treewidget, ["Stack"])
treeitem1 = QTreeWidgetItem(["WorkSpace"])
treeitem2 = QTreeWidgetItem(["About"])
treeroot.addChild(treeitem1)
treeroot.addChild(treeitem2)
stack = QStackedWidget(mainSplitter)
stack.setFrameStyle(QFrame.Panel | QFrame.Raised)
stackworkspace = StackWorkSpace()
stackabout = StackAbout()
treeitem1.setData(0, Qt.UserRole, stack.addWidget(stackworkspace))
treeitem2.setData(0, Qt.UserRole, stack.addWidget(stackabout))
closePushButton = QPushButton(self.tr("Close"))
treewidget.itemClicked.connect(lambda item, column: stack.setCurrentIndex(item.data(column, Qt.UserRole))
if item.data(column, Qt.UserRole) is not None else None)
layout = QVBoxLayout(self)
layout.addWidget(mainSplitter)
layout.addWidget(closePushButton)
self.setLayout(layout)

PyQt: QTableView + QSqlTableModel - Copy and Paste All Selected Rows or Columns into Notepad or Excel

To start, I've already looked at ekhumoro's code on a nearly similar subject Here. However, when I attempt to implement this code, I am getting a different result. Instead of copying and pasting all the I selected, it only copies the first cell of the selected row or rows respectively. I need users to be able to select multiple rows or columns and paste those values in either excel or notepad. Any ideas?
GUI:
Code:
from PyQt4 import QtCore, QtGui, QtSql
import sys
import sqlite3
import time
import csv
import Search # This file holds our MainWindow and all design related things
# it also keeps events etc that we defined in Qt Designer
import os
try:
from PyQt4.QtCore import QString
except ImportError:
QString = str
class TableEditor(QtGui.QMainWindow, Search.Search_MainWindow):
def __init__(self, tableName, parent=None):
# Explaining super is out of the scope of this article
# So please google it if you're not familar with it
# Simple reason why we use it here is that it allows us to
# access variables, methods etc in the design.py file
super(self.__class__, self).__init__()
self.setupUi(self) # This is defined in design.py file automatically
# It sets up layout and widgets that are defined
self.model = QtSql.QSqlTableModel(self)
self.model.setTable('CAUTI')
self.model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
self.model.select()
self.model.setHeaderData(0, QtCore.Qt.Horizontal, "MRN")
self.model.setHeaderData(1, QtCore.Qt.Horizontal, "Last Name")
self.model.setHeaderData(2, QtCore.Qt.Horizontal, "First Name")
self.model.setHeaderData(3, QtCore.Qt.Horizontal, "Date of Event")
self.model.setHeaderData(4, QtCore.Qt.Horizontal, "Facility")
self.model.setHeaderData(5, QtCore.Qt.Horizontal, "Unit")
self.model.setHeaderData(6, QtCore.Qt.Horizontal, "User")
#self.tableView.verticalHeader().setVisible(False)
self.tableView.setModel(self.model)
self.setWindowTitle("HAI Table")
self.tableView.setColumnWidth(0,100)
self.tableView.setColumnWidth(1,100)
self.tableView.setColumnWidth(2,100)
self.tableView.setColumnWidth(3,100)
self.tableView.setColumnWidth(4,100)
self.tableView.setColumnWidth(5,100)
self.tableView.setColumnWidth(6,83)
self.tableView.setSortingEnabled(True)
self.tableView.setDropIndicatorShown(True)
self.tableView.setAcceptDrops(True)
self.tableView.setDragEnabled(True)
self.tableView.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.horizontalHeader().setMovable(True)
self.tableView.horizontalHeader().setDragEnabled(True)
self.clip = QtGui.QApplication.clipboard()
## Note: ** When I unblock this line of code, I get an error.
#self.tableView.installEventFilters(self)
self.tableView.horizontalHeader().setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.verticalHeader().setMovable(True)
self.tableView.verticalHeader().setDragEnabled(True)
self.tableView.verticalHeader().setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.tableView.setSelectionBehavior(QtGui.QTableView.SelectRows)
self.submitButton.clicked.connect(self.submit)
self.revertButton.clicked.connect(self.model.revertAll)
self.quitButton.clicked.connect(self.close)
self.tableView.horizontalHeader().setSortIndicatorShown(True)
self.tableView.horizontalHeader().setClickable(True)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.KeyPress and
event.matches(QtGui.QKeySequence.Copy)):
self.copySelection()
return True
return super(Window, self).eventFilter(source, event)
def copySelection(self):
selection = self.tableView.selectedIndexes()
if selection:
rows = sorted(index.row() for index in selection)
columns = sorted(index.column() for index in selection)
rowcount = rows[-1] - rows[0] + 1
colcount = columns[-1] - columns[0] + 1
table = [[''] * colcount for _ in range(rowcount)]
for index in selection:
row = index.row() - rows[0]
column = index.column() - columns[0]
table[row][column] = index.data()
stream = io.StringIO()
csv.writer(stream).writerows(table)
QtGui.qApp.clipboard().setText(stream.getvalue())
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDropEnabled
def main():
app = QtGui.QApplication(sys.argv)
#app.setStyle( "Plastique" )
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('HAI.db')
editor = TableEditor('CAUTI')
editor.show()
app.exec_()
if __name__ == '__main__':
main()
Your implementation is broken in several ways, so the code you copied never gets executed. As a result, the table's built-in copy function is used, which does not handle multiple selected items.
My original code for copying multiple table items is here. I think you should be able to get your example working by changing the following lines:
from PyQt4 import QtCore, QtGui, QtSql
# fix imports
import sys, io
...
class TableEditor(QtGui.QMainWindow, Search.Search_MainWindow):
def __init__(self, tableName, parent=None):
# do not ever use self.__class__ here
super(TableEditor, self).__init__()
...
# install event-filter properly
self.tableView.installEventFilter(self)
def eventFilter(self, source, event):
...
# call super properly
return super(TableEditor, self).eventFilter(source, event)
Obviously, I cannot run your actual example code, so there may be other errors that I haven't spotted.
This is much faster :
def copySelection(self):
clipboardString = StringIO()
selectedIndexes = self.ui.tableView.selectedIndexes()
if selectedIndexes:
countList = len(selectedIndexes)
for r in range(countList):
current = selectedIndexes[r]
displayText = current.data(QtCore.Qt.DisplayRole)
if r+1 < countList:
next_ = selectedIndexes[r+1]
if next_.row() != current.row():
displayText += ("\n")
else:
displayText += ("\t")
clipboardString.write(displayText)
pyperclip.copy(clipboardString.getvalue())

PyQt Get specific value in ListWidget

I am new to python and pyqt/pyside ...
i make customwidget class consist of 2 label (title & desc) which is example instance to add to Listwidget later ...
here is the complete clean code (pyside maya)
import PySide.QtCore as qc
import PySide.QtGui as qg
class CustomQWidget (qg.QWidget):
def __init__ (self, parent = None):
super(CustomQWidget, self).__init__(parent)
self.textQVBoxLayout = qg.QVBoxLayout()
self.titleLabel = qg.QLabel()
self.description = qg.QLabel()
self.textQVBoxLayout.addWidget(self.titleLabel)
self.textQVBoxLayout.addWidget(self.description)
self.setLayout(self.textQVBoxLayout)
def setTitle (self, text):
self.titleLabel.setText(text)
def setDescription (self, text):
self.description.setText(text)
class example_ui(qg.QDialog):
def __init__(self):
qg.QDialog.__init__(self)
self.myQListWidget = qg.QListWidget(self)
self.myQListWidget.currentItemChanged.connect(self.getTitleValue)
self.myQListWidget.setGeometry(qc.QRect(0,0,200,300))
# make instance customwidget item (just one)------
instance_1 = CustomQWidget()
instance_1.setTitle('First title')
instance_1.setDescription('this is a sample desc')
myQListWidgetItem = qg.QListWidgetItem(self.myQListWidget)
myQListWidgetItem.setSizeHint(instance_1.sizeHint())
self.myQListWidget.addItem(myQListWidgetItem)
self.myQListWidget.setItemWidget(myQListWidgetItem, instance_1)
def getTitleValue(self,val):
# i make assume something like below but didnt work
# print (self.myQListWidget.currentItem.titleLabel.text()
return 0
dialog = example_ui()
dialog.show()
now at getTitleValue function how do i get Title and desc value when i change selection ?
You should remember that the list items and corresponding widgets are not the same. Luckily, QListWidget tracks them and gives you access to the displayed widget if you provide the list item:
class example_ui(qg.QDialog):
def getTitleValue(self,val):
# parameter val is actually the same as self.myQListWidget.currentItem
selected_widget = self.myQListWidget.itemWidget(val)
print selected_widget.titleLabel.text()
return 0
Side note: I had to add a main loop in order for the app to be executed at all:
import sys # to give Qt access to parameters
# ... class definitions etc. ...
app = qg.QApplication(sys.argv)
dialog = example_ui()
dialog.show()
exec_status = app.exec_() # main loop

Accessing property of dynamically created QStandardItems in PyQt5

I'm having a problem determining whether or not the checkboxes that are dynamically created have been checked or unchecked by the user in a simple GUI I've created.
I've adapted the relevant code and pasted it below. Although it may be easy to just create and name 4 QStandardItems, I'm dealing with many lists containing many different items that change quite a lot, so it isn't really feasible to create them myself.
Any help finding out how to access these properties would be much appreciated.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class Splash(QWidget):
def __init__(self):
super().__init__()
# imagine this is a very long list...
self.seasons = ['summer','autumn','winter','spring']
self.initUI()
def initUI(self):
layout = QVBoxLayout()
list = QListView()
model = QStandardItemModel()
list.setModel(model)
printbtn = QPushButton('print values')
printbtn.clicked.connect(self.print_action)
for season in self.seasons:
item = QStandardItem(season)
item.setCheckable(True)
model.appendRow(item)
model.dataChanged.connect(lambda: self.print_action(item.text()))
layout.addWidget(printbtn)
layout.addWidget(list)
self.setLayout(layout)
self.show()
def print_action(self, item):
print('changed', item)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = Splash()
sys.exit(app.exec_())
In short - I can detect when an item has been checked using model.dataChanged and connecting that to a function, but it cannot differentiate between the seasons.
If you keep a reference to the list (or the model), you can search for the items by their text, and then get their check-state:
def print_action(self):
model = self.list.model()
for text in 'summer', 'autumn', 'winter', 'spring':
items = model.findItems(text)
if items:
checked = items[0].checkState() == Qt.Checked
print('%s = %s' % (text, checked))
It seems you want to get notified when the checkState of a item has been changed.
In my opinion, there are possible two ways.
First way, QModel will emit "dataChanged" to refresh the view, so you can connect the signal which means the checkState of a item might be changed.
model.dataChanged.connect(self.test)
def test(self):
pass
Second way, use a timer to notify yourself and you check it by yourselves.
timer = QTimer()
timer.timeout.connect(self.test)
timer.start(1000)

Categories

Resources