How to select cells in QtableWidget from list of indexes - python

In a QtableWidget, I would like to store the selected cells while I query a database and return the previously selected cells back to being selected. My refresh of items on the QtableWidget clears the selection. The user can select non-contiguous ranges of cells.
I have no problem getting the selected cells before I refresh the data with QtableWidget.selectedIndexes().
I have tried looping through the list of indexes and using setCurrentIndex but that only leaves me with the last index. I have run out of ideas. How can I restore the selected ranges of cells based on the stored indexes?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from room_chart import *
from datetime import datetime, timedelta
class Guest_form(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self)
self.ui = Ui_rooms_chart()
self.ui.setupUi(self)
self.build_chart()
self.ui.book.clicked.connect(self.book)
def book(self):
self.indexes = self.ui.room_chart.selectedIndexes()
#Do stuff
self.build_chart()
#This has the right behaviour but only selects the last index
for x in range(len(self.indexes)):
self.ui.room_chart.setCurrentIndex(self.indexes[x])
self.ui.room_chart.setFocus()
def build_chart(self):
self.ui.room_chart.setRowCount(0)
self.ui.room_chart.setColumnCount(0)
col_labels = []
for x in range(8):
current_day = datetime.now() + timedelta(days=x)
col_labels.append(current_day.strftime('%a') + '\n' + current_day.strftime('%d/%m/%y'))
self.ui.room_chart.setColumnCount(len(col_labels))
self.ui.room_chart.setHorizontalHeaderLabels(col_labels)
row_labels = []
for x in range(8):
row_labels.append(str(x))
self.ui.room_chart.setRowCount(len(row_labels))
self.ui.room_chart.setVerticalHeaderLabels(row_labels)
self.button = QPushButton(self.ui.room_chart)
self.button.setText("Push me")
self.ui.room_chart.setCellWidget(0 , 0, self.button)
if __name__=="__main__":
app=QApplication(sys.argv)
myapp = Guest_form()
myapp.show()
sys.exit(app.exec_())

You have to use the select() method of QItemSelectionModel:
def book(self):
persistenIndex = map(QPersistentModelIndex, self.ui.room_chart.selectedIndexes())
#Do stuff
self.build_chart()
for pix in persistenIndex:
ix = QModelIndex(pix)
self.ui.room_chart.selectionModel().select(ix, QItemSelectionModel.Select)
self.ui.room_chart.setFocus()
Note: It converts the QModelIndex to QPersistentModelIndex to avoid problems since it is not known if build_chart() deletes, moves or performs any other action that changes the position of the items.

Related

Scrolling/Zooming within a Plotwidget

i am playing around with a Plotwidget and i cant figure out how i can find the min-max positions from my list when zooming in and out.
Lets say i have a list which has 1000 values. When im scrolling/zooming i can only figure out the viewbox range and not the positions i am in my list.
Example:
On init i am at x[0], x[1000]
Scrolling once im at x[?], x[?]
Is it possible to find out the min-max index (if i am not directly on an index , whats the closest one)? I also would like to know, if it is possible to change the zooming factor when scrolling and can i set the viewbox padding to 0? I only figured out that u can change this with setXRange(..., padding=0)
import sys
from random import randint
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt5.QtCore import Qt
import pyqtgraph as pg
class Win(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.graph = pg.PlotWidget()
self.setCentralWidget(self.graph)
self.graph.sigRangeChanged.connect(self.viewboxChanged)
x = list()
y = list()
pos = 0
for i in range(1000):
r = randint(0,5)
y.append(r)
x.append(pos)
pos +=r
self.graph.plot(x, y)
def viewboxChanged(self, view, range):
print(range)
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Win()
win.show()
sys.exit( app.exec_() )
Your question is more like how to interpret the values in your range variable. Note: Consider changing that variable name, because it can be confusing because there is a python's built-in-function called like that.
Your range variable has this structure:
[ [x_0, x_1], [y_0, y_1] ]
Where:
x_0 and x_1 are the minimum and maximum values that are visible in the x-axis.
y_0 and y_1 are the minimum and maximum values that are visible in the y-axis.
Now, what you want are the indexes, and that can be done using this answer. Finally, the implementation of all of the above in your code will look like this:
class Win(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.graph = pg.PlotWidget()
self.setCentralWidget(self.graph)
self.graph.sigRangeChanged.connect(self.viewboxChanged)
## Change the variables from local to an instance attribute
## to acces from the other functions
self.x = list()
self.y = list()
pos = 0
for i in range(1000):
r = randint(0,5)
self.y.append(r)
self.x.append(pos)
pos +=r
self.graph.plot(self.x, self.y)
def viewboxChanged(self, view, range_v):
## range_v[0] will return the list: [x_0, x_1]
x0 = range_v[0][0]
x1 = range_v[0][1]
x0_index = min(self.x, key = lambda x: abs(x-x0))
x1_index = min(self.x, key = lambda x: abs(x-x1))
print(x0_index, x1_index)
pass
There are comments inside explaining the changes I did.

Crashes when moving cellWidgets around in a TableWidget

I am writing a tool that allows me to track some tasks along a path of predifined stages, from something on a backlog, to ToDo, through WIP, Review and finally to done.
I created a custom widget, that will eventually be yellow, not unlike a postit note and perhaps with a bit of formatting it to give it a nice frame, etc... but stopped before getting far enough to make it look right because of this issue.
The idea is that each of these yellow Task widgets will have a stage they are at, and that I can select them in a Table Widget, and move them onto the next or previous stage, which will update taht objects stage, then refresh the TableWidget, read all the widget and where thay should be and set them in their new place.
So I have it kind of working to some degree (below), where I can move the tasks forward and they update location, but I noticed when I click the cells that the widget was previously in, print statement still says that the cell still has a widget there (which kind of makes sense, as code below isn't removing the previous one, but I'd expect to visually still see it). And I can move them forward and backwards, and the information on the tasks does update correctly, but the table won't refresh unless the task moves to a cell that never had a cellWidget in it. Test this by moving it backwards. It works, movnig forward visually does nothing, but moving again, does show up.
I tried clearing the TableWidget and rebuilding from scratch and that crashes. The main issue I am having is that with all these crashes, which is an issue in itself as it makes debugging very tough... When I try and clear the TableWidget (with .clear()) before repopulating, I get this.
Process finished with exit code -1073741819 (0xC0000005)
Same error code if I try removing the old cells by setting the Table Widget to 0 rows before adding the correct number of rows.
A known issue that is less important is when I select a cell without a widget and try and move it, gies me this, but don't worry too much about that fix, as it's known issue.
Process finished with exit code -1073740791 (0xC0000409)
Also tried cleaning up by iterating every cell and if it has a cell widget, remove cell widget before re-setting them to correct place and it still crashes. I'm out of ideas.
Task Widget
import sys
from PyQt5.QtWidgets import (QApplication, QTableWidget, QWidget, QFrame, QHBoxLayout, QLabel,
QPushButton,QVBoxLayout)
class Task(QWidget):
def __init__(self, ID, name, est):
super(Task, self).__init__()
# Creates a small widget that will be added to a table widget
self.ID = ID
self.name = name
self.est = est
# These cell widgets represent tasks. So each task has a particular 'stage' it is at
self.stage = 'ToDo'
self.stages = ['Backlog', 'ToDo', 'WIP', 'Review', 'Done']
self.objects_labels = {}
self.initUI()
def initUI(self):
# adds a bunch of labels to the widget
layout = QVBoxLayout()
frame = QFrame()
frame.setFrameShape(QFrame.StyledPanel)
frame.setStyleSheet('background-color: red')
frame.setLineWidth(2)
layout.addWidget(frame)
info = [self.ID, self.name, self.est]
for section in info:
self.objects_labels[section] = QLabel(str(section))
layout.addWidget(self.objects_labels[section])
self.setLayout(layout)
self.setStyleSheet('background-color: yellow')
def task_move(self, forward = True):
# The main widget will allow me to change the stage of a particular Task
# The idea is that I update the Table widget to show everything in the right place
# This function finds out what stage it is at and increments/decrements by one
index = self.stages.index(self.stage)
print(self.stages)
print(index)
if forward:
print('--->')
if self.stage == self.stages[-1]:
print('Already at the end of process')
return
self.stage = self.stages[index + 1]
else:
print('<---')
if self.stage == self.stages[0]:
print('Already at the start of process')
return
self.stage = self.stages[index - 1]
MainWidget
class MainWidget(QWidget):
def __init__(self):
super().__init__()
self.tasks = self.make_tasks()
self.init_ui()
self.update_tw()
def make_tasks(self):
# Create a few tasks
a = Task(0, 'Name_A', 44)
b = Task(0, 'Name_B', 22)
c = Task(0, 'Name_C', 66)
d = Task(0, 'Name_D', 90)
return [a, b, c, d]
def init_ui(self):
layout_main = QVBoxLayout()
self.tw = QTableWidget()
self.tw.cellClicked.connect(self.cell_clicked)
self.tw.horizontalHeader().setDefaultSectionSize(120)
self.tw.verticalHeader().setDefaultSectionSize(120)
layout_main.addWidget(self.tw)
layout_bottom_button_bar = QHBoxLayout()
self.btn_task_backward = QPushButton('<--- Task')
self.btn_task_backward.clicked.connect(lambda: self.move_task(forward=False))
self.btn_task_forward = QPushButton('Task --->')
self.btn_task_forward.clicked.connect(lambda: self.move_task())
for widget in [self.btn_task_backward, self.btn_task_forward]:
layout_bottom_button_bar.addWidget(widget)
layout_main.addLayout(layout_bottom_button_bar)
self.setLayout(layout_main)
self.setGeometry(300, 300, 800, 600)
self.setWindowTitle('MainWidget')
self.show()
#property
def tw_header(self):
return {'Backlog': 0, 'ToDo': 1, 'WIP': 2, 'Review': 3, 'Done': 4}
#property
def selected_indices(self):
return [(x.row(), x.column()) for x in self.tw.selectedIndexes()]
#property
def selected_widgets(self):
selected_widgets = [self.tw.cellWidget(x[0], x[1]) for x in self.selected_indices]
print(selected_widgets)
return selected_widgets
def move_task(self, forward=True):
# Crashes if you select a non-widget cell, but thats a known issue
# Moves the task forward or backward and then prompts to update the TableWidget
for object in self.selected_widgets:
object.task_move(forward=forward)
self.tw.clearSelection()
self.update_tw()
def cell_clicked(self, row, column):
if self.tw.cellWidget(row, column):
print(self.selected_indices)
print(self.selected_widgets)
else:
print('No Cell Widget here')
def update_tw(self):
#I wanted to clear the Table widget and rebuild, but this crashes
# self.tw.clear()
self.tw.setHorizontalHeaderLabels(self.tw_header.keys())
rows = len(self.tasks)
columns = len(self.tw_header)
self.tw.setRowCount(rows)
self.tw.setColumnCount(columns)
# Looks through each task, and then gets it's stage, and then adds the widget to the correct column
for index, object in enumerate(self.tasks):
column = self.tw_header[object.stage]
print('Setting stage {} for {}\n...to r={}, c={}\n***'.format(object.stage, object, index, column))
self.tw.setCellWidget(index, column, object)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWidget()
sys.exit(app.exec_())
From my previous experience, I always found using setCellWidget clunky, underperforming and buggy.
Most of the times my Widgets were lost or misplaced, while refreshing the table similarly to the way you are doing it.
In addition, I guess you would want to use this "Task Mover" on a larger scale, and from what I could see, setting separate Widgets inside QWidgetItems becomes quite slow when done on loads of items.
My suggestion would be to use style delegates, so that you can customize the look of your items to your liking, without having to deal with the setCellWidget stuff which is giving you problem.
Once you have your own delegate, and paint the items the way you want, you can just keep updating that item data and moving the items around the table by using "take" and "set".
I am not sure if this would be the best way of executing this specific task, but moving towards this direction would probably give you greater flexibility and customisation power in the long run.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TaskProperty():
properties = ["ID", "name", "est", "stage"]
count = 4
ID, Name, Est, Stage = [Qt.UserRole + x for x in range(count)]
STAGES = ['Backlog', 'ToDo', 'WIP', 'Review', 'Done']
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
self.tasks = self.make_tasks()
self.init_ui()
self.update_tw()
def make_tasks(self):
# Create a few tasks
a = Task(0, 'Name_A', 44)
b = Task(0, 'Name_B', 22)
c = Task(0, 'Name_C', 66)
d = Task(0, 'Name_D', 90)
return [a, b, c, d]
def init_ui(self):
layout_main = QVBoxLayout()
self.tw = QTableWidget()
# create and set the delegate to the TableWidget
self.delegate = TaskDelegate(self.tw )
self.tw.setItemDelegate(self.delegate)
self.tw.cellClicked.connect(self.cell_clicked)
self.tw.horizontalHeader().setDefaultSectionSize(120)
self.tw.verticalHeader().setDefaultSectionSize(120)
layout_main.addWidget(self.tw)
layout_bottom_button_bar = QHBoxLayout()
self.btn_task_backward = QPushButton('<--- Task')
self.btn_task_backward.clicked.connect(lambda: self.move_task(forward=False))
self.btn_task_forward = QPushButton('Task --->')
self.btn_task_forward.clicked.connect(lambda: self.move_task())
for widget in [self.btn_task_backward, self.btn_task_forward]:
layout_bottom_button_bar.addWidget(widget)
layout_main.addLayout(layout_bottom_button_bar)
self.setLayout(layout_main)
self.setGeometry(300, 300, 800, 600)
self.setWindowTitle('MainWidget')
self.show()
#property
def tw_header(self):
return {'Backlog': 0, 'ToDo': 1, 'WIP': 2, 'Review': 3, 'Done': 4}
#property
def selected_indices(self):
return [(x.row(), x.column()) for x in self.tw.selectedIndexes()]
def move_task(self, forward=True):
'''
To move the task to the next step, we iterate all the items selected.
If the task can be moved, we take the corresponding item from its current cell and move it to the destination.
:param forward:
:return:
'''
selected =self.tw.selectedItems()
for item in selected:
item.setSelected(False)
result = item.task_move(forward=forward)
if result:
next = 1 if forward else -1
row = item.row()
column = item.column()
moveItem = self.tw.takeItem(row, column)
self.tw.setItem(row, column + next, moveItem)
moveItem.setSelected(True)
def cell_clicked(self, row, column):
item = self.tw.item(row, column)
if not isinstance(item, TaskItem):
print "No Task Item Here"
def update_tw(self):
# I wanted to clear the Table widget and rebuild, but this crashes
# self.tw.clear()
self.tw.clear()
self.tw.setHorizontalHeaderLabels(self.tw_header.keys())
rows = len(self.tasks)
columns = len(self.tw_header)
self.tw.setRowCount(rows)
self.tw.setColumnCount(columns)
# Looks through each task, and then gets it's stage, and then adds the widget to the correct column
for row, object in enumerate(self.tasks):
# create items of our custom type only for the column that need to be filled.
# the other cells will be filled with null items.
column = STAGES.index(object.stage)
print('Setting stage {} for {}\n...to r={}, c={}\n***'.format(object.stage, object, row, column))
item = TaskItem(object)
self.tw.setItem(row, column, item)
class TaskDelegate(QStyledItemDelegate):
'''
This delegate take care of Drawing our cells the way we want it to be.
'''
def paint(self, painter, option, index):
'''
Override the Paint function to draw our own cell.
If the QTableWidgetItem does not have our Data stored in it, we do a default paint
:param painter:
:param option:
:param index:
:return:
'''
painter.save()
rect = option.rect
status = index.data(TaskProperty.Stage)
if status is None:
return super(TaskDelegate, self).paint(painter, option, index)
else:
id = STAGES.index(status)
pen = painter.pen()
pen.setBrush(Qt.black)
painter.setPen(pen)
if id == index.column():
rect.translate(3, 3)
newRect = QRect(rect.x(), rect.y(), rect.width() - 6, 20)
infos = [index.data(TaskProperty.ID), index.data(TaskProperty.Name), index.data(TaskProperty.Est)]
painter.setBrush(Qt.red)
painter.drawRect(newRect)
painter.setBrush(Qt.yellow)
for info in infos:
newRect.translate(0, 25)
painter.drawRect(newRect)
painter.drawText(newRect, Qt.AlignHCenter | Qt.AlignVCenter,
str(info))
class TaskItem(QTableWidgetItem):
'''
Subclass QTableWidgetItem.
Probably not needed, since we can set the property when we create the item instead of in the init,
and keep track of which item is attached to which task object using the Column Index of the table.
However, this can be useful if you want to attach more specific procedures to your items
'''
def __init__(self, task):
super(TaskItem, self).__init__()
self._task = task
self.setData(TaskProperty.ID, task.ID)
self.setData(TaskProperty.Name, task.name)
self.setData(TaskProperty.Est, task.est)
self.setData(TaskProperty.Stage, task.stage)
self.objects_labels = {}
def task_move(self, forward=True):
result = self._task.task_move(forward=forward)
self.setData(TaskProperty.Stage, self._task.stage)
return result
class Task(object):
'''
The Task class is now just an object, not a widget.
'''
def __init__(self, ID, name, est):
# Creates a small widget that will be added to a table widget
self.ID = ID
self.name = name
self.est = est
# These cell widgets represent tasks. So each task has a particular 'stage' it is at
self.stage = 'ToDo'
self.stages = ['Backlog', 'ToDo', 'WIP', 'Review', 'Done']
self.objects_labels = {}
def task_move(self, forward=True):
# The main widget will allow me to change the stage of a particular Task
# The idea is that I update the Table widget to show everything in the right place
# This function finds out what stage it is at and increments/decrements by one
index = self.stages.index(self.stage)
if forward:
print('--->')
if self.stage == self.stages[-1]:
#print('Already at the end of process')
return False
self.stage = self.stages[index + 1]
else:
print('<---')
if self.stage == self.stages[0]:
#print('Already at the start of process')
return False
self.stage = self.stages[index - 1]
return True
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWidget()
sys.exit(app.exec_())
It is not necessary to clean and create everything again, instead just move the widget for it we must know if it can be moved or not and for that task_move must indicate if the movement is valid or not. Considering the above, the solution is:
def task_move(self, forward=True):
# The main widget will allow me to change the stage of a particular Task
# The idea is that I update the Table widget to show everything in the right place
# This function finds out what stage it is at and increments/decrements by one
index = self.stages.index(self.stage)
print(self.stages)
print(index)
if forward:
print("---&gt")
if self.stage == self.stages[-1]:
print("Already at the end of process")
return False
self.stage = self.stages[index + 1]
else:
print("&lt---")
if self.stage == self.stages[0]:
print("Already at the start of process")
return False
self.stage = self.stages[index - 1]
return True
def move_task(self, forward=True):
for row, column in self.selected_indices:
widget = self.tw.cellWidget(row, column)
if isinstance(widget, Task) and widget.task_move(forward):
next_column = column + (1 if forward else -1)
# create new task widget
task = Task(widget.ID, widget.name, widget.est)
# remove all task widget
self.tw.removeCellWidget(row, column)
# move task widget
self.tw.setCellWidget(row, next_column, task)
self.tw.clearSelection()
The crashed is because when using clear you are also removing the Task widget so "self.tasks" has objects deleted from C++ that you should not use.

QSortFilterProxyModel Filter Issue

Using a QSortFilterProxyModel I need to filter the data based on the value of a specific column; however, the column may contain multiple values. I need to NOT show the row if the column contains a specific value. Do I need to subclass the QSortFilterProxyModel and override the filterAcceptsRow() method or should I use a setFilterRegExp?
The column can contain integers: 0,1,2,3. If the column contains a 2 then I need to not show the row.
If you store data as a QList or list you can easily subclass QSortFilterProxyModel to check this list in every row
Here is a simple example:
import sys
from PyQt5.QtCore import QSortFilterProxyModel,Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QListView, QApplication
app = QApplication(sys.argv)
list = QListView()
list.setWindowTitle('sample')
list.setMinimumSize(600, 400)
model = QStandardItemModel(list)
for i in range(1, 10):
# create each item with a list attached to the Qt::UserRole + 1
item = QStandardItem(str(i))
item.setData([i, i*2], Qt.UserRole + 1)
model.appendRow(item)
class MyFilterModel(QSortFilterProxyModel):
def filterAcceptsRow(self, source_row, source_parent):
i = self.sourceModel().index(source_row, 0, source_parent)
data = self.sourceModel().data(i, Qt.UserRole + 1)
print(data)
return 2 not in data
filter_model = MyFilterModel()
filter_model.setSourceModel(model)
list.setModel(filter_model)
list.show()
app.exec()
You can even customize your filter model to accepts a lambda filter function
According to your description, you should subclass the QSortFilterProxyModel and override the filterAcceptsRow(), here bellow is a simple proxy model for table model:
class DemoProxyModel(QSortFilterProxyModel):
"""
"""
def __init__(self, parent=None):
super().__init__(parent)
def filterAcceptsRow(self, sourceRow, sourceParent):
"""custom filtering"""
if not self.filterRegExp().isEmpty():
model_source = self.sourceModel()
if model_source is None:
return False
# pick up the column that you want to filter, e.g., column 3
index0 = model_source.index(sourceRow, 3, sourceParent)
data0 = model_source.data(index0)
# return self.filterRegExp() == data_type # equal match, the tight binding
return self.filterRegExp().indexIn(data0) >= 0 # include match, the loose binding
# return data_type.__contains__(self.filterRegExp()) # include match, the loose binding
# parent call for initial behaviour
return super().filterAcceptsRow(sourceRow, sourceParent)

When I use "currentIndex()" on

I've been stuck on a school assignment for hours because of this one problem. I need to check the index number (AS AN INTEGER) for the currently selected item in a ListWidget.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QListView
from PyQt5.QtGui import QPixmap
import Ui_countries
class MyForm(QMainWindow, Ui_countries.Ui_mainWindow):
# DON'T TOUCH!
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
self.setupUi(self)
# END DON'T TOUCH
# EVENT HOOKS HERE
self.countryList.itemClicked.connect(self.CountrySelected)
self.actionLoad_Countries.triggered.connect(self.LoadCountries)
self.sqUnits.currentIndexChanged.connect(self.SqUnits)
#self.updatePopulation.itemClicked.connect(self.updateMemory)
# RESPONSES HERE
# def updateMemory(self):
def LoadCountries(self):
global namelist
global populationlist
global arealist
namelist = []
populationlist = []
arealist = []
objFile = open("GUI/countries.txt")
for line in objFile:
line = line.replace("\n","")
lineList = line.split(",")
self.countryList.addItem(lineList[0])
namelist.append(lineList[0])
populationlist.append(lineList[1])
arealist.append(lineList[2])
objFile.close()
def CountrySelected(self,selectedCountryIndex):
QMessageBox.information(self,"Country changed!",selectedCountryIndex.text())
strCountryName = selectedCountryIndex.text()
strCountryName = strCountryName.replace(" ", "_")
imagePixmap = QPixmap(f"GUI/Flags/{strCountryName}")
strCountryName = strCountryName.replace("_", " ")
self.lblCountryName.setText(strCountryName)
self.flag.setPixmap(imagePixmap)
self.flag.resize(imagePixmap.width(),imagePixmap.height())
idx = self.countryList.currentIndex()
# self.populationbox.setText(populationlist[idx])
# selectedCountryIndex.index()
#^^^^^^^^^^^ useful code
print(int(strCountryName))
def SqUnits(self):
QMessageBox.information(self,"Event Received","Please convert between different units.")
if self.sqUnits.currentText() == "Sq. Miles":
self.totalareabox.setText("YAAAAA")
else:
self.totalareabox.setText("YEEEE")
# DON'T TOUCH
if __name__ == "__main__":
app = QApplication(sys.argv)
the_form = MyForm()
the_form.show()
sys.exit(app.exec_())
The area to focus on would be the CountrySelected function. Whenever I try to run idx = self.countryList.currentIndex(), instead of an integer, I get 'PyQt5.QtCore.QModelIndex object at 0x051A6470' if I try to print idx. My instructor wants us to use pyqt, and I have no experience with it, so I'm kinda freaking out!
Figured it out, classmate sent me this: index = self.listWidget.currentRow()
No idea why index didn't work, but alas, tis the nature of doing your assignments one hour before they're due.

How can I have a searchable Qlistview in pyqt

I have a QListView which displays a list of items using PyQt in Python. How can I get it to return a qlistview specific item when searched for?
For example, if I have the following Qlistview with 4 items, how can I get the item which contains text = dan? or bring it to the top of the list. Also, the search doesn't need to be completely specific, If I type "da" I'd like it to return dan or items that starts with "da" and possibly bring it to the top of the list
My Qlistview is defined as follows:
from PyQt4 import QtCore, QtGui
import os
import sys
class AppView(QtGui.QDialog):
def __init__(self, parent=None):
super(AppView, self).__init__(parent)
self.resize(400, 400)
self.ShowItemsList()
def ShowItemsList(self):
self.setWindowTitle("List")
buttonBox = QtGui.QDialogButtonBox(self)
buttonBox.setOrientation(QtCore.Qt.Horizontal)
buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
listview = QtGui.QListView(self)
verticalLayout = QtGui.QVBoxLayout(self)
verticalLayout.addWidget(listview)
verticalLayout.addWidget(buttonBox)
buttonBox.accepted.connect(self.close)
model = QtGui.QStandardItemModel(listview)
with open("names-list.txt") as input:
if input is not None:
item = input.readlines()
for line in item:
item = QtGui.QStandardItem(line)
item.setCheckable(True)
item.setCheckState(QtCore.Qt.PartiallyChecked)
model.appendRow(item)
listview.setModel(model)
listview.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
view = AppView()
view.show()
sys.exit(app.exec_())
I fixed it like this. I made my model an instance variable beginning with self so that I can access it from another function.
def searchItem(self):
search_string = self.searchEditText.text() # Created a QlineEdit to input search strings
items = self.model.findItems(search_string, QtCore.Qt.MatchStartsWith)
if len(items) > 0:
for item in items:
if search_string:
self.model.takeRow(item.row()) #take row of item
self.model.insertRow(0, item) # and bring it to the top
else:
print "not found"

Categories