Drag and Drop without duplicate elements - python

In PyQt4 with Python 3, how do I access the data being drag and dropped into a view/model?
Background
I am building an interface with two QListViews. I want to be able to drag an item from the first view and drop it into the second. However, the list needs to remain unique, so dropping an item that's already in the list should do nothing.
Problem
I've not been able to figure out how to access the data being dropped to verify that it's not already in the list.
Research
I've found the QAbstractItemModel method dropMimeData(), which should return True to accept the dropped data, and False to reject it. However, the drag and drop data is in a QMimeData, and it is of the type application/x-qabstractitemmodeldatalist. Going off the docs, I tried to decode the data:
def dropMimeData(self, data, action, row, column, parent=None):
stream = QtCore.QDataStream(data.data('application/x-qabstractitemmodeldatalist'))
text = ''
while not stream.atEnd():
stream >> text
print(text)
However, if I run this, I get
stream >> text
ValueError: string of length 1 expected
If I set text = 'a', then a just gets printed repeatedly.
I tried this solution, but in Python 3 I cannot create a QVariant object (per this answer).
Question
So how do I access the drag and drop data in PyQt? Is there a way to decode the QMimeData stuff? Or is there another way to check on what gets dropped?
Update
This is not a duplicate of this question. My question is about accessing the data dropped in a QAbstractItemView/QAbstractItemModel, while that one is about basic dragging and dropping in PyQt. The answer does not address how to get data from the mimetype 'application/x-qabstractitemmodeldatalist', nor does it show how to prevent duplicates in a QAbstractItemModel.

Turns out if you want to control your drag and drop behavior you need to set your own mime data.
You can encode the data you want to drag and drop any way you want. I'm going to put the dragged items into a list and encode them as JSON because I'm lazy.
In the model for the source view, override mimeTypes() and mimeData():
def mimeTypes(self):
return ['text/json']
def mimeData(self, indexes):
dragData = json.dumps([index.data() for index in indexes])
mimeData = QtCore.QMimeData()
mimeData.setData('text/json', dragData)
return mimeData
When someone grabs and drags an item/items from the list, Qt will call mimeData() to encode the items. Here I get the item data for each index dragged, put it in a list, convert the list to a string via JSON, and add that JSON to the QMimeData.
In mimeTypes() I specify that the mime data will include JSON data.
In the model for the destination view, override dropMimeData():
def dropMimeData(self, data, action, row, column, parent=None):
dropData = json.loads(bytes(data.data('text/json')))
for item in dropData:
if item in self.stringList():
self.removeRow(self.stringList().index(item))
if row != -1:
beginRow = row
elif parent:
beginRow = parent.row()
else:
beginRow = self.rowCount()
self.insertRows(beginRow, len(dropData))
for i, item in enumerate(dropData):
self.setData(self.index(beginRow+i, 0), item)
return True
In the first line I get the JSON data out of the QMimeData and decode it. Then, in the for loop, I check to see if one of the items is already in the destination view. If so, remove it.
After that the rest of the code determines where to add the new rows and inserts them.
More information is in the docs, particularly the section about adding new drag and drop types.

Related

How to open a document from a Notes View with python noteslib?

I have an established connection with a notes database and I am able to loop through all the records in a view. What I am curious about if it is possible to open a document and get the data from it using python. (Like double clicking on a record from an HCL Notes Client).
Here is my code simplified:
import noteslib
db = noteslib.Database('my-domino-server','my-db.nsf', 'mypassword')
view = db.GetView('my_view')
doc = view.GetFirstDocument()
while doc:
print(doc.ColumnValues)
#here after printing the column values, I want to open the document and store it's values in a variable.
doc = view.GetNextDocument(doc)
I tried googling about LotusScript and I found the Open() method, but doc.Open() did not work.
Just use the LotusScript documentation to find examples for everything you need.
In your case you start with the NotesDatabase - class, then get an object of type NotesView and finally get a NotesDocument object.
This doc object does not need to be opened. You can directly access all items in that document either by their name or -if you don't know the name- by cycling through all items.
If you e.g. know the name of an item (can be found in the document properties box on the second tab, found with Alt + Enter) then you can read the value like this:
#Subject of a given mail
subject = doc.GetitemValue( "Subject" )[0]
#Start date of a calendar entry
startdate = doc.GetItemValue( "StartDate" )[0]
# cycle through all items
for item in doc.Items
print(item.Name)
value = item.Values
Take care: items are always arrays, even if they contain only a single value. To get the value of a single value item, always access the element at position 0.

Removing Paragraph From Cell In Python-Docx

I am attempting to create a table with a two row header that uses a simple template format for all of the styling. The two row header is required because I have headers that are the same under two primary categories. It appears that the only way to handle this within Word so that a document will format and flow with repeating header across pages is to nest a two row table into the header row of a main content table.
In Python-DocX a table cell is always created with a single empty paragraph element. For my use case I need to be able to remove this empty paragraph element entirely not simply clear it with an empty string. Or else I have line break above my nested table that ruins my illusion of a single table.
So the question is how do you remove the empty paragraph?
If you know of a better way to handle the two row header implementation... that would also be appreciated info.
While Paragraph.delete() is not implemented yet in python-docx, there is a workaround function documented here: https://github.com/python-openxml/python-docx/issues/33#issuecomment-77661907
Note that a table cell must always end with a paragraph. So you'll need to add an empty one after your table otherwise I believe you'll get a so-called "repair-step" error when you try to load the document.
Probably worth a try without the extra paragraph just to confirm; I'm expect it would look better without it, but last time I tried that I got the error.
As #scanny said before, it can delete the current graph if pass the p to self-defined delete function.
I just want to do a supplement, in case if you want to delete multiple paragraphs.
def delete_paragraph(paragraph):
p = paragraph._element
p.getparent().remove(p)
paragraph._p = paragraph._element = None
def remove_multiple_para(doc):
i = 0
while i < len(doc.paragraphs):
if 'xxxx' in doc.paragraphs[i].text:
for j in range(i+2, i-2, -1):
# delete the related 4 lines
delete_paragraph(doc.paragraphs[j])
i += 1
doc.save('outputDoc.docx')
doc = docx.Document('./inputDoc.docx')
remove_multiple_para(doc)

Retrieving cell data from a selected cell in a tablewidget

I am making a stock control program and i have hit a problem with getting the value of a selected cell, i know i need to use "QtGui.QTableWidget.currentRow" and "QtGui.QTableWidget.currentColumn" to get the item's position. However i cannot seem to get this to work because when the functions are called nothing has been selected and so it returns -1,-1
Does anyone know how to get it so it runs the "QtGui.QTableWidget.currentRow" and "QtGui.QTableWidget.currentColumn" everytime the user selects a cell?
i think the code i need to get the actual data once i have the co-ords is QtGui.QTableWidget.item ?
This is the code i am using to get the row and column:
row = self.table.currentRow
column = self.table.currentColumn
self.ID = self.table.item(row, column)
when the user clicks a button to add stock the program should then use the product code it will have just got to make the change to the database after getting the quantity added from the user
I am using python 3.2 and pyqt 4
any help would be appreciated
Thank you
Sam
When the QTableWidget sees that someone has clicked one of it's cells, it will emit a cellClicked event - which you need to connect to. Maybe something like
self.table.cellClicked.connect(self.cell_was_clicked)
could be in your setup code,
and the function cell_was_clicked might be something like
def cell_was_clicked(self, row, column):
print("Row %d and Column %d was clicked" % (row, column))
item = self.table.itemAt(row, column)
self.ID = item.text()
I've not used currentRow and currentColumn as you want a response on the click. This function is documented here (sorry, I prefer pyside - it's pretty much the same as PyQT). Note also itemAt instead of just item - as you will get the item at the cell, not it's contents. Use the .text() function of QTableWidgetItem to get at the contents.
Note - this is using 'slots and signals' and in particular the 'new style'. As you're using PyQT4 and Python 3 you should have no trouble with 'new stuff' :-)
You might consider browsing a slots and signals tutorial - that might straighten a few of these abstract concepts out. Good luck!
here is the code that worked for me:
def get_selected_cell_value():
current_row = self.sold_items_details_table.currentRow()
current_column = self.sold_items_details_table.currentColumn()
cell_value = table.item(current_row, current_column).text()

How do you get the list data from a wxpyton gui click event?

I have a list ctrl box and I populate it with data.
self.listView1.Append([sFilename,sTitle,sArtist,sAlbum,sDestDir])
I created an event that triggers when a user clicks on a specific item in the list
def OnListView1ListItemSelected(self, event):
print "onListViewSelect"
This works, but what I am stuck on is how do I capture the single line of data from the list the user clicked on?
Using wxPython 2.8.10, this is one way to drop the text from all columns in the selected row into a list. You're getting the object, selected index, number of columns, and then grabbing the text from each column:
def onListView1ListItemSelected(self, event):
obj = event.GetEventObject()
index = event.GetIndex()
columns = obj.GetColumnCount()
data = []
for i in range(columns):
item = obj.GetItem(index, i)
data.append(item.GetText())
print(data)
I mentioned the version because I think the newest wxPython release allows you to specify a column in wx.ListCtrl.GetItemText, which could simplify things a bit. I haven't tried it though.
I think the simplest way is to just associate the data with the row. You can read about my approach here:
http://www.blog.pythonlibrary.org/2011/01/04/wxpython-wx-listctrl-tips-and-tricks/
Personally, I like ObjectListView the best: http://objectlistview.sourceforge.net/python/index.html

PyGTK: Doubleclick on CellRenderer

In my PyGTK application I currently use 'editable' to make cells editable. But since my cell contents sometimes are really really large I want to ask the user for changes in a new window when he doubleclicks on a cell. But I could not find out how to hook on double-clicks on specific cellrenderers - I don't want to edit the whole row and I also don't want to set this callback for the whole row, only for columns where too long content can occur. How can I do this with CellRendererText() or something similar.
My currently cell-generating code is:
cols[i] = gtk.TreeViewColumn(coltitle)
cells[i] = gtk.CellRendererText()
cols[i].pack_start(cells[i])
cols[i].add_attribute(cells[i], 'text', i)
cols[i].set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
cols[i].set_fixed_width(100)
cells[i].set_property('editable', True)
cells[i].connect('edited', self.edited, (i, ls))
cols[i].set_resizable(True)
mytreeview.append_column(cols[i])
Thanks!
I believe this is not possible directly. However, you can connect to button-press-event on the gtk.TreeView. Then, when event.type equals to gtk.gdk._2BUTTON_PRESS, convert x and y to tree location using gtk.TreeView.get_path_at_pos(). This will return both a tree path indicating the row and gtk.TreeViewColumn object on which the click was made.

Categories