PyQt5: Get row number from button-menu action in QTableView index-widget - python

Basically, I have a QTableView and the last column of each row contains a QMenu where if triggered, the row should be deleted. I tried the code below, but if I click on the menu that is in a row number > 1, the returned rowNum is -1:
Code:
def addrowIntable(self, a, b):
Column1 = QStandardItem("%s" % (a))
Column2 = QStandardItem("%s" % (b))
actionBTN = QPushButton("")
actionMenu = QMenu()
self.RemList = QtWidgets.QAction("Remove row", actionMenu)
actionMenu.addAction(self.RemList)
actionMenu.raise_()
actionBTN.setMenu(actionMenu)
self.rulesWhitelistWidgetModel.appendRow([Column1, Column2])
self.rulesWhiteListFileTBL.setIndexWidget(
self.rulesWhitelistWidgetModel.index(self.rulesWhitelistWidgetModel.rowCount() - 1,
self.rulesWhiteListFileTBL.model().columnCount() - 1), actionBTN)
self.RemList.triggered.connect(lambda: self.deleteRow("Hello"))
def deleteRow(self, str):
rowNum = self.rulesWhiteListFileTBL.rowAt(self.rulesWhiteListFileTBL.viewport().mapFromGlobal(self.sender().parent().pos()).y())
print(self.rulesWhiteListFileTBL.indexAt(self.sender().parent().pos()).row())
print(rowNum)
I just need to know which row number was the sender from inside deleteRow where I could then use model.removeRow() to delete it.

The main reason why your code doesn't work as expected is because you set the menu as the parent of the action. A menu is a popup window, so its position will be in global coordinates, whereas you want the position relative to the table. A simple way to achieve this is to make the button the parent of the action instead.
The following revision of your code should do what you want:
def addrowIntable(self, a, b):
Column1 = QStandardItem("%s" % (a))
Column2 = QStandardItem("%s" % (b))
actionBTN = QPushButton()
actionMenu = QMenu()
actionRem = QtWidgets.QAction("Remove row", actionBTN)
actionMenu.addAction(actionRem)
actionBTN.setMenu(actionMenu)
self.rulesWhitelistWidgetModel.appendRow([Column1, Column2])
self.rulesWhiteListFileTBL.setIndexWidget(Column2.index(), actionBTN)
actionRem.triggered.connect(lambda: self.deleteRow("Hello"))
def deleteRow(self, str):
pos = self.sender().parent().pos()
index = self.rulesWhiteListFileTBL.indexAt(pos)
self.rulesWhitelistWidgetModel.removeRow(index.row())

Related

Python tkinter widget tksheet problem with printing cell values

I'm trying to print the value from a selected cell into the sheet widget, but every time it returns 'No cell is selected'. Here is the code snippet:
import tkinter as tk
import sqlite3
import tksheet
top = tk.Tk()
sheet = tksheet.Sheet(top, width=943)
sheet.place(relx = 0.0, rely = 0.1)
conn = sqlite3.connect('dbz/data.db')
c = conn.cursor()
c.execute('''SELECT * FROM table''')
rows = c.fetchall()
data = rows
sheet.set_sheet_data(data = data)
sheet.enable_bindings(("single_select",
"column_select",
"row_select",
"column_width_resize",
"row_height_resize",
"arrowkeys",
"right_click_popup_menu",
"rc_select",
"rc_insert_row",
"rc_delete_row",
"copy",
"cut",
"paste",
"delete",
"undo",
"edit_cell",
"drag_select"))
selected_cells = sheet.get_selected_cells()
def pr(e):
# If there is a selected cell, get the data for it
if selected_cells:
#row, column = selected_cells[0]
value = sheet.get_cell_data(r=row, c=column, return_copy=True)
print(value)
else:
print("No cell is selected")
sheet.bind("<1>", pr)
#sheet.bind("cell_update", update_record)
top.mainloop()
I tried most of the things I found in the documentation: https://github.com/ragardner/tksheet/wiki
get_sheet_data(return_copy = False, get_header = False, get_index = False)
get_cell_data(r, c, return_copy = True)
, etc. But nothing seems to work. I would very much appreciate any help. Thanks in advance.
this should work like you want.
Firstly, you need to identify which row and column was clicked. Really useful to get this information with sheet.identify_row and sheet.identify_column.
With sheet.cell_selected we can be sure that cell was actually clicked.
At the end, I've changed sheet.bind("<1>", pr) to sheet.bind("<ButtonPress-1>", pr).
def pr(event):
row = sheet.identify_row(event, exclude_index = False, allow_end = True)
column = sheet.identify_column(event, exclude_header = False, allow_end = True)
# If there is a selected cell, get the data for it
if sheet.cell_selected(row, column):
value = sheet.get_cell_data(row, column, return_copy = True)
print(value)
else:
print("No cell is selected")
sheet.bind("<ButtonPress-1>", pr)

Make an editable table in PySimpleGUI?

Hello I am using a Table element from PySimpleGUI. I would like for the user to be able to edit the data inside it.
I have seen some mentions of it. Is it possible? This guy was using PySimpleGUIQt, while I am using the PySimpleGUI on top of tkinter.
It's much difficult for me to build it by pure PySimpleGUI code.
If go it with new class inherited from sg.Table, some variables as argument or global variables not required.
Here, colors of cell not considered in Entry.
All tkinter code in function edit_cell of following example.
import PySimpleGUI as sg
import random, string
# ------ Some functions to help generate data for the table ------
def word():
return ''.join(random.choice(string.ascii_lowercase) for i in range(10))
def number(max_val=1000):
return random.randint(0, max_val)
def make_table(num_rows, num_cols):
data = [[j for j in range(num_cols)] for i in range(num_rows)]
data[0] = [word() for _ in range(num_cols)]
for i in range(0, num_rows):
data[i] = [i, word(), *[number() for i in range(num_cols - 1)]]
return data
def edit_cell(window, key, row, col, justify='left'):
global textvariable, edit
def callback(event, row, col, text, key):
global edit
widget = event.widget
if key == 'Return':
text = widget.get()
print(text)
widget.destroy()
widget.master.destroy()
values = list(table.item(row, 'values'))
values[col] = text
table.item(row, values=values)
edit = False
if edit or row <= 0:
return
edit = True
root = window.TKroot
table = window[key].Widget
text = table.item(row, "values")[col]
x, y, width, height = table.bbox(row, col)
frame = sg.tk.Frame(root)
frame.place(x=x, y=y, anchor="nw", width=width, height=height)
textvariable = sg.tk.StringVar()
textvariable.set(text)
entry = sg.tk.Entry(frame, textvariable=textvariable, justify=justify)
entry.pack()
entry.select_range(0, sg.tk.END)
entry.icursor(sg.tk.END)
entry.focus_force()
entry.bind("<Return>", lambda e, r=row, c=col, t=text, k='Return':callback(e, r, c, t, k))
entry.bind("<Escape>", lambda e, r=row, c=col, t=text, k='Escape':callback(e, r, c, t, k))
def main_example1():
global edit
edit = False
# ------ Make the Table Data ------
# sg.Print('Creating table...')
data = make_table(num_rows=1_000, num_cols=6)
# headings = [str(data[0][x])+' ..' for x in range(len(data[0]))]
headings = [f'Col {col}' for col in range(len(data[0]))]
# sg.Print('Done creating table. Creating GUI...')
sg.set_options(dpi_awareness=True)
layout = [[sg.Table(values=data, headings=headings, max_col_width=25,
auto_size_columns=True,
# display_row_numbers=True,
justification='right',
num_rows=20,
alternating_row_color=sg.theme_button_color()[1],
key='-TABLE-',
# selected_row_colors='red on yellow',
# enable_events=True,
# select_mode=sg.TABLE_SELECT_MODE_BROWSE,
expand_x=True,
expand_y=True,
enable_click_events=True, # Comment out to not enable header and other clicks
)],
[sg.Button('Read'), sg.Button('Double'), sg.Button('Change Colors')],
[sg.Text('Cell clicked:'), sg.T(k='-CLICKED-')]]
window = sg.Window('Table Element - Example 1', layout, resizable=True, finalize=True)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif isinstance(event, tuple):
cell = row, col = event[2]
window['-CLICKED-'].update(cell)
edit_cell(window, '-TABLE-', row+1, col, justify='right')
window.close()
main_example1()
I would strongly suggest use MutiLine for showing table, which is much simpler to manage and can be easily updated etc. The Same table effects can be achieved by less lines of code. Giving a sample code here from my own database program with uses Sqlite3 table.
def ViewTable (TableNme):
try: # LOAD TABLE FROM Sqlite3 Database
txt2="SELECT * FROM {} ".format(TableNme)
cursor.execute(txt2)
##Include headers###
txt2='S.No '
for j in fields: txt2=txt2+j +" "
txt2=txt2+'\n'
#converts the table into string separated by Comma
#fine as long as table is not very massive running more than 3 Mbs, having more than 1000 records
while True:
h1=cursor.fetchone()
if not h1: break
for j in h1:txt2=txt2+str(j)+" "
txt2=txt2+'\n'
#sg.popup('Archieved table', txt2)
#Define text to load by text= before calling me
layout2 = [[sg.Multiline(txt2,size=(28,28),key='-Items-'),],[sg.Ok()] ]
sg.Window('Scroll to see the whole database', layout2,finalize=True).read(close=True)
except:
sg.popup_error('Failed','Access denied or Memory full.')

how to disable all columns apart from one column in Qtablewidget PYQT - python

Problem Statement:
Disable specific columns and enable specific row on bases of current row index.
I am working on Qtablewidget i tried following code this working fine for disable.
i wanted to enable tablewidget row contents on bases of row index .
when i clicked pushbutton that row should be enable.I am new to the stackoverflow please guide me.I am tring to enable tablewidget cell,pushbutton,combobox i am not getting please guide me how to enble particular row on bases of index.
Code :
self.tableWidget = QtGui.QTableWidget(self.centralwidget)
self.tableWidget.setColumnCount(4)
myresult = [(u'sandeep', u'checkbox'), (u'shivaraj', u'checkbox')]
for row, result in enumerate(myresult):
self.tableWidget.insertRow(self.tableWidget.rowCount())
for column, value in enumerate(result):
item = QtGui.QTableWidgetItem(str(value))
if column == 1:
if (value == "textbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(1)
self.tableWidget.setCellWidget(row, column, self.cb)
self.cb.setEnabled(False)#this is for combobox disable
elif (value == "checkbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(2)
self.tableWidget.setCellWidget(row, column, self.cb)
self.cb.setEnabled(False)#this is for combobox disable
self.tableWidget.setItem(row, column, item)
item.setFlags(QtCore.Qt.ItemIsEnabled)#this is for text non editable
self.btn_sell = QtGui.QPushButton('Edit')
self.btn_sell1 = QtGui.QPushButton('Delete')
self.btn_sell1.setEnabled(False)#this for button disble
self.tableWidget.setCellWidget(row, 2, self.btn_sell)
self.tableWidget.setCellWidget(row, 3, self.btn_sell1)
self.btn_sell.clicked.connect(self.edit)
def edit(self):
index = self.tableWidget.currentRow()
print index
self.table = QtGui.QTableWidget()
self.table.setGeometry(QtCore.QRect(220, 100, 881, 100))
self.table.setColumnCount(4)
self.table.setRowCount(1)
self.table.setColumnWidth(0, 120)
self.table.setColumnWidth(1, 500)
self.table.setColumnWidth(2, 120)
self.table.setColumnWidth(3, 120)
indexes = self.tableWidget.currentRow()
widget = self.tableWidget.cellWidget(indexes, 2)
# print widget
if isinstance(widget, QtGui.QComboBox):
h = str(widget.currentText())
if (h == "multiple_input"):
j = "checkbox"
elif (h == "description"):
j = "textbox"
if (j == "textbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(1)
self.table.setCellWidget(0, 2, self.cb)
[enter image description here][1]elif (j == "checkbox"):
self.cb = QtGui.QComboBox()
lis = ("---select---", "description", "multiple_input")
self.cb.addItems(lis)
self.cb.setCurrentIndex(2)
self.table.setCellWidget(0, 2, self.cb)
n = [str(self.tableWidget.item(indexes, 0).text())]
for x in n:
f = ''.join(map(str, x))
self.table.setItem(0, 0, QtGui.QTableWidgetItem(f))
n1 = [str(self.tableWidget.item(indexes, 1).text())]
# print n1
for i in n1:
s = ''.join(i)
self.table.setItem(0, 1, QtGui.QTableWidgetItem(s))
self.li = QtGui.QPushButton("Update")
self.table.setCellWidget(0, 3, self.li)
self.li.clicked.connect(self.edit_data)
self.table.show()
click on below link has image.
Try the above code:
Select row and click on edit button it will take the current index row values into the separate table `where we can edit and update the values.This is the one way i have tried to update the value it works fine.

Setting row Span in QTableView using Python?

I am trying to set rowspan on second column of my QTableView but somehow logically i am missing something. i am only able to get A and B but not C. Plus i am getting warning QTableView::setSpan: span cannot overlap and QTableView::setSpan: single cell span won't be added
My code snippet is:-
startspan = 0
for i, tcname in enumerate(tcfilename):
if tcfilename[i]:
if i > 0:
print '#######################'
print 'startspan = '+str(startspan)+' i = '+str(i)
if tcname == tcfilename[i-1]:
#setSpan (row, column, rowSpan, columnSpan)
print 'if (from_row, till_row) '+str(startspan)+' '+str(i)
table_view.setSpan(startspan, 1, i, 1);
elif tcname != tcfilename[i-1]:
print 'Else no span (from_row, till_row) '+str(startspan)+' '+str(i)
table_view.setSpan(startspan, 1, i, 1);
if i == 1:
startspan = 0
else:
startspan = i
else:
break
Did this with simple two line code below
for toRow, tcname in enumerate(tcfilename):
table_view.setSpan(tcfilename.index(tcname), 1, tcfilename.count(tcname), 1)
I made a nifty little function to solve this.. Had recursion but then optimized it without recursion.. feed it a table and a data set
def my_span_checker(self, my_data, table):
for i in range(len(my_data)):
my_item_count = 0
my_label = table.item(i, 0).text()
for j in range(len(my_data)):
if table.item(j, 0).text() == my_label:
my_item_count += 1
if my_item_count != 1:
table.setSpan(i, 0, my_item_count, 1)

How do I use the values from a QComboBox to edit a database (PyQt, Sqlite3, Python)

I have a system of editing data when the data is just text. I have added a combobox, and I want the data from that to be editable too. When the combobox is changed, and the edit button is clicked, I want the data to update in the database. How do I do that?
Here is the code for one of my outputted queries.
def click_btn_lessons(self):
self.screen_name = "lessons"
self.page_constants()
self.cur.execute("SELECT StudentID FROM StudentProfile")
students_students = self.cur.fetchall()
self.cur.execute("SELECT StudentID FROM Lessons")
students_lessons = self.cur.fetchall()
self.cur.execute("""SELECT s.studentID, s.FullName, l.LessonDate, r.RoadExerciseName, e.Rating
FROM StudentProfile s
LEFT JOIN Lessons l ON s.StudentID=l.StudentID
LEFT JOIN LessonExercises e ON l.LessonID=e.LessonID
LEFT JOIN RoadExerciseInfo r ON r.RoadExerciseID=e.RoadExerciseID
""")
self.all_data = self.cur.fetchall()
"""This section just removes black records"""
for student in students_students:
if student not in students_lessons:
for item in self.all_data:
if item[0] == student[0]:
self.all_data.remove(item)
self.table.setRowCount(len(self.all_data))
self.tableFields = ['check','Full name','Lesson date', 'Exercise', "Rating"]
self.table.setColumnCount(len(self.tableFields))
self.table.setHorizontalHeaderLabels(self.tableFields)
self.checkbox_list = []
for i, item in enumerate(self.all_data):
FullName = QtGui.QTableWidgetItem(str(item[1]))
LessonDate = QtGui.QTableWidgetItem(str(item[2]))
RoadExerciseName = QtGui.QTableWidgetItem(str(item[3]))
#This is the combobox and its items
Rating = QtGui.QComboBox()
for num in range(5):
Rating.addItem(str(num))
self.table.setItem(i, 1, FullName)
self.table.setItem(i, 2, LessonDate)
self.table.setItem(i, 3, RoadExerciseName)
#here is how the combobox isadded to the table
self.table.setCellWidget(i, 4, Rating)
#the checkbox items have to be ticked for them to be passed to the edit function
chkBoxItem = QtGui.QTableWidgetItem()
chkBoxItem.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
chkBoxItem.setCheckState(QtCore.Qt.Unchecked)
self.checkbox_list.append(chkBoxItem)
self.table.setItem(i, 0, self.checkbox_list[i])
FullName.setFlags(FullName.flags() & ~Qt.ItemIsEditable)
RoadExerciseName.setFlags(RoadExerciseName.flags() & ~Qt.ItemIsEditable)
self.name = None
self.changed_items = []
self.table.itemChanged.connect(self.log_change)
def log_change(self, item):
self.table.blockSignals(True)
item.setBackgroundColor(QtGui.QColor("red"))
self.table.blockSignals(False)
self.changed_items.append(item)
def click_btn_edit(self):
print("Updating")
for item in self.changed_items:
self.table.blockSignals(True)
item.setBackgroundColor(QtGui.QColor("white"))
self.table.blockSignals(False)
text, col, row = item.text(), item.column(), item.row()
new_data = self.all_data
for i, record in enumerate(self.all_data):
if i == row:
if self.checkbox_list[i].checkState() == QtCore.Qt.Checked:
#Below adds the new data to the database.
self.cur.execute("""UPDATE {0} SET {1} = ? WHERE {2} = ?""".format(self.table_edit_name,self.columnList[col],self.field_ID),(text,record[0],))
"""self.field_ID is the field name for the primary key, record[0]. This ensures data is entered in the correct record.
columnList is a list of columns in the record, in order.
self.table_edit_name is the name of the table involved."""

Categories