How can I edit the label of a horizontal header in a qtablewidget by double clicking on it?
What I have so far but does not work. So when someone doubleclicks the top header, a linedit pops up allows for text and then resets the label.
import sys
from PyQt4 import QtGui,QtCore
import generate_file
class WebGetMain(QtGui.QMainWindow,generate_file):
def __init__(self,parent=None):
super(WebGetMain,self).__init__(parent)
self.setupUi(self)
#set these values customizable before user populates the table
self.row_count = 20
self.col_count = 20
#setup actual cols,rows
self.web_get_table.setRowCount(self.row_count)
self.web_get_table.setColumnCount(self.col_count)
#allow horizontal header to be altered
#create and initialize linedit
self.linedit = QtGui.QLineEdit(parent=self.web_get_table.viewport())
self.linedit.setAlignment(QtCore.Qt.AlignTop)
self.linedit.setHidden(True)
self.linedit.editingFinished.connect(self.doneEditing)
#connect double click action on header
self.web_get_table.connect(self.web_get_table.horizontalHeader(),QtCore.SIGNAL('sectionDoubleClicked(int)'),self.on_header_doubleClicked)
#setup vertical scrollbars for adding rows
self.vBar = self.web_get_table.verticalScrollBar()
self._vBar_lastVal = self.vBar.value()
#initialize cols,rows
for column in range(0, 2):
for row in range(0, 3):
print row, column
item = QtGui.QTableWidgetItem()
self.web_get_table.setItem(row, column, item)
#scrollbar value signal to detect for scrolling
self.vBar.valueChanged.connect(self.scrollbarChanged)
def scrollbarChanged(self, val):
#initialize scrollbar
bar = self.vBar
minVal, maxVal = bar.minimum(), bar.maximum()
avg = (minVal+maxVal)/2
rowCount = self.web_get_table.rowCount()
# scrolling down
if val > self._vBar_lastVal and val >= avg:
self.web_get_table.insertRow(rowCount)
# scrolling up
elif val < self._vBar_lastVal:
lastRow = rowCount-20
empty = True
for col in xrange(self.web_get_table.columnCount()):
item = self.web_get_table.item(lastRow, col)
if item and item.text():
empty=False
break
if empty:
#remove rows when scrolling up
self.web_get_table.removeRow(lastRow)
self._vBar_lastVal = val
def doneEditing(self):
self.linedit.blockSignals(True)
self.linedit.setHidden(True)
oldname = self.web_get_table.model().dataset.field(self.sectionedit)
newname = str(self.linedit.text())
self.web_get_table.model().dataset.changeFieldName(oldname,newname)
self.linedit.setText('')
self.web_get_table.setCurrentIndex(QtCore.QModelIndex())
def on_header_doubleClicked(self,item):
self.linedit.setText(self.web_get_table.model().field(item).name)
self.linedit.setHidden(False)
self.linedit.blockSignals(False)
self.linedit.setFocus()
self.linedit.selectAll()
self.sectionedit = item
def main():
app = QtGui.QApplication(sys.argv)
app.setStyle(QtGui.QStyleFactory.create("Plastique"))
main_window = WebGetMain()
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
To be honest, I couldn't figure out what you're trying to do with that QLineEdit. But as far as I can see, you are halfway there. Using sectionDoubleClicked signal of the horizontalHeader() is a good start. But the rest is a big question mark for me.
All you need to do is this: Get the header item with horizontalHeaderItem(index) and use text to get the value or setText to set the new value.
And you might consider QInputDialog.getText to obtain the new value from the user.
Here is a minimal example that shows this:
import sys
from PyQt4 import QtGui
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.table = QtGui.QTableWidget(5,5)
self.table.setHorizontalHeaderLabels(['1', '2', '3', '4', '5'])
self.table.setVerticalHeaderLabels(['1', '2', '3', '4', '5'])
self.table.horizontalHeader().sectionDoubleClicked.connect(self.changeHorizontalHeader)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.table)
self.setLayout(layout)
def changeHorizontalHeader(self, index):
oldHeader = self.table.horizontalHeaderItem(index).text()
newHeader, ok = QtGui.QInputDialog.getText(self,
'Change header label for column %d' % index,
'Header:',
QtGui.QLineEdit.Normal,
oldHeader)
if ok:
self.table.horizontalHeaderItem(index).setText(newHeader)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = MyWindow()
main.show()
sys.exit(app.exec_())
I found this answer from mechsin on QtCentre and it worked for me:
http://www.qtcentre.org/threads/12835-How-to-edit-Horizontal-Header-Item-in-QTableWidget
See last comment in the thread. In summary, it creates a QLineEdit in the viewport of the table header. A slot for double-clicking the header shows the line edit in the proper position and width to cover the header section. A slot for finishing the line edit hides the line edit and captures its text value for the header item.
Related
I have a QListWidget with custom widgets. I am seeing an issue where if I do the following:
add two items
Remove the second item
Add another item
then the first item's widget contents disappears until I either resize the window, or add a third item.
import sys
from PySide.QtGui import *
from PySide.QtCore import *
class StagingWidget(QGroupBox):
def __init__(self,parent=None):
#QWidget.__init__(self,parent)
super(StagingWidget,self).__init__()
self.itemWidgets = list()
self.count = 1
self.createUi()
def createUi(self):
self.widget_layout=QVBoxLayout(self)
self.list_widget=QListWidget()
self.setFixedWidth(450)
self.setFixedHeight(600)
self.list_widget.setStyleSheet("QListWidget::item:selected{background:lightblue}")
self.widget_layout.addWidget(self.list_widget)
self.buttonHLayout = QHBoxLayout()
self.add = QPushButton("Add Item")
self.add.clicked.connect(self.addListItem)
self.buttonHLayout.addWidget(self.add)
self.widget_layout.addLayout(self.buttonHLayout)
def addListItem(self):
itemN = QListWidgetItem()
widget = QWidget()
Button1 = QPushButton(str(self.count))
Button2 = QPushButton("Remove")
Button2.clicked.connect(lambda item=itemN:self.removeJob(item))
widgetLayout = QHBoxLayout()
widgetLayout.addWidget(Button1)
widgetLayout.addWidget(Button2)
widget.setLayout(widgetLayout)
itemN.setSizeHint(widget.sizeHint())
self.itemWidgets.append(widget)
self.list_widget.addItem(itemN)
self.list_widget.setItemWidget(itemN, self.itemWidgets[-1])
self.count = self.count + 1
def removeJob(self,item):
print("Removing Job")
row = self.list_widget.indexFromItem(item).row()
self.list_widget.takeItem(row)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = StagingWidget()
widget.show()
sys.exit(app.exec_())
Example:
Add 1 item:
Add another item:
Remove item 2:
Add another item(You can see widget of item 1 disappear):
Add another item(Widget of item 1 reappears):
This happens every single time I do the above steps.
I'm trying to create a layout where the user can click a combination of buttons, each button's click will add a 1 or a 0 to a certain position in a list which is the actual input I'd like to get out of it.
However, I don't know how to manage a cluster of buttons, there are 48 buttons and managing them all individually is the antithesis of DRY.
Here's an example attempt:
num_buttons = 48
press_list = [None]*len(num_buttons)
button_list = list()
for button in range(num_buttons):
some_btn = QtWidgets.QPushButton(SomeDialog)
some_btn.setGeometry(QtCore.QRect(70, 90, 141, 28))
some_btn.setObjectName("button_%s" % (button,))
some_btn.clicked.connect(self.button_pressed(button))
def button_pressed(self, button_num):
if press_list[button_num] == 1:
press_list[button_num] = 0
else:
press_list[button_num] = 1
(clicks turn buttons blue), is it possible to have a set geometry through the Qt designer and still do something like this, or will I have to calculate the setGeometry positions and add the buttons through the code?
If you want to pass an additional argument to the slots you can use partial as shown below:
import sys
from functools import partial
from PyQt5 import QtWidgets
QSS = """
QToolButton::checked{
background-color: blue
}
"""
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.listA = [0 for _ in range(24)]
self.listB = [0 for _ in range(24)]
lay = QtWidgets.QVBoxLayout(self)
hlay1 = QtWidgets.QHBoxLayout()
hlay2 = QtWidgets.QHBoxLayout()
lay.addLayout(hlay1)
lay.addLayout(hlay2)
for i, val in enumerate(self.listA):
button = QtWidgets.QToolButton()
button.setCheckable(True)
hlay1.addWidget(button)
button.clicked.connect(partial(self.callbackA, i))
button.setStyleSheet(QSS)
for i, val in enumerate(self.listB):
button = QtWidgets.QToolButton()
button.setCheckable(True)
hlay2.addWidget(button)
button.clicked.connect(partial(self.callbackB, i))
button.setStyleSheet(QSS)
def callbackA(self, index, state):
self.listA[index] = 1 if state else 0
print("listA: ")
print(self.listA)
def callbackB(self, index, state):
self.listB[index] = 1 if state else 0
print("listB: ")
print(self.listB)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I have a simple QTreeWidget :
self.treeWidget = QTreeWidget(self)
col = ("Status", "Name")
self.treeWidget.setColumnCount(2)
self.treeWidget.setHeaderLabels(col)
witch I populate using :
wid = Custom_Widget()
item = QTreeWidgetItem(self.treeWidget)
item.setText(1, "some string")
item.treeWidget().setItemWidget(item, 0, wid)
I can easly acces the text column by using :
root = self.treeWidget.invisibleRootItem()
it = root.child(2) # for example the third item
it.setText(1, "Edited")
what i need is to edit the costum_widget in column 0, so how can i access it or invoke his methods ?
I resolved the issue by keeping a list of all the costum_widgets that i create, my code would be :
from CustomWidgets import Custom_Widget
from PyQt5.QtWidgets import *
import sys
class MyExample(QWidget):
def __init__(self):
super().__init__()
self.treeWidget = QTreeWidget(self)
self.group_wid = [] # my list of widgets
col = ("Status", "Name")
self.treeWidget.setColumnCount(2)
self.treeWidget.setHeaderLabels(col)
self.populate_tree()
self.edit_tree()
self.show()
def populate_tree(self):
for i in range(10):
wid = Custom_Widget()
self.group_wid.append(wid) # each time i instantiate a Custom_Widget i'll add it to my list of widgets
item = QTreeWidgetItem(self.treeWidget)
item.setText(1, "some string")
item.treeWidget().setItemWidget(item, 0, wid)
def edit_tree(self):
root = self.treeWidget.invisibleRootItem()
it = root.child(2) # for example the third item
it.setText(1, "Edited")
self.group_wid[2].my_method() # i can easily edit my widgets that are in the QTreeWidget
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyExample()
app.exec_()
Guess the QtreeWidgetItem only hold a reference to the widgets that are contained.
what I want to do is to change the color of a QTableWidget item, when I hover with the mouse over the item of my QTableWidget.
Firstly, the table widget needs to have mouse-tracking switched on to get the hover events.
Secondly, we need to find some signals that tell us when the mouse enters and leaves the table cells, so that the background colours can be changed at the right times.
The QTableWidget class has the cellEntered / itemEntered signals, but there is nothing for when the mouse leaves a cell. So, we will need to create some custom signals to do that.
The TableWidget class in the demo script below sets up the necessary cellExited / itemExited signals, and then shows how everything can be hooked up to change the item background when hovering with the mouse:
from PyQt4 import QtGui, QtCore
class TableWidget(QtGui.QTableWidget):
cellExited = QtCore.pyqtSignal(int, int)
itemExited = QtCore.pyqtSignal(QtGui.QTableWidgetItem)
def __init__(self, rows, columns, parent=None):
QtGui.QTableWidget.__init__(self, rows, columns, parent)
self._last_index = QtCore.QPersistentModelIndex()
self.viewport().installEventFilter(self)
def eventFilter(self, widget, event):
if widget is self.viewport():
index = self._last_index
if event.type() == QtCore.QEvent.MouseMove:
index = self.indexAt(event.pos())
elif event.type() == QtCore.QEvent.Leave:
index = QtCore.QModelIndex()
if index != self._last_index:
row = self._last_index.row()
column = self._last_index.column()
item = self.item(row, column)
if item is not None:
self.itemExited.emit(item)
self.cellExited.emit(row, column)
self._last_index = QtCore.QPersistentModelIndex(index)
return QtGui.QTableWidget.eventFilter(self, widget, event)
class Window(QtGui.QWidget):
def __init__(self, rows, columns):
QtGui.QWidget.__init__(self)
self.table = TableWidget(rows, columns, self)
for column in range(columns):
for row in range(rows):
item = QtGui.QTableWidgetItem('Text%d' % row)
self.table.setItem(row, column, item)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table)
self.table.setMouseTracking(True)
self.table.itemEntered.connect(self.handleItemEntered)
self.table.itemExited.connect(self.handleItemExited)
def handleItemEntered(self, item):
item.setBackground(QtGui.QColor('moccasin'))
def handleItemExited(self, item):
item.setBackground(QtGui.QTableWidgetItem().background())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window(6, 3)
window.setGeometry(500, 300, 350, 250)
window.show()
sys.exit(app.exec_())
You can achieve your goal pretty easily using the proper signals as proved by the following simple code:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class TableViewer(QMainWindow):
def __init__(self, parent=None):
super(TableViewer, self).__init__(parent)
self.table = QTableWidget(3, 3)
for row in range (0,3):
for column in range(0,3):
item = QTableWidgetItem("This is cell {} {}".format(row+1, column+1))
self.table.setItem(row, column, item)
self.setCentralWidget(self.table)
self.table.setMouseTracking(True)
self.current_hover = [0, 0]
self.table.cellEntered.connect(self.cellHover)
def cellHover(self, row, column):
item = self.table.item(row, column)
old_item = self.table.item(self.current_hover[0], self.current_hover[1])
if self.current_hover != [row,column]:
old_item.setBackground(QBrush(QColor('white')))
item.setBackground(QBrush(QColor('yellow')))
self.current_hover = [row, column]
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
tv = TableViewer()
tv.show()
sys.exit(app.exec_())
You may be interested in other signals too, especially itemEntered. However, if you want total control over the editing and display of items then using delegates (via the QTableWidget.setItemDelegate method) is strongly recommended.
UPDATE:
sorry, I had forgotten the second part of the problem i.e. what happens when the mouse exits a cell. Even then the problem can be solved easily without using events. See the updated code, please.
There are no events based on QTableWidgetItem, but you can do this:
reimplement the mouseMoveEvent() of QTableWidget, you can get the mouse position;
use itemAt() method to get the item under your mouse cursor;
customize your item;
This may simalute what you want.
I know this is old but wanted to update a couple of parts to it as I came across this page looking for a similar solution. This has a couple of parts to it, one is similar to the above but avoids the NoneType error if the cell is empty. Additionally, it will change the color of the highlighted cell, but also update a tooltip for the cell to display the contents of the cell in a tooltip. Nice if you have cells with runoffs 123...
Sure it could be cleaned up a bit, but works for PyQt5. Cheers!
def cellHover(self, row, column):
item = self.My_Table1.item(row, column)
old_item = self.My_Table1.item(self.current_hover[0], self.current_hover[1])
if item is not None:
if self.current_hover != [row,column]:
text = item.text()
if text is not None:
self.My_Table1.setToolTip(text)
item.setBackground(QBrush(QColor('#bbd9f7')))
old_item.setBackground(QBrush(QColor('white')))
self.current_hover = [row, column]
This is a method to copy a word from my textedit, and set it to a new line into my tableview. What i need is: How to change the color of the word that i selected into my textedit? The name of my text edit is "editor", when i copy the word i need change the color of this word, and i dont know how to do it. help please :). With examples please ~~
def addLineTable(self):
row = self.model.rowCount() #create a line into my tableview
self.model.insertRows(row)
column = 0
index = self.model.index(row, column)
tableView = self.TABLE
tableView.setFocus()
tableView.setCurrentIndex(index)
cursor = self.editor.textCursor()
textSelected = cursor.selectedText() #set text to cursor
self.model.setData(index, QVariant(textSelected)) #set text to new tableview line
If I understand your question correctly, you just want to change the color of the text, right?
You can do that by assigning StyleSheets with css to your QWidgets, documentation here.
A sample below:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
self._offset = 200
self._closed = False
self._maxwidth = self.maximumWidth()
self.widget = QtGui.QWidget(self)
self.listbox = QtGui.QListWidget(self.widget)
self.editor = QtGui.QTextEdit(self)
self.editor.setStyleSheet("QTextEdit {color:red}")
layout = QtGui.QHBoxLayout(self)
layout.addWidget(self.widget)
layout.addWidget(self.editor)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.move(500, 300)
window.show()
sys.exit(app.exec_())
Edit
Or you can setStyleSheet to all your QTextEdit, try this:
......
app = QtGui.QApplication(sys.argv)
app.setStyleSheet("QTextEdit {color:red}")
......
You're already getting the QTextCursor. All you need to do is apply a format (QTextCharFormat) to this cursor and the selected text will be formatted accordingly:
def addLineTable(self):
row = self.model.rowCount() #create a line into my tableview
self.model.insertRows(row)
column = 0
index = self.model.index(row, column)
tableView = self.TABLE
tableView.setFocus()
tableView.setCurrentIndex(index)
cursor = self.editor.textCursor()
# get the current format
format = cursor.charFormat()
# modify it
format.setBackground(QtCore.Qt.red)
format.setForeground(QtCore.Qt.blue)
# apply it
cursor.setCharFormat(format)
textSelected = cursor.selectedText() #set text to cursor
self.model.setData(index, QVariant(textSelected)) #set text to new tableview line