I have the custom item delegate set for one column for QTableView. In some cases I need to remove it (i.e. set default item delegate). But it seems that QT does not allow this. The old delegate is used even after setting the new one.
According to QT documentation for QItemDelegate all handling should be done in the same delegate, but this may bring to performance issues. Is there any way to remove/reset to default the item delegate for QTableView.
I tried it in PyQt5 (sorry, im not able to write C++). I could set the standard itemGelegate to the view and then set a custom itemDelegate to one column. By using the „clicked“-signal i could replace the custom delegate by the standard itemDelegate for this column and vice versa.
This is my code, perhaps it helps:
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
class MyDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self):
QtWidgets.QStyledItemDelegate.__init__(self)
self.AlignmentFlag = QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter
self.abstand = 2
def paint(self, painter, item, index):
rahmen = item.rect.adjusted(self.abstand, self.abstand, -self.abstand, -self.abstand)
eintrag = index.data()
painter.save()
painter.drawText(rahmen,self.AlignmentFlag, eintrag)
painter.restore()
class MyModel(QtCore.QAbstractTableModel):
def __init__(self,):
QtCore.QAbstractTableModel.__init__(self)
self.items = [['a0','a1','a2','a3','a4'],['b0','b1','b2','b3','b4'],['c0','c1','c2','c3','c4']]
def columnCount(self,items):
cc = len(self.items[0])
return cc
def rowCount(self,items):
rc = len(self.items)
return rc
def data(self, index, role=2):
return self.items[index.row()][index.column()]
class MyWidget(QtWidgets.QTableView):
def __init__(self):
QtWidgets.QTableView.__init__(self)
self.setModel(MyModel())
self.setGeometry(200,200,530,120)
self.delegate_1 = MyDelegate()
self.delegate_2 = QtWidgets.QStyledItemDelegate()
self.setItemDelegate(self.delegate_2)
self.setItemDelegateForColumn(0,self.delegate_1)
self.clicked.connect(self.changeDelegate)
def changeDelegate(self,index):
if index.column() == 0:
delegate_new = self.delegate_2 if self.itemDelegateForColumn(index.column()) == self.delegate_1 else self.delegate_1
self.setItemDelegateForColumn(index.column(),delegate_new)
else:
pass
app = QtWidgets.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
Related
In the tkinter app I'm building (win10 python 3.8) each time I open a new file I get a new list that I distribute to textboxes (Ok), combobox(ok), etc. When I open the first list checkbox loop builds ok, next file called checkboxes don't change. I can't update checkboxes, I mean, remove the previous list and insert another one. In the example I used to buttons (in app askopenfilename), lists build one bellow other. I need one replacing the other. I believe I need to use grid.clear() or destroy, but how? Thanks in advance.
from tkinter import *
root = Tk()
root.geometry('400x400')
my_friends = ['Donald', 'Daisy', 'Uncle Scrooge', 'Ze Carioca']
my_heroes = ['Captain America', 'Hulk', 'Spider Man', 'Black Widow',
'Wanda Maximoff', 'Vision', 'Winter Soldier']
what_list = ' '
def list_my_friends():
global what_list
what_list = my_friends
create_cbox()
def list_my_heroes():
global what_list
what_list = my_heroes
create_cbox()
def create_cbox():
for index, friend in enumerate(what_list):
current_var = tk.StringVar()
current_box = tk.Checkbutton(root, text= friend,
variable = current_var,
onvalue = friend,
offvalue = '')
current_box.pack()
button1= tk.Button(root, text = "my_friends",command=lambda:list_my_friends()).pack()
button2= tk.Button(root, text = "my_heroes",command=lambda:list_my_heroes()).pack()
root.mainloop()
You can put those checkbuttons in a frame, then it is more easier to remove old checkbuttons before populating new checkbuttons:
def create_cbox():
# remove existing checkbuttons
for w in container.winfo_children():
w.destroy()
# populate new checkbuttons
for friend in what_list:
current_var = tk.StringVar()
# use container as the parent
current_box = tk.Checkbutton(container, text=friend,
variable=current_var,
onvalue=friend,
offvalue='')
current_box.pack(anchor='w')
tk.Button(root, text="my_friends", command=list_my_friends).pack()
tk.Button(root, text="my_heroes", command=list_my_heroes).pack()
# frame to hold the checkbuttons
container = tk.Frame(root)
container.pack(padx=10, pady=10)
worked with little adjustments as I'm working in class:
class PageEight(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent) # parent is Frame
self.controller = controller
# to adequate to widgets outside the self.container_pg8:
self.rowspan_list = len(what_list)
#container:
self.container_pg8 = tk.Frame(self)
self.container_pg8.grid(row=2,
rowspan = self.rowspan_list,
column=1,)
I'm having trouble when modifying the source model of a QSortFilterProxyModel from another thread, while the proxy model is connected to a view. The proxy model seems to add "ghost" items to the model being displayed, blank items that cannot be interacted with (apparently)
This example code demonstrates the problem I'm experiencing, reduced down to a bare-basic example where there's a view for each of the proxy model and the source model:
from PySide2 import QtCore, QtGui, QtWidgets
import random
WordList = ["apples", "pears", "down", "stairs", "mother", "hubord", "isn't", "cupboard", "lemon", "tango", "apricot", "nuke"]
class Searcher(QtCore.QThread):
def __init__(self, model):
super(Searcher, self).__init__()
self.model = model
def run(self):
populateModel(self.model)
def populateModel(model):
for i in range(5):
item1 = QtGui.QStandardItem()
model.invisibleRootItem().appendRow(item1)
majorWord = random.choice(WordList)
item1.setData(majorWord, QtCore.Qt.DisplayRole)
for i in range(random.randint(2, 8)):
item2 = QtGui.QStandardItem()
item1.appendRow(item2)
item2.setData(os.path.join(majorWord, random.choice(WordList)), QtCore.Qt.DisplayRole)
if __name__ == '__main__':
tv = QtWidgets.QTreeView()
tv.setWindowTitle("Source Model, Threaded")
tv.show()
tv2 = QtWidgets.QTreeView()
tv2.setWindowTitle("Proxy Model, Threaded")
tv2.show()
tv3 = QtWidgets.QTreeView()
tv3.setWindowTitle("Proxy Model 2, No Threading")
tv3.show()
sourceModel = QtGui.QStandardItemModel()
tv.setModel(sourceModel)
proxyModel = QtCore.QSortFilterProxyModel()
proxyModel.setDynamicSortFilter(True)
proxyModel.setSourceModel(sourceModel)
tv2.setModel(proxyModel)
s = Searcher(sourceModel)
s.start()
sm3 = QtGui.QStandardItemModel()
pm3 = QtCore.QSortFilterProxyModel()
pm3.setSourceModel(sm3)
proxyModel.setDynamicSortFilter(True)
tv3.setModel(pm3)
populateModel(sm3)
I thought that maybe the setDynamicSortFilter property of the proxy model could be useful, but it didn't seem to have any effect.
Any help or pointers appreciated!
I have a list of buttons which is shown to the user according to a particular number which the user enters.For e.g. If the user enters 2 then only 2 buttons will be shown.
The code that does this is here:
def set(self):
global seismicAttributeCount,lineEditlist
seismicAttributeCount=int(self.ui.lineEdit_23.text())
mygroupbox = QtGui.QGroupBox()
myform = QtGui.QFormLayout()
labellist = []
buttonList= []
for i in range(seismicAttributeCount):
lineEditlist.append(QtGui.QLineEdit())
buttonList.append(QtGui.QPushButton('Browse Attribute %i'%(i+1)))
myform.addRow(lineEditlist[i],buttonList[i])
mygroupbox.setLayout(myform)
self.ui.scrollArea_12.setWidget(mygroupbox)
self.ui.scrollArea_12.setWidgetResizable(True)
for i in range(seismicAttributeCount):
if buttonList[i].clicked.connect():
print i
I want to get the index of the button clicked. Any help would be appreciated.
You probably want to look into functools.partial. This allows you to connect an event with a method and a particular input. I've made here a minimal example of a GUI that does what you want
from PyQt4 import QtGui, QtCore
import sys
import functools
class test(QtGui.QWidget):
def __init__(self,parent=None):
self.widget=QtGui.QWidget.__init__(self, parent)
# Button to add buttons
self.btnAdd = QtGui.QPushButton('Add')
self.btnAdd.connect(self.btnAdd, QtCore.SIGNAL('clicked()'), self.btnAddPressed)
# Line edit for number of buttons
self.qleN = QtGui.QLineEdit(str(0))
# List to keep track of buttons
self.buttons=[]
# Layout
self.hbox = QtGui.QHBoxLayout()
self.hbox.addWidget(self.btnAdd)
self.hbox.addWidget(self.qleN)
self.setLayout(self.hbox)
self.show()
def btnAddPressed(self):
"""Adds number of buttons."""
# Get number of buttons to add
n=int(self.qleN.text())
self.buttons=[]
for i in range(n):
# Create new button
newBtn = QtGui.QPushButton(str(i))
self.buttons.append(newBtn)
# Connect
newBtn.clicked.connect(functools.partial(self.btnPressed,i))
self.hbox.addWidget(newBtn)
def btnPressed(self,idx):
"""Returns idx of btn."""
print idx
return idx
def main():
#Creating application
app = QtGui.QApplication(sys.argv)
main_win = test()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Note how I connect the button with a functools call and the index of the button.
By the way, next time, please make the effort and format your question properly.
I'm using Python 2.7 with PyQt 4.0.
I'm trying to make a QGraphicsRectItem move 10 px up in a animation. I have read the documentation and several tutorials but I can't get it to work. What is wrong with my code?
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import random
class TestWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene)
self.button1 = QPushButton("Do test")
self.button2 = QPushButton("Move forward 10")
layout = QVBoxLayout()
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(self.button1)
buttonLayout.addWidget(self.button2)
buttonLayout.addStretch()
layout.addWidget(self.view)
layout.addLayout(buttonLayout)
self.setLayout(layout)
self.button1.clicked.connect(self.do_test)
self.button2.clicked.connect(self.move_forward)
def do_test(self):
self.turtle = self.scene.addRect(0,0,10,20)
def move_forward(self):
animation = QGraphicsItemAnimation()
timeline = QTimeLine(1000)
timeline.setFrameRange(0,100)
animation.setTimeLine(timeline)
animation.setItem(self.turtle)
animation.setPosAt(1.0, QPointF(self.turtle.x(),self.turtle.y()+10))
timeline.start()
Thanks for the help!
The reason why your example doesn't work, is that you are not keeping a reference to the QGraphicsItemAnimation created in the move_forward method, and so it gets garbage-collected before it has a chance to do anything.
I would suggest you create the animation in __init__ so that you can access it later as an instance attribute:
def __init__(self, parent=None):
...
self.animation = QGraphicsItemAnimation()
def move_forward(self):
timeline = QTimeLine(1000)
timeline.setFrameRange(0, 100)
self.animation.setTimeLine(timeline)
self.animation.setItem(self.turtle)
self.animation.setPosAt(
1.0, QPointF(self.turtle.x(), self.turtle.y() + 10))
timeline.start()
try this small change (in function move_forward).
replace
animation = QGraphicsItemAnimation()
with
animation = QGraphicsItemAnimation(self)
that changes the behaviour for me.
In my application I want to use QGraphicsItemGroup for grouping items into one item.
I played with it a little and not sure using it because when I want to catch events, events are merged together but I want to handle specific event with specific child.
How can I achieve this?
You need to call QGraphicsItemGroup::setHandlesChildEvents(false). This stops the QGraphicsItemGroup trying to handle the event, and lets the child QGraphicsItems handle them instead.
I think that's the point of the QGraphicsItemGroup. Judging from the documentation, this is meant to simplify moving and transforming multiple items at once e.g. imagine the following case: a user draws a selection rectangle around several items in an application because he wants to move all of them. Perhaps what you want more is to create a hierarchy of items, e.g. have one parent item with several child items. This way you'll get the individual events for each item. This can be accomplished by calling QGraphicsItem::setParentItem();
The question doesn't specify which version of Qt is concerned and there is a correct answer for Qt4. Here is an answer for Qt5 (it works for PyQt5 and I guess it'll work also in C++). The solution was to reimplement sceneEvent, reimplementing a specialized event-catcher such as contextMenuEvent was not sufficient.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
class GraphicsItem(QtWidgets.QGraphicsItem):
def __init__(self,
rect: QtCore.QRectF,
name: str,
parent: QtWidgets.QGraphicsItem = None):
super().__init__(parent)
self._name = name
self._rect = rect
def boundingRect(self):
return self._rect
def paint(self,
painter: QtGui.QPainter,
option: QtWidgets.QStyleOptionGraphicsItem,
widget: QtWidgets.QWidget = None):
painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
painter.setBrush(QtGui.QBrush(QtCore.Qt.red))
painter.drawRect(self._rect)
def sceneEvent(self, event: QtCore.QEvent):
if (event.type() == QtCore.QEvent.GraphicsSceneContextMenu):
self.contextMenuEvent(event)
event.accept()
return True
def contextMenuEvent(self, event: QtWidgets.QGraphicsSceneContextMenuEvent):
print(f'contextMenuEvent in "{self._name}"')
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._scene = QtWidgets.QGraphicsScene()
layout = QtWidgets.QHBoxLayout()
self._view = QtWidgets.QGraphicsView(self._scene)
layout.addWidget(self._view)
self._widget = QtWidgets.QWidget()
self._widget.setLayout(layout)
group = QtWidgets.QGraphicsItemGroup()
self._scene.addItem(group)
scene_item = GraphicsItem(QtCore.QRectF(0, 0, 100, 100), 'in scene')
self._scene.addItem(scene_item)
group_item = GraphicsItem(QtCore.QRectF(150, 0, 100, 100), 'in group')
group.addToGroup(group_item)
group_item = GraphicsItem(QtCore.QRectF(300, 0, 100, 100), '2nd in group')
group.addToGroup(group_item)
self.setCentralWidget(self._widget)
self.setWindowTitle('contextMenuEvent with QGraphicsItemGroup')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 800, 500)
mainWindow.show()
sys.exit(app.exec_())