well I'm using Pyqt4 in Maya2012 to make a reference editor alike ui. I working with a QtableWidget to make the reference list and i have subwidgets in each cell. One of the widgets is a checkbox that unload or reload the reference.
The problem i have is if a click directly in the checkbox without have the cell selected it doesn't do anything
this is my code:
def listConnections(self):
self.pos=self.sender().currentRow()
wid = self.list.ref_l.cellWidget(self.pos, 0).children()
self.text = self.list.list[self.pos]
self.ref()
for wt in wid:
if type(wt)== type(QCheckBox()):
wt.stateChanged.connect(self.changeState)
if type(wt)== type(QComboBox()):
wt.currentIndexChanged.connect(self.changeType)
I'm calling the function with a "itemSlectionChanged" signal because is the only way i knew i could detect the subwidgets.
All the subwidgets are made in the moment i fill the list.
Is there a way to make what i want?
Edit:
This is how i called the function
self.list.ref_l.itemSelectionChanged.connect(self.listConnections)
and this is how i create all the subwidgets in the cells
def fillList(self):
mayaRef = self.findRef()
if len(mayaRef)>0:
for count in range(0,len(mayaRef)):
self.ref_l.insertRow(count)
wid=QWidget()
cLayout=QHBoxLayout()
wid.setLayout(cLayout)
checkWid=QCheckBox()
nameWid=QLabel()
cLayout.addWidget(nameWid)
nameWid2=QLabel()
cLayout.addWidget(nameWid2)
comWid=QComboBox()
cLayout.addWidget(comWid)
self.ref_l.setCellWidget(count,0,wid)
self.ref_l is my QTable Widget, this is in another code that i'm calling with self.list in the original
You should set up all your connections right after you create the check boxes in fillList. Each item is associated with the path of the reference. You can use a QSignalMapper to map each check box to the path, then connect the signal mapper to your changeState slot. The signal mapper then calls that slot with the path you specified.
Related
Alright, so I'm pretty deep into an app I'm making using PyQt5 and I have a section (QScrollArea) where I essentially have multiple lines of "accounts" which are QWidgets. The user can add an account and delete an account, but I would like to give them the ability to edit an account. On the same row as the account is a QPushButton, which is basically the edit button. If I have 3 accounts, then I can see 3 buttons. However, when I click the button for any account, all of the buttons refer to the exact same button object. I am storing the objects in a list and using findChildren() to get the objects. Both of these will give me three different button objects, but using clicked.connect() with the buttons reference only the latest, or bottom-most, button.
I am calling the clicked.connect() function after the part of my code that adds the account. I have also tried moving this to right below where I instantiate the button, but it didn't work.
Here is what I have:
btns = self.accountsWidget.findChildren(QPushButton)
for btn in btns:
btn.clicked.connect(lambda: self.editAccount(btn))
The method I call to try to edit the account is here:
def editAccount(self, btn):
print(btn)
self.editAccountWindow = EditAccountsWindow()
self.editAccountWindow.show()
The result of what's printed is always the last QPushButton object, and I'm only printing to see if I referenced the button I wanted to. That method just opens up a window, but what's shown on the window depends on calling the correct object. I want to use the object to reference which account I want to edit.
I have looked on many SO threads and could not find anything similar to this. I think I'm missing something, so please help a brother out :)
def editAccount(self, objects):
sender = self.sender()
index = 0
for object in objects:
if sender == object:
account = account_handler.get_account(index)
...
widgets = self.accountsScrollView.findChildren(QWidget, "accountsWidget")
for widget in widgets:
editButtons = widget.findChildren(QPushButton, 'editButtonAccounts')
for e in range(0, len(editButtons)):
editButtons[e].clicked.connect(lambda: self.editAccount(edit_objects_list))
...
I am loading in a picture into a QGraphicsScene then when you click on the photo it will place circles in the area that is clicked. Then I am adding a custom widget that will keep a list of the points. This widget has a couple of buttons. one of them will be able to move the circle and the other will be to delete it. I am currently stuck on the delete part.
Problem
I can delete the circle just fine and I can delete the widget from the list. The problem is there is still a space in the list from where the widget once was and since I'm using the button from the widget and not selecting the item I'm not sure how to delete that spot. Also if I delete a bunch and then try and add some Python its self will crash and I have no idea why.
I'm not sure if what I want can be done since there really is no reference or if I'm better off moving the buttons to the main window and removing them from the custom widget. If it is possible I would like to keep it the way I have it.
There are a few files so I will put it on GitHub so I am not to take up a ton of space. Any help is much appreciated.
GitHub Link
Link to GitHub Project
The relevant code (from Main.py):
class MainWindow(QMainWindow, Ui_MainWindow):
...
def Add_History(self,pos):
self.HistoryWidget = HistoryList()
self.HistoryWidget.setObjectName("HistoryWidget_"+ str(Constents.analysisCount))
myQListWidgetItem = QListWidgetItem(self.History_List)
myQListWidgetItem.setSizeHint(self.HistoryWidget.sizeHint())
self.History_List.addItem(myQListWidgetItem)
self.History_List.setItemWidget(myQListWidgetItem, self.HistoryWidget)
self.HistoryWidget.buttonPushed.connect(self.deleteObject)
self.HistoryWidget.setXpoint(str(pos.x()))
self.HistoryWidget.setYpoint(str(pos.y()))
self.HistoryWidget.setHistoryName("Point "+ str(Constents.analysisCount))
Constents.analysisCount = Constents.analysisCount + 1
def deleteObject(self):
sender = self.sender() #var of object that sent the request
row_number = sender.objectName().split("_")
number = row_number[1]
x,y = Constents.analysisDetails[str(number)]# getting xy for object
self.loadPicture.findAndRemoveAnalysisPoint(x,y) #calling the class with the scense to delete it
Constents.analysisDetails.pop(str(number)) # get rid of the object in the variables
HistoryWidget = self.findChildren(HistoryList, "HistoryWidget_"+number)[0] #find the actual object
HistoryWidget.deleteLater() #delete that object
#Simport pdb; pdb.set_trace()
#self.History_List.takeItem(HistoryWidget)
Pictures
You must delete both the item-widget and the item itself. To do that, a method is required for getting an item from an item-widget (or one of its child widgets). A clean way to do this is to use the list-widget's itemAt method, which can get an item from a point on the screen. The major benefit of doing it this way is that it does not require knowledge of the item's index, which can of course change when other items are deleted. It also means the item-widgets don't need to know anything about the specific list-widget items they are associated with.
Here is a re-write of your deleteObject method, which implements that:
def deleteObject(self):
sender = self.sender() #var of object that sent the request
row_number = sender.objectName().split("_")
number = row_number[1]
x,y = Constents.analysisDetails[str(number)]# getting xy for object
self.loadPicture.findAndRemoveAnalysisPoint(x,y) #calling the class with the scense to delete it
Constents.analysisDetails.pop(str(number)) # get rid of the object in the variables
# get the viewport coords of the item-widget
pos = self.History_List.viewport().mapFromGlobal(
sender.mapToGlobal(QtCore.QPoint(1, 1)))
if not pos.isNull():
# get the item from the coords
item = self.History_List.itemAt(pos)
if item is not None:
# delete both the widget and the item
widget = self.History_List.itemWidget(item)
self.History_List.removeItemWidget(item)
self.History_List.takeItem(self.History_List.row(item))
widget.deleteLater()
I want to dynamically change the number of sliders on my application window, in dependence of the number of checked items in a QStandardItemModel structure.
My application window has an instance of QVBoxLayout called sliders, which I update when a button is pressed:
first removing all sliders eventually in there:
self.sliders.removeWidget(slider)
And then creating a new set.
The relevant code:
def create_sliders(self):
if len(self.sliders_list):
for sl in self.sliders_list:
self.sliders.removeWidget(sl)
self.sliders_list = []
for index in range(self.model.rowCount()):
if self.model.item(index).checkState():
slid = QSlider(Qt.Horizontal)
self.sliders.addWidget(slid)
self.sliders_list.append(slid)
The principle seems to work, however what happens is weird as the deleted sliders do not really disappear but it is as they were 'disconnected' from the underlying layout.
When created, the sliders keep their position among other elements while I resize the main window.
However, once they've been removed, they occupy a fixed position and can for instance disappear if I reduce the size of the window.
Unfortunately I'm having difficulties in linking images (it says the format is not supported when I try to link from pasteboard), so I hope this description is enough to highlight the issue.
Do I have to remove the sliders using a different procedure?
EDIT
Thanks to #eyllansec for his reply, it condenses a bunch of other replies around the topic, which I wasn't able to find as I did not know the method deleteLater() which is the key to get rid of widgets inside a QLayout.
I am marking it as my chosen (hey, it's the only one and it works, after all!), however I want to propose my own code which also works with minimal changes w.r.t. to what I proposed in the beginning.
The key point here is that I was using the metod QLayout.removeWidget(QWidget) which I was wrongly thinking, it would..er..Remove the widget! But actually what it does is (if I understood it right) remove it from the layout instance.
That is why it was still hanging in my window, although it seemed disconnected
Manual reference
Also, the proposed code is far more general than what I need, as it is a recursion over layout contents, which could in principle be both other QLayout objects or QWidgets (or even Qspacer), and be organized in a hierarchy (i.e., a QWidget QLayout within a QLayout and so on).
check this other answer
From there, the use of recursion and the series of if-then constructs.
My case is much simpler though, as I use this QVLayout instance to just place my QSliders and this will be all. So, for me, I stick to my list for now as I do not like the formalism of QLayout.TakeAt(n) and I don't need it. I was glad that the references I build in a list are absolutely fine to work with.
In the end, this is the slightly changed code that works for me in this case:
def create_sliders(self):
if len(self.sliders_list):
for sl in self.sliders_list:
sl.deleteLater()
self.sliders_list = []
for index in range(self.model.rowCount()):
if self.model.item(index).checkState():
slid = QSlider(Qt.Horizontal)
self.sliders.addWidget(slid)
self.sliders_list.append(slid)
It is not necessary to save the sliders in a list, they can be accessed through the layout where it is contained. I have implemented a function that deletes the elements within a layout. The solution is as follows:
def create_sliders(self):
self.clearLayout(self.sliders)
for index in range(self.model.rowCount()):
if self.model.item(index).checkState():
slid = QSlider(Qt.Horizontal)
self.sliders.addWidget(slid)
def clearLayout(self, layout):
if layout:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget:
widget.deleteLater()
else :
self.clearLayout(item.layout())
layout.removeItem(item)
I have a window containing multiple QRowWidgets, which are custom widgets defined by me. These QRowWidgets contain QLineEdits and other standard widgets. To show or hide certain parts of a QRowWidget, I overdefined the focusInEvent() methodes of all the widgets within it. It works perfectly, when I click on the QRowWidget, the hidden elements appear.
The weird thing is that the blinking cursor line hovewer doesn't appear in the QLineEdits within the custom widgets. I can select them both by a mouse click or with Tab, and a glow effect indicates that the QLineEdit is selected in it, I can select a text in it, or start typing at any location wherever I clicked, but the cursor never appears and it's quite annoying.
My 1st thought was that it is a bug on Mac, but I have the same experience on SuSe Linux.
I'm using python 2.7 and PyQt4.
This is in the __init__() of the QRowWidget:
for i in self.findChildren(QWidget):
i.focusInEvent = self.focusInEvent
And then this is the own focusInEvent():
def focusInEvent(self, event):
if self.pself.focusedLine:
self.pself.focusedLine.setStyleSheet("color: #666;")
self.pself.focusedLine.desc.hide()
self.pself.focusedLine.closebutton.hide()
self.setStyleSheet("color: #000;")
self.desc.show()
self.closebutton.show()
self.pself.focusedLine = self
I suspect you do not make a call to the original focusInEvent() when you override it. Your function should look something like:
def focusInEvent(self,...):
QParent.focusInEvent(self,...)
# the rest of your code
where QParent is the nearest base class for your widgets is.
Either that, or make sure you call focusInEvent() on your QLineEdit widgets as part of your function.
Given the comments, it sounds like you are dynamically reassigning the focusInEvent function on the insantiatations in your custom widget. I would either make a derived class for each of the widgets you use that just overrides focusInEvent as above, or include a line like
type(self).focusInEvent(self,..)
in you function.
I am a Python and Qt newbie...
I am working on a GUI program that shows a list of data using QTableWidget at the beginning.
The rows are inserted into the table using the setItem() method. They are QTableWidgetItem objects.
Now I want to allow users to click-select a certain row (the QTableWidgetItem), and my program will populate a secondary QTableWidget.
I have learnt that there is a thing called Signal and Slot. Am I going to use that?
There are also examples of using installEventFilter(), but it is not appropiate for the QTableWidgetItem.
The easiest way for this would just be to use the itemClicked-signal of the QTableWidget:
def handle_item_clicked(tableItem):
#the callback, called when an item is clicked, with the item as an argument
#populate the secondary table in some way
#register the callback with the QTableWidget
yourTableWidget.itemClicked.connect(handle_item_clicked)
You could also use the currentItemChanged signal which gives you the current and previous selection (e.g. so you can clear or hide your secondary widget if the user deselects an item, in which case the current item will be None).
Qt doesn't have "event listeners". As you mentioned, you do such things by connecting signals to slots.
For example:
QObject.connect(myTableWidget, QtCore.SIGNAL("clicked()"), self.foobar)
This will cause the method self.foobar() to be called when you click on myTableWidget. The foobar function could retrieve which item is selected and populate the second widget based on that.
Take note that in the last argument of connect, you don't use the parentheses (foobar()), just the method name!