i am currently designing a QT-gui in Python and i want to allow the user to switch QListWidgetItems between two QListWidgets. Multiple selection (CTRL) is allowed and switching is done by two control buttons.
In the QT4-Designer the lists looks like this
So if the user selects for example two items from the left list and clicks on the '>' Button the items have to be added to the right list and consequently deleted from the left list.
My current triggered Button-Events look like this:
def switchR( self ):
itemlistSel = self.list_left.selectedItems()
for item in itemlistSel:
self.list_right.addItem( item )
self.list_left.removeItemWidget( item )
But nothing happens? Someone got a quick solution?
The removeItemWidget() method doesn't quite do what you're expecting it to do (see docs). Use takeItem(), addItem() and row() instead:
def switch(self):
items = self.left.selectedItems()
for item in items:
n = self.left.row(item) # get the index/row of the item
i = self.left.takeItem(n) # pop
self.right.addItem(i) # add to right QListWidget
Related
Using Python 3.x and wxPython.
I'm working on a GUI tool that will likely need to be expanded and updated over time and I'm trying to reduce the amount of work (long term) as features are added.
I have a method that needs a variable passed to it, and then the method checks the dictionary to see which "ID" is associated with it, then uses that ID to trigger some automation.
job_dictionary = {
'job_name_in_english': 'id_goes_here',
'job_name_in_english': 'id_goes_here'
}
I have a very simple window setup and I'm populating a file menu with the following:
for key in job_dictionary:
newitem = wx.MenuItem(file_menu,wx.ID_NEW, text = key,kind = wx.ITEM_NORMAL)
file_menu.Append(newitem)
Then later on I want to bind them to a method, and pass that method the 'key' value, depending on which item they select from the menu.
Which is where I'm running into some difficulties. I was wondering if there was a way to bind them dynamically based on the key value within the dictionary, because once I've got that part done I can just do call the method and pass it the key:
def job_trigger(key)
id = job_dictionary[key]
#Rest Of Code...
Hopefully what I'm trying to do makes sense. I'm still new to building GUI applications, and this is the first time I'm dealing with a File Menu, and the first time I'm trying to implement something like this.
I know in a batch script I can do something like this:
set x=0
loop of some kind here(
set /A "x=x+1"
set menuItem%x%=foobar
)
Which would make the variable name dynamic, and then I was thinking I could bind them as they're being written. So I guess something like:
x = 0
for key in dictionary
x += 1
menu_item = 'menu_item_' + x
'menu_item_' + x = wx.Menu().Append(wx.ID_FILEDLGG, key)
'menu_item_' + x.Bind(wx.EVT_MENU, self.job_trigger(key), key)
I know that's not the correct syntax for something like this, and I have no idea if that would be the appropriate way of handling this scenario, or if there is something else entirely I should be attempting.
From what I deducted, you might need only simple iteration over dict:
#! /usr/bin/env python3
a_dict = {
"key_0": "value_0",
"key_1": "value_1"
}
for i, v in enumerate(a_dict):
print("current index: {}".format(i))
print("current value: {}".format(v))
# string manipulation
builder_string = "prefix_" + str(i)
print(builder_string)
# rest of code...
If you also need keys, then you can use for key, value in a_dict.items(): and set index outside loop and increment it manually.
I mistakenly confused the File menu with the dropdown menu. So here is my solution.
job_dictionary = {
'job_name1': 'job_id_1',
'job_name2': 'job_id_2'
}
Code to make Window and Panel goes here
#Create Dropdown Menu
jobs = list()
for i in job_dictionary.keys():
jobs.append(i)
self.choice = wx.Choice(panel, pos=(5,5), size=(375,50), choices = jobs)
#Create Buttons
trigger_job_btn = wx.Button(panel, label='Trigger Job', pos=(290,50))
#Button Trigger
trigger_job_btn.Bind(wx.EVT_BUTTON, self.execute_job)
The above creates a list from the dictionary, then uses that list to populate the dropdown menu (wx.Choice). Then I create a button, and bind that button to a method.
Then the method looks like this:
def execute_job(self, evt):
choice = self.choice.GetSelection()
if choice == -1:
wx.MessageBox('Please Make a Selection from the DropDown.',
'Warning', wx.OK | wx.ICON_INFORMATION)
else:
key = self.choice.GetString(choice)
jobTrigger(key, job_dictionary[key])
The above method gets choice number, if it's blank, you get an error message popup informing you to make a selection. If it's a populated choice, it will then look for the string within that choice, and pass that to another method as the dictionary key value.
I need some help with deleting some items from some lists at the same time, when a button is clicked.
This is the code:
class Window(QMainWindow):
list_1 = [] #The items are strings
list_2 = [] #The items are strings
def __init__(self):
#A lot of stuff in here
def fillLists(self):
#I fill the lists list_1 and list_2 with this method
def callAnotherClass(self):
self.AnotherClass().exec_() #I do this to open a QDialog in a new window
class AnotherClass(QDialog):
def __init__(self):
QDialog.__init__(self)
self.listWidget = QListWidget()
def fillListWidget(self):
#I fill self.listWidget in here
def deleteItems(self):
item_index = self.listWidget.currentRow()
item_selected = self.listWidget.currentItem().text()
for i in Window.list_2:
if i == item_selected:
?????????? #Here is where i get confussed
When i open the QDialog with a combination of keys, i see in the QListWidget some items. In the deleteItems method, i get the index and the text from the item that i selected in the QListWidget. That works fine.
What i need to do is to delete the item from the list_1, list_2, and the QListWidget when i press a button (that i have already created).
How can i do this? Hope you can help me.
Python lists have a "remove" object that perform that action directly:
Window.list_2.remove(item_selected)
(with no need for your for loop)
If you ever need to perform more complex operations on the list items, you can retrieve an item's index with the index method instead:
position = Window.list_2.index(item_selected)
Window.list_2[position] += "(selected)"
And in some ocasions you will want to do a for loop getting to the actual index, as well as the content at that index of a list or other sequence. In that case, use the builtin enumerate.
Using the enumerate pattern, (if removedid not exist) would look like:
for index, content in enumerate(Window.list_2):
if content == item_selected:
del Window.list_2[index]
# must break out of the for loop,
# as the original list now has changed:
break
if you have the value, then just find its index in each list and then delete it. Something like:
item_selected = self.listWidget.currentItem().text()
i = Window.list_2.index(item_selected)
if i >= 0:
del Window.list_2[i]
You can also use directly Widow.list_x.remove(value) but it can throw an exception if the value does not exist.
So I've been trying to implement a proper TreeView that displays directories and files depending on user input - I am allowing the user to add directories and files to his "project" either recursively or otherwise, after which I create my own tree view of the contents of said project.
Now, my problem is that even though most of the documentation and other questions I've found on this subject seem to want to disable the editability of treeview items, I am trying ( and failing ) to find a way to enable it. What I would like is for the user to be able to double click on any cell in any column of my treeview and then edit its contents. Does anyone know how to do this?
Below is the code I am using to generate a tab in a tabView Widget, after which i then add the TreeView. The items of the TreeView are later added through the AddParent and AddChild methods.
class treeTab(QtWidgets.QWidget):
def __init__(self,core,main,label):
super (treeTab,self).__init__()
self.label = label
self.core = core
self.sizes = core.UISizes
self.tab_sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Expanding)
self.tree = QtWidgets.QTreeWidget(self)
self.tree.setColumnCount(len(self.sizes.projectTreeColumnLabels))
self.tree.setHeaderLabels(self.sizes.projectTreeColumnLabels)
self.tree.setSizePolicy(self.tab_sizePolicy)
self.tree_layout = QtWidgets.QGridLayout()
self.tree_layout.objectName = self.label + "TreeGridLayout"
self.tree.setLayout(self.tree_layout)
self.treeroot = self.tree.invisibleRootItem()
self.tree.setSelectionMode(Qt.QAbstractItemView.ContiguousSelection)
def addParent(self, parent, column, title, data):
item = QtWidgets.QTreeWidgetItem(parent, [title])
item.setData(column, QtCore.Qt.UserRole, data)
item.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ShowIndicator)
item.setExpanded (True)
return item
def addChild(self, parent, column, title, data):
item = QtWidgets.QTreeWidgetItem(parent, [title])
item.setData(column, QtCore.Qt.UserRole, data)
item.setText(1,data.print_tags())
item.setText(2,data.category.name)
item.setText(3,data.format)
item.setCheckState (column, QtCore.Qt.Unchecked)
item.setFlags(item.flags() or QtCore.Qt.ItemIsEditable)
return item
You have confused binary operators and Boolean operators. Boolean operators (such as and and or) are used with Boolean values (such as True and False) to produce a single True or False once the expression is evaluated.
However, the flags and not Boolean values. They are integers which are powers of 2 (or binary numbers with only one bit set), such that they can be combined into a single integer that represents whether each flag is enabled or disabled. For instance, 2 is represented in binary as 0b010. 4 is represented as 0b100. If you bitwise or these together, you get 0b110, indicating that both the flag equal to 2 and the flag equal to 4 is set. However the flag equal to 1 is not set (the 0 in the 0b110).
In short, you should set the flags using the bitwise or operator (|):
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
So I managed to figure this one out - it turned out to be quite simple in the end :)
In order to create an 'editable' treeView item where you can double click on the text of a particular item to edit it, you simply need to change the widget contained in that particular column of the item to a QLineEdit Widget that deletes itself upon pressing enter. The code for this looks as follows:
This is the code to connect the double click event to a method that gets the currently selected item and replaces it with a QLineEdit Widget:
self.tree.itemDoubleClicked.connect(self.editItem)
def editItem(self,*args):
itm = self.tree.itemFromIndex(self.tree.selectedIndexes()[0])
column = self.tree.currentColumn()
edit = QtWidgets.QLineEdit()
edit.returnPressed.connect(lambda*_:self.project.setData(column,edit.text(),itm,column,self.tree))
edit.returnPressed.connect(lambda*_:self.update())
self.tree.setItemWidget(itm,column,edit)
Note especially the combination of the following code:
itm = self.tree.itemFromIndex(self.tree.selectedIndexes()[0])
column = self.tree.currentColumn()
This code in effect gives you both the row and the column of the currently selected item, which is useful if you want to edit column items separately.
Now You'll be asking yourself why I'm passing so many parameters to the 'setData' method: this is purely for the purposes of my particular project, so don't worry about it. the 'returnPressed' event simply needs to connect to the proper method to handle whatever data it contains, and then delete itself. in my code, this looks like this:
def setData(self,dataTypeIndex,data,item,column,tree):
if dataTypeIndex == 0:
# filename
self.name = data
elif dataTypeIndex == 1:
# tags
data = data.split(",")
self.tags = []
for tag in data:
self.tags.append(Tag(tag))
elif dataTypeIndex == 2:
# category
self.category.name = data
tree.setItemWidget(item,column,None)
This last line of code ( tree.setItemWidget(item,column,None) ) is where the QlineEdit is unparented and therefore effectively removed.
Im learning how to use QTreeWidget and Im stuck adding new items to it. The QTreewidget itself is created with qtdesigner, so my idea was just to add items. eg:
tw = self.ui.treeWidget
item = QtGui.QTreeWidgetItem("TEST")
tw.addTopLevelItem(item)
But in the treewidget only appears the first letter of "TEST". Doesnt matter what I type, it always only displays the first letter and I have no idea why...
QTreeWidgetItem constructor expects a list of strings. Try this:
tw = self.ui.treeWidget
item = QtGui.QTreeWidgetItem(["TEST"])
tw.addTopLevelItem(item)
The QtGui.QTreeWidgetItem is expecting a list for different columns. You can simply wrap your text in a list
item = QtGui.QTreeWidgetItem(["TEST"])
or you can set the text for a specific column.
item = QtGui.QTreeWidgetItem()
item.setText(0, "TEST")
Created a QtGui.QListWidget list widget:
myListWidget = QtGui.QListWidget()
Populated this ListWidget with QListWidgetItem list items:
for word in ['cat', 'dog', 'bird']:
list_item = QtGui.QListWidgetItem(word, myListWidget)
Now connect a function on list_item's left click:
def print_info():
print myListWidget.currentItem().text()
myListWidget.currentItemChanged.connect(print_info)
As you see from my code all I am getting on a left click is a list_item's label name. But aside from a label name I would like to get a list_item's index number (order number as it is displayed in ListWidget). I would like to get as much info on left-clicked list_item as possible. I looked at dir(my_list_item). But I can't anything useful there ( other than already used my_list_item.text() method which returns a list_item's label name). Thanks in advance!
Use QListWidget.currentRow to get the index of the current item:
def print_info():
print myListWidget.currentRow()
print myListWidget.currentItem().text()
A QListWidgetItem does not know its own index: it's up to the list-widget to manage that.
You should also note that currentItemChanged sends the current and previous items as arguments, so you could simplify to:
def print_info(current, previous):
print myListWidget.currentRow()
print current.text()
print current.isSelected()
...
Well, I have listed some of the things you can display about the current item, if you want more than this then you should look through the PyQt Documentation. link
def print_info():
print myListWidget.currentItem().text()
print myListWidget.row(myListWidget.currentItem())
print myListWidget.checkState() # if it is a checkable item
print myListWidget.currentItem().toolTip().toString()
print myListWidget.currentItem().whatsThis().toString()
myListWidget.currentItemChanged.connect(print_info)