I have a simple app with two QListWidgets
I want to:
drag and drop between them
rearrange the order within them.
The problem: When I attempt to rearrange the order within one of the QListWidgets, the QListItem disappears.
Here is a small example (I'm using python3)
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtCore import Qt
class DragWidget(QtWidgets.QListWidget) :
def __init__(self,parent,total=None) :
super(DragWidget,self).__init__(parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Minimum)
#Want horizontal listwidgets.
self.setFlow(QtWidgets.QListView.Flow.LeftToRight)
#Here's the attempt to configure dragging.
self.setDragEnabled(True)
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
self.setDropIndicatorShown(True)
self.setDefaultDropAction(Qt.MoveAction)
self.viewport().setAcceptDrops(True)
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.setResizeMode(QtWidgets.QListView.ResizeMode.Adjust)
self.setSpacing(2)
self.setFixedHeight(50)
#An attempt to overload the dragEnterEvent
def dragEnterEvent(self,event) :
#Use the InternalMove if the source = the drop site
if (event.source() is self):
self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
else :
#And regular ol' DragDrop if not.
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
super().dragEnterEvent(event)
class DragDemo(QtWidgets.QDialog) :
def __init__(self,parent=None,*args,**kwargs) :
super().__init__()
layout = QtWidgets.QGridLayout()
groupbox = QtWidgets.QGroupBox("Display Columns")
groupbox.setLayout(layout)
showlist = DragWidget(groupbox)
options = ['type','name','timestamp']
itemlist = []
for option in options :
item = QtWidgets.QListWidgetItem(option,showlist)
itemlist.append(item)
layout.addWidget(showlist,0,0)
hidelist = DragWidget(groupbox)
layout.addWidget(hidelist,1,0)
vlayout = QtWidgets.QVBoxLayout()
vlayout.addWidget(groupbox)
self.setLayout(vlayout)
self.show()
app = QApplication(sys.argv)
demo = DragDemo()
demo.show()
sys.exit(app.exec_())
Who out there can tell me what I'm doing wrong?
Any, and all help is appreciated.
Related
I am trying to code an application that will allow the user to view a list of Tag IDs, as well as its description, and allow the user to check off each Tag ID that they would like to import data from. At this point I am working on developing the UI only.
The code below worked and would show the application window until I added the itemChanged function & connection. Now, when I run this code, only the print statement from the new function will show. The window never shows and the entire application promptly exits (see image for outcome of running script).
Additionally, you'll notice that we get the checkState of each type of item - I only want the checkState of the Tag ID.
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QTableView, QHeaderView, QVBoxLayout, QAbstractItemView
from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtGui import QStandardItemModel, QStandardItem
class myApp(QWidget):
def __init__(self):
super().__init__()
self.resize(1000, 500)
mainLayout = QVBoxLayout()
tagIDs = ('Tag_1', 'Tag_2', 'Tag_3', 'Tag_4', 'Tag_5')
descriptions = ('Description_1', 'Description_2', 'Description_3', 'Description_4', 'Description_5')
model = QStandardItemModel(len(tagIDs), 2)
model.itemChanged.connect(self.itemChanged)
model.setHorizontalHeaderLabels(['Tag IDs', 'Description'])
for i in range(len(tagIDs)):
item1 = QStandardItem(tagIDs[i])
item1.setCheckable(True)
item2 = QStandardItem(descriptions[i])
model.setItem(i, 0, item1)
model.setItem(i, 1, item2)
filterProxyModel = QSortFilterProxyModel()
filterProxyModel.setSourceModel(model)
filterProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
filterProxyModel.setFilterKeyColumn(1)
searchField = QLineEdit()
searchField.setStyleSheet('font-size: 20px; height: 30px')
searchField.textChanged.connect(filterProxyModel.setFilterRegExp)
mainLayout.addWidget(searchField)
table = QTableView()
table.setStyleSheet('font-size: 20px;')
table.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
table.setModel(filterProxyModel)
table.setEditTriggers(QAbstractItemView.NoEditTriggers)
mainLayout.addWidget(table)
self.setLayout(mainLayout)
def itemChanged(self, item):
print("Item {!r} checkState: {}".format(item.text(), item.checkState()))
def main():
app = QApplication(sys.argv)
myAppControl = myApp()
myAppControl.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Header settings that depend on the model must always be set when a model is set.
Move table.setModel(filterProxyModel) right after the creation of the table or, at least, before table.horizontalHeader().setSectionResizeMode (the vertical setSectionResizeMode() is generic for the whole header and doesn't cause problems).
My code is supposed to add data to a two column table when "add" button is clicked. The problem is that when the "add" button is clicked, only the empty row is being added. Can someone please let me know what is wrong? Below is the part of the code that adds data1 and data2 to a table on the right side of the layout. The function add_entry is where the data is being added.
# Import dependencies
from PyQt5.QtWidgets import (QWidget, QApplication, QTableWidget, QTableWidgetItem,QHBoxLayout, QVBoxLayout, QHeaderView, QPushButton, QDialog,
QLabel, QFileDialog, QMainWindow, QAction, QLineEdit)
from PyQt5.Qt import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtChart import QChart, QChartView, QLineSeries
import sys
import pandas as pd
import math
# ------------------------------------------------------UI-main----------------------------------------------------------------------------------
# Creates a QApplication instance
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.items=0
# Creates table on the left size
self.table_l = QTableWidget()
self.table_l.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# Creates layout object for the right side
self.layoutRight = QVBoxLayout()
# Creates chart widget
self.chartView = QChartView()
# Smooths the edge of the chart
self.chartView.setRenderHint(QPainter.Antialiasing)
# Creates table on the right size
self.table_r = QTableWidget()
self.table_r.setColumnCount(2)
# self.table_r.setRowCount()
self.table_r.setHorizontalHeaderLabels(('Data1', 'Data2'))
self.table_r.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.table_r.setMaximumSize(600, 300)
self.lineEditData1 = QLineEdit()
self.lineEditData2 = QLineEdit()
# Create push buttons
self.buttonAdd = QPushButton('Add')
self.buttonClear = QPushButton('Clear')
self.buttonQuit = QPushButton('Quit')
self.buttonAdd.setEnabled(False)
self.layoutRight.setSpacing(10)
self.layoutRight.addWidget(self.table_r, 50)
self.layoutRight.addWidget(QLabel('data1'))
self.layoutRight.addWidget(self.lineEditData1)
self.layoutRight.addWidget(QLabel('data2'))
self.layoutRight.addWidget(self.lineEditData2)
self.layoutRight.addWidget(self.buttonAdd)
self.layout = QHBoxLayout()
self.layout.addWidget(self.table_l, 50)
self.setLayout(self.layout)
self.layout.addLayout(self.layoutRight, 50)
# Connect button to function functions
self.buttonQuit.clicked.connect(lambda:app.quit())
self.buttonAdd.clicked.connect(self.add_entry)
self.buttonClear.clicked.connect(self.reset_table)
self.lineEditData1.textChanged[str].connect(self.check_disable)
self.lineEditData2.textChanged[str].connect(self.check_disable)
def add_entry(self):
Data1 = self.lineEditData1.text()
Data2 = self.lineEditData2.text()
try:
Data1Item = QTableWidgetItem(int(Data1))
Data2Item = QTableWidgetItem(float(Data2))
Data2Item.setTextAlignment(Qt.AlignRight | Qt.AlignCenter)
self.table_r.insertRow(self.items)
self.table_r.setItem(self.items, 0, Data1Item)
self.table_r.setItem(self.items, 1, Data2Item)
self.items +=1
# after passing the item, clear the field by entering an empty string
self.lineEditData1.setText('')
self.lineEditData2.setText('')
except ValueError:
pass
# Creates main window object instance
class MainWindow(QMainWindow):
def __init__(self, widget):
super().__init__()
self.setWindowTitle('test')
self.resize(1200, 1200)
self.menuBar = self.menuBar()
self.fileMenu = self.menuBar.addMenu('File')
# import wind speed data
importAction = QAction('Open File', self)
importAction.setShortcut('Ctrl+O')
# exit action
exitAction = QAction('Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(lambda: app.quit())
self.fileMenu.addAction(importAction)
self.fileMenu.addAction(exitAction)
self.setCentralWidget(widget)
if __name__ =='__main__':
# don't auto scale when drag app to a different monitor
#QGuiApplication.setHightDpiScaleFactorRoundingPolicy(Qt.HightDpiScaleFactorRoundingPolicy.PassThrough)
app = QApplication(sys.argv)
w = MyApp()
demo = MainWindow(w)
demo.show()
try:
sys.exit(app.exec())
except SystemExit:
print('Closing window...')
The objective of exceptions is not to hide errors but to know how to prevent them, so they must be as small as possible so as not to hide other errors. In this case, QTableWidgetItem accepts a string as an argument and not numerical values, therefore an exception is thrown preventing the code that adds the items from being executed. The solution is to use the setData() method of the QTableWidgetItem:
def add_entry(self):
data1 = self.lineEditData1.text()
data2 = self.lineEditData2.text()
try:
value1 = int(data1)
value2 = float(data2)
except ValueError:
print("failed conversion")
return
else:
data1_item = QTableWidgetItem()
data1_item.setData(Qt.DisplayRole, value1)
data2_item = QTableWidgetItem()
data2_item.setData(Qt.DisplayRole, value2)
data2_item.setTextAlignment(Qt.AlignRight | Qt.AlignCenter)
row = self.table_r.rowCount()
self.table_r.insertRow(row)
self.table_r.setItem(row, 0, data1_item)
self.table_r.setItem(row, 1, data2_item)
self.lineEditData1.clear()
self.lineEditData2.clear()
I found a similar question on this.
Keep the selection after filtering a QTableView with a QSortFilterProxyModel
But it is about C++ QT, I tried myself many times, but I still did not pull it off in PyQt5 or PySide6?
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QTableView, QHeaderView, QVBoxLayout
from PyQt5.QtCore import Qt, QSortFilterProxyModel
from PyQt5.QtGui import QStandardItemModel, QStandardItem
class AppDemo(QWidget):
def __init__(self):
super().__init__()
self.resize(1200, 1000)
mainLayout = QVBoxLayout()
# companies = ('Apple', 'Facebook', 'Google', 'Amazon', 'Walmart', 'Dropbox', 'Starbucks', 'eBay', 'Canon')
companies = [f'company_{i}' for i in range(200)]
model = QStandardItemModel(len(companies), 1)
model.setHorizontalHeaderLabels(['Company'])
for row, company in enumerate(companies):
item = QStandardItem(company)
model.setItem(row, 0, item)
filter_proxy_model = QSortFilterProxyModel()
filter_proxy_model.setSourceModel(model)
filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
filter_proxy_model.setFilterKeyColumn(0)
search_field = QLineEdit()
search_field.textChanged.connect(filter_proxy_model.setFilterRegExp)
mainLayout.addWidget(search_field)
table = QTableView()
table.setStyleSheet('font-size: 35px;')
table.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setModel(filter_proxy_model)
mainLayout.addWidget(table)
self.setLayout(mainLayout)
app = QApplication(sys.argv)
demo = AppDemo()
demo.show()
sys.exit(app.exec_())
I found a solution myself. I switched to use a QListWidget instead. But the same logic also can be applied to QListView. Below is the code.
Below is the core part, it is a slot that is connected to a line edit. Doing this way, the efficiency is good especially when you have many items as it doesn' use QListWidget.clear() when lineedit input changes, it just hide the items behind the curtain.
# deal with search file
def onFileTextChanged(self, text):
search_items = self.listWidget.findItems(text, Qt.MatchContains)
for index in range(self.listWidget.count()):
item = self.listWidget.item(index)
if item in search_items:
item.setHidden(False)
else:
item.setHidden(True)
I am trying to write a PyQt5 widget which will be a basic preset manager inside an application. I am trying to use the QTreeWidget to display the structure of each type. I have this weird issue, where I see an extra QTreeWidget with a column named "1" outside my QTreeWidget. I have attached a picture (sorry, I could not take a screenshots) and the code. I have also noticed that this does not happen if I don't use classes.
Here's my test code showing the problem:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MyTree(QtWidgets.QTreeWidget):
def __init__(self, parent = None):
super(self.__class__, self).__init__(parent)
boxLayout = QtWidgets.QVBoxLayout()
treeWidget = QtWidgets.QTreeWidget()
treeWidget.setHeaderLabels(['Preset Name'])
treeWidget.setColumnCount(1)
items = []
for i in range(10):
items.append(QtWidgets.QTreeWidgetItem(["item {0}".format(i)]))
treeWidget.insertTopLevelItems(0, items)
boxLayout.addWidget(treeWidget)
self.setLayout(boxLayout)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MyTree()
w.show()
sys.exit(app.exec_())
That's what I see:
My best guess that it's the layout that's causing it, because if I just create the QTreeWidget like so:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
app = QtWidgets.QApplication(sys.argv)
treeWidget = QtWidgets.QTreeWidget()
treeWidget.setHeaderLabels(['Preset Name'])
treeWidget.setColumnCount(1)
items = []
for i in range(10):
items.append(QtWidgets.QTreeWidgetItem(["item {0}".format(i)]))
treeWidget.insertTopLevelItems(0, items)
treeWidget.show()
sys.exit(app.exec_())
That's what I see:
Any ideas how to get it showing like the second image, from a class?
Any help is greatly appreciated
The error is simple: Your window (MyTree) is an empty QTreeWidget that has another QTreeWidget inside, so "1" belongs to the empty QTreeWidget.
There are 2 possible solutions:
No user as base class to QTreeWidget
class MyTree(QtWidgets.QWidget):
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
treeWidget = QtWidgets.QTreeWidget()
treeWidget.setHeaderLabels(["Preset Name"])
treeWidget.setColumnCount(1)
items = []
for i in range(10):
items.append(QtWidgets.QTreeWidgetItem(["item {0}".format(i)]))
treeWidget.insertTopLevelItems(0, items)
boxLayout = QtWidgets.QVBoxLayout(self)
boxLayout.addWidget(treeWidget)
Do not nest a new QTreeWidget
class MyTree(QtWidgets.QTreeWidget):
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
self.setHeaderLabels(["Preset Name"])
self.setColumnCount(1)
items = []
for i in range(10):
items.append(QtWidgets.QTreeWidgetItem(["item {0}".format(i)]))
self.insertTopLevelItems(0, items)
I am migrating some code from pyGObject to pyQT4 and I make use of set_progress_fraction as a means to indication the percentage of the MAXIMUM the inputted value is.
I am trying to find an equivalent in pyQT4 but I am failing.
QLineEdit does have a paint method so is the only real way to "paint" the LineEdit?
--edit--
example of the gtk equiv:
& snippit of my present pyGObject code
def on_entry_change(self,widget,*args):
try:
tmp = float(widget.get_text())
tmp = (tmp- widget.min_bin)/(widget.max_bin - widget.min_bin)
widget.set_progress_fraction(tmp)
except:
return
I think you want to connect the value in the QLineEdit to a method that modifies the progress bar. This might help:
from __future__ import division
import sys
from PyQt4.QtCore import (Qt, SIGNAL)
from PyQt4.QtGui import (QApplication, QDialog, QLineEdit,
QVBoxLayout, QProgressBar, QWidget, QLabel)
class Form(QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.barA = QProgressBar()
self.maximum = 200
self.label1 = QLabel('Maximum = 200')
self.seqM = QLineEdit("num")
layoutO = QVBoxLayout() #set overal layout
layoutO.addWidget(self.label1)
layoutO.addWidget(self.barA)
layoutO.addWidget(self.seqM)
self.setLayout(layoutO)
self.connect(self.seqM, SIGNAL("returnPressed()"), self.updatebar)
def updatebar(self):
try:
currval = float(self.seqM.text())
except:
print 'enter float or integer only'
self.track = (currval/self.maximum)*100
self.barA.setValue(self.track)
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()`