PyGTK: Doubleclick on CellRenderer - python

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.

Related

QLineEdit dont want to save changes :(

stackoverflow!
It's my first question, I was just a reader before
I'm trying to make an app in python using PyQt5: I need to get data from DB into a table, every row has an EDIT button, when you push it, fields of the row become editable and you can change the data and the EDIT button changes to SAVE button. When you push it , data should be saved and be sent to database(I didn't made a "commit to DB" function yet) but its not saving and comes to previous amounts every time I click SAVE.
When I click EDIT button, it takes a cell text and than replace the widget in a cell by the same , but editable version(just change an option to EDITABLE = TRUE is not working), and it makes it for the whole row.
The save button should make the same, but it makes cells UNEDITABLE again...
tell me why it is so?
my function on SAVE button is
def ButtonSaveClicked(self):
s = self.sender()
roww = int((s.text().split())[1]) - 1
print('SAVE')
# replacing the existing active widgets by inactive with a same text
q = self.ui.tableWidget.item(roww, 0).text()
self.ui.btn_sell = QtWidgets.QLineEdit(q)
self.ui.btn_sell.setEnabled(False)
self.ui.tableWidget.setCellWidget(roww, 0, self.ui.btn_sell)
q = self.ui.tableWidget.item(roww, 1).text()
print(q)
self.ui.btn_sell = QtWidgets.QLineEdit(q)
self.ui.btn_sell.setEnabled(False)
self.ui.tableWidget.setCellWidget(roww, 1, self.ui.btn_sell)
q = self.ui.tableWidget.item(roww, 2).text()
print(q)
self.ui.btn_sell = QtWidgets.QLineEdit(q)
self.ui.btn_sell.setEnabled(False)
self.ui.tableWidget.setCellWidget(roww, 2, self.ui.btn_sell)
self.ui.btn_sell = QtWidgets.QPushButton("Edit " +str(roww+1))
self.ui.btn_sell.setEnabled(True)
self.ui.tableWidget.setCellWidget(roww, 3, self.ui.btn_sell)
self.ui.btn_sell.clicked.connect(self.ButtonEditClicked)
enter image description here
I will attempt to answer your question, but I think you may also want to consider the following alternative:
You can set your table to have Edit Triggers instead of you needing to keep track of the state of a QPushButton and keep replacing items and copying data back and forth between them.
My suggestion would be to use the QAbstractItemView::DoubleClicked edit trigger and you can apply that to your QTableWidget like this:
self.tableWidget.setSelectionBehavior(QAbstractItemView::SelectItems)
self.tableWidget.setSelectionMode(QAbstractItemView::SingleSelection)
self.tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked)
If the user double clicks on a cell, that cell becomes editable.
setSelectionBehavior() lets you choose whether you want each cell, row or column to be selected when clicked.
setSelectionMode() determines whether one (cell,row,column) or many (cells,rows,columns) can be selected at once. I think in your case you want to select one cell at a time.
This way the user can double click the cells they wish to edit and then you can have a "Save" button at the bottom of the widget which saves all the changes.
I will try to reproduce your problem above, but I wanted to bring this functionality to your attention as it may save you a lot of headaches.

How to refresh or update frame after value changes in text file using Python/Tkinter

So basically I am reading a valuefrom a text file which is displayed on the Profile frame using a label.
def view_value(self):
self.user = self.controller.user
self.view_value()
with open(self.user + '.txt', "r") as f:
value_line = 2
for i, line in enumerate(f):
if i == value_line:
self.value.set(line)
self.value.config(textvariable=line)
When I go to a different frame to calculate this value again, it will update the text file with the newly calculated value. However, when I go back to the previous page using the back button - the old value is still there. To get the new value to appear I need to reopen/re-run the program.
Is it possible to have the newly updated value displayed on the page without restarting the application? I have tried calling my view_value method to try and update the value and also tried configuring the label from the other class but wasn't able to get it working.
I also realise there are probably a million things wrong with my code, I am very new to Python so apologies!
You need to update the StringVar Profile.allowance in order to make the display in Profile page updated. The simple way is adding the following statement before self.controller.show_frame(Profile) in write_to_file() of CalculateAllowance class:
self.controller.frames[Profile].allowance.set(self.user_data[2])
Also you need to fix the following issues in your code:
Remove calling self.view_allowance() inside view_allowance() in Profile class as it will cause infinite recursion problem.
Remove self.holiday_allowance_amount.config(textvariable=line) in view_allowance() as it wrongly reassigns textvariable to a string.

Drag and Drop without duplicate elements

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.

Recovering checkboxes in python Tkinter

I have a situation in which i create a few checkboxes and a 'save' button on a frame. Clicking on the button will save the current 'check status' of the checkboxes and close the frame (the frame has another frame as it's parent). The 'check status' is stored in a list in a separate .py file.
There's another button, clicking on which would i would want to restore those same check values on the check boxes. I am not sure what approach is proper here: whether to save the checkbox objects itself and load them or to create new checkboxes every time and apply the check status values on them.
This issue has taken up quite some time of mine as i am new to the python GUI concept, any help would be much appreciated.
P.S: if any details are required to make the question more clear kindly let me know.
EDIT: here's a small representation of what i have tried:
#file that generates the checkboxes (part of a bigger code)
var_list=[] #list of the variables of the checkbox objects
load_status = savedata.load() #fetch the saved status list from another file, if it exists
if len(load_status)!=0: #if the checkboxes have already been created before
for count in range(len(var_list)):
if load_status[count] == 0:
var_list[count].set(0) #set/apply the values from the status list
else:
var_list[count].set(1)
return
def check(var_list):
check_status_list=[]
for i in range(len(var_list)):
c = var_list[i].get()
check_status_list.append(c) #the status of the checkboxes that is stored in another file
var = IntVar()
var_list.append(var)
poll_check = ttk.Checkbutton(parent,variable=rr,command=lambda:check(var_list))
poll_check.place(x=x1,y=y1)
x1=x1+15
when i click on the save button the status list (check_status_list) is stored in another file. On clicking the third button ('load') i fetch the status list first and (try to) set the values on the already exsting checkbox objects.
Kindly feel free to point out the (obviously) silly mistake that this approach has.

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()

Categories