PyQt4 Combobox Not Updating After Initial Selection - python

Combobox dbSelection does not update to apply in code when selecting table names from sqlite to display in combobox tabSelection.
It also takes a significant number of seconds to load the directory dialog window once the button is clicked.
I would also like to ensure that all tables within a database are listed in the tabSelection combobox.
The code is as follows and is associated with a Qt Designer file:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
import qcDbWidget2
import os
import sqlite3
class MainDialog(QWidget, qcDbWidget2.Ui_qcQueryWidget):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.connect(self.dbDirSelect, SIGNAL("clicked()"), self.getDirName)
def getDirName(self):
existDir = QFileDialog.getExistingDirectory(self)
dbDir = str(existDir)
self.dbDirDisplay.setText(dbDir)
dbFileList = []
for root, dirs, files in os.walk(dbDir):
for file in files:
if file.endswith('.db'):
dbFileList.append(file)
self.dbSelection.addItems(dbFileList)
tableList = []
self.dbSelection.update()
dbName = str(self.dbSelection.currentText())
dbPath = str(dbDir + '\\' + dbName)
conn = sqlite3.connect(dbPath)
c = conn.cursor()
res = c.execute("SELECT name FROM sqlite_master WHERE type='table';")
self.tabSelection.clear()
for name in res:
tableList.append(name[0])
print(name[0])
for name in tableList:
self.tabSelection.addItems(tableList)
app = QApplication(sys.argv)
form = MainDialog()
form.show()
app.exec_()

Have you considered to use QTableView instead of QTableList?
Qt uses MVC which methods are way faster than doing it by hand.
In this case, it's pretty simple.
You create a model:
my_model = QStringListModel()
You can then save data to my_model:
my_list = ["toto", "tutu", "tata"]
my_model.setStringList(my_list)
Once you have a QTableView, you use it's method setModel() to provide the model.
my_table_view.setModel(my_model)

Related

File dialog always returns alpha order

I'm creating an application which plots data in files. The first file plotted defines the origin to which all other data is transformed relative to. AFAICT, the QFileDialog always returns files in alphabetical order, regardless of selection order.
Is there a way to return the data ordered by selection?
To illustrate, create a folder with files named something A, B, C or 1, 2, 3. Regardless of the manner they're selected or appear in File Name line edit, the list of paths returned is in alphabetical order.
import os
import sys
from PyQt5 import QtCore, QtWidgets
MYDIR = (os.environ['USERPROFILE'] + '/Desktop/numbered').replace("\\", "/")
def on_button_pressed():
paths, _ = QtWidgets.QFileDialog.getOpenFileNames(
directory = MYDIR,
caption='Open',
filter=(
'All (*.*)'
))
for i, path in enumerate(paths):
print(i, path, flush=True)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushButton("Open")
button.pressed.connect(on_button_pressed)
button.show()
sys.exit(app.exec_())
EDIT An implementation of #musicamate's response which may hang:
import os
import sys
from PyQt5 import QtCore, QtWidgets
MYDIR = (os.environ['USERPROFILE'] + '/Desktop/numbered').replace("\\", "/")
class SelectionOrderFileDialog(QtWidgets.QFileDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog)
self.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
list_view = self.findChild(QtWidgets.QListView, 'listView')
self.selection_model = list_view.selectionModel()
self.selection_model.selectionChanged.connect(self.check_selection)
self.current_selection = []
def check_selection(self):
active_selection = []
for index in self.selection_model.selectedRows():
path = index.data(QtWidgets.QFileSystemModel.FilePathRole)
active_selection.append(path)
updated_current_selection = []
for path in self.current_selection:
if path in active_selection:
updated_current_selection.append(path)
active_selection.remove(path)
updated_current_selection.extend(active_selection)
self.current_selection[:] = updated_current_selection
print(self.current_selection, flush=True)
def on_button_pressed():
# Works fine when called as...
# dialog = SelectionOrderFileDialog()
# Causes hangs on Open
dialog = SelectionOrderFileDialog(
directory = MYDIR,
caption='Open',
filter=(
'text (*.txt)'
';;python (*.py)'
';;All (*.*)'
))
dialog.exec_()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushButton("Open")
button.resize(300, 25)
button.pressed.connect(on_button_pressed)
button.show()
sys.exit(app.exec_())
Such level of control cannot be achieved by using static methods, but it can be done by using an instance of QFileDialog.
The only requirement is to avoid the native dialog, so that we can access one of the file views (a non-native QFileDialog has a list view and a tree view), and then connect to the selectionChanged signal of its selection model.
Then, everytime the signal is emitted, we check with the current ordered selection, remove paths that already exist and build a new list by extending with the new elements.
def showDialog(self):
def checkSelection():
selection = []
for index in selectionModel.selectedRows():
path = index.data(QtWidgets.QFileSystemModel.FilePathRole)
selection.append(path)
newOrderedPaths = []
for path in orderedPaths:
if path in selection:
newOrderedPaths.append(path)
selection.remove(path)
newOrderedPaths.extend(selection)
orderedPaths[:] = newOrderedPaths
dialog = QtWidgets.QFileDialog(self)
dialog.setOption(dialog.DontUseNativeDialog)
dialog.setFileMode(dialog.ExistingFiles)
listView = dialog.findChild(QtWidgets.QListView, 'listView')
selectionModel = listView.selectionModel()
selectionModel.selectionChanged.connect(checkSelection)
orderedPaths = []
dialog.exec_()
print(orderedPaths)
Obviously, the same can be done using a subclass.

How I can align values exported from database with query in PyQt5 table view

I have the following issue.
I have a very simple code which is connecting to my local sql database, and from there I am exporting a data set with a query. Then I display this data in a PyQT table widget and that is all.
The problem is that I cannot align my data to center of columns. Currently, everything is aligned to the left.
I tried to use different combinations with Qt.AlignCenter but I cannot make it working.
here is my code
import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class BlobDelegate(QtWidgets.QStyledItemDelegate):
def displayText(self, value, locale):
if isinstance(value, QtCore.QByteArray):
value = value.data().decode()
return super(BlobDelegate, self).displayText(value, locale)
def createConnection():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "stats.db")
db.setDatabaseName(file)
if not db.open():
QtWidgets.QMessageBox.critical(
None,
QtWidgets.qApp.tr("Cannot open database"),
QtWidgets.qApp.tr(
"Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information "
"how to build it.\n\n"
"Click Cancel to exit."
),
QtWidgets.QMessageBox.Cancel)
return False
return True
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
if not createConnection():
sys.exit(-1)
w = QtWidgets.QTableView()
w.setFont(QtGui.QFont('Arial', 18))
w.horizontalHeader().setStretchLastSection(False)
w.horizontalHeader().setFixedHeight(40)
w.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
# w.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
w.setWordWrap(True)
delegate = BlobDelegate(w)
w.setItemDelegateForColumn(4, delegate)
model = QtSql.QSqlQueryModel()
model.setQuery("SELECT * FROM table_tennis_statistics")
w.setModel(model)
w.resize(1024, 600)
w.show()
sys.exit(app.exec_())
screenshot
You have to modify the displayAlignment property of QStyleOptionViewItem and apply the delegate to all columns:
class BlobDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super(BlobDelegate, self).initStyleOption(option, index)
option.displayAlignment = QtCore.Qt.AlignCenter
def displayText(self, value, locale):
if isinstance(value, QtCore.QByteArray):
value = value.data().decode()
return super(BlobDelegate, self).displayText(value, locale)
delegate = BlobDelegate(w)
w.setItemDelegate(delegate)

QComboBox Auto Complete (QCompleter?)

I have another question with Python GUI widgets in Qt Designer.
I am using Python 3.7 w/ PyQt5.
I have a list of values being generated to a combo box from an SQL table.
The combo box displays all of the values correctly but there are around 100 values total, I want to be able to type and it start to autocomplete so I can quickly find and select any value I may need.
I've done some research which has me baffled.
The list I created in Python is named listofCustOrders as I am building a "business gui" to help me learn more about Python coding. Is there a way to autocomplete this list?
from PyQt5 import QtWidgets, uic
from Classes import CustOrders as CO
import DBConnection as DB
import time
class MyWindow(QtWidgets.QMainWindow):
listofCustOrders = []
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('PyGUI.ui',self)
self.init()
def init(self):
global listofCustOrders
listofCustOrders = CO.CustOrders.getCustOrders()
for x in listofCustOrders:
self.cbCONum.addItem(x.getCustOrderNO())
self.cbCONum.currentIndexChanged.connect(self.coSelected)
self.CObutton.clicked.connect(self.Submitted1)
self.SLabel2.hide()
def coSelected(self, text):
cbCOIndex = self.cbCONum.currentIndex()
selectedCO = listofCustOrders[cbCOIndex]
self.RLbl2.setText(selectedCO.getPart())
self.QtyLbl3.setText(str(selectedCO.getQTY()))
def Submitted1(self):
self.SLabel1.hide()
self.SLabel2.show()
CBW = str(self.cbCONum.currentText())
PN = self.RLbl2.text()
QY = self.QLINE.text()
EP = self.EMPLINE.text()
TIMER = time.strftime('%m-%d-%Y %H:%M:%S')
conn1 = DB.DBConnection.getConnection()
cursor = conn1.cursor()
cursor.execute('''
INSERT INTO database.dbo (CustOrderNo, PartNo, Qty, Employee, Color)
VALUES (?, ?, ?, ?, ?)''',
(CBW, PN, QY, EP,TIMER,))
conn1.commit()
conn1.close()
self.QLINE.clear()
self.EMPLINE.clear()
self.RLbl2.clear()
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Qt has QCompleter class for such tasks, here is an example of how you can use it.
completer = QCompleter(wordList, self)
completer.setCaseSensitivity(Qt.CaseInsensitive)
comboBox.setCompleter(completer)
For reference: https://doc.qt.io/qt-5/qcompleter.html, also checkout simple examples that show how to change your completer if your model (set of words) changed - https://doc.qt.io/qt-5/qtwidgets-tools-completer-example.html . The examples are in C++, unfortunately.

Python AttributeError : Object has no attribute in QGIS Plugin

I'm trying to create a GUI with QT Designer. I've converted my .ui designer file to a .py.
Here is my code:
from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt4.QtGui import QAction, QIcon
from qgis.core import *
import resources
from delete_feature_dialog import DeleteFeatureDialog
import os.path
class DeleteFeature:
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
# Declare instance attributes
self.actions = []
self.menu = self.tr(u'&DeleteFeature')
self.toolbar = self.iface.addToolBar(u'DeleteFeature')
self.toolbar.setObjectName(u'DeleteFeature')
def add_action(
self,
icon_path,
text,
callback,
enabled_flag=True,
add_to_menu=True,
add_to_toolbar=True,
status_tip=None,
whats_this=None,
parent=None):
# Create the dialog (after translation) and keep reference
self.dlg = DeleteFeatureDialog()
....
return action
def initGui(self):
icon_path = ':/plugins/DeleteFeature/icon.png'
self.add_action(
icon_path,
text=self.tr(u''),
callback=self.run,
parent=self.iface.mainWindow())
def run(self):
#this code will populate the combo box with all vector layer
self.dlg.layerListCombo.clear()
layers = self.iface.legendInterface().layers()
layer_list = []
for layer in layers:
layerType = layer.type()
if layerType == QgsMapLayer.VectorLayer:
layer_list.append(layer.name())
self.dlg.layerListCombo.addItems(layer_list)
# show the dialog
self.dlg.show()
# Run the dialog event loop
result = self.dlg.exec_()
# See if OK was pressed
if result:
# Do something useful here - delete the line containing pass and
# substitute with your code.
selectedLayerIndex = self.dlg.layerlistcombo.currentIndex()
selectedLayer = layers [selectedLayerIndex]
.....
Then when I open the plugin, I get the following error:
'DeleteFeatureDialog' objectObject has no attribute
'layerlistcombo'in QGIS Plugin
Any suggestion for this.
Seems that you wrote:
selectedLayerIndex = self.dlg.layerlistcombo.currentIndex()
but you should have written:
selectedLayerIndex = self.dlg.layerListCombo.currentIndex()
Like you did previously in your code (notice the Camel Notation when writing, not just lower-case letters), which is probably causing the No Attribute error you get.

PyQt: How to connect a QStandardItem in a QStandardItemModel to a function

I am creating an application in PyQt in which I have a a list of check boxes which I have created using QStandardItemModel and QStandardItem and it works perfectly. I want to connect the first item in the list which is a 'Select all' check box to a function. This function should be able to check all the other items of the list. I'm trying to do this by the following code:
model = QStandardItemModel(list)
item = QStandardItem("Select all")
model.appendRow(item)
item.setCheckable(True)
model.itemChanged.connect(state_changed)
def state_changed(item):
print ("Hello")
I have added more items to the list from the output of an SQL query and I can see that 'Hello' is printed irrespective of which check box I click. This is my entire code:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtSql import *
def main():
db = QSqlDatabase.addDatabase("QODBC")
db.setHostName('LAPTOP-B79DRPA3')
db.setDatabaseName('local')
db.open()
if (db.open()==False):
QMessageBox.critical(None, "Database Error",
db.lastError().text())
query = QSqlQuery ()
query.exec_ ("select id from [Sarah].[dbo].fraga")
list = QListView()
model = QStandardItemModel(list)
item = QStandardItem("Select all")
model.appendRow(item)
item.setCheckable(True)
model.itemChanged.connect(state_changed)
while (query.next()):
item1 = QStandardItem(str(query.value(0)))
model.appendRow(item1)
item1.setCheckable(True)
list.setModel(model)
list.show()
return app.exec_()
def state_changed(item):
print ("Hello")
if __name__ == '__main__':
app = QApplication(sys.argv)
list = QListView()
model = QStandardItemModel(list)
main()
How do I ensure that the function is invoked only when the state of 'Select All' is changed?
Instead of connecting to QAbstractItemModel.itemChanged signal, connect to QAbstractItemView.clicked signal, which specifies the index clicked. Also, it's advisable not to name a variable list, as it interferes with the built-in list.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtSql import *
def main():
db = QSqlDatabase.addDatabase("QODBC")
db.setHostName('LAPTOP-B79DRPA3')
db.setDatabaseName('local')
db.open()
if (db.open()==False):
QMessageBox.critical(None, "Database Error",
db.lastError().text())
query = QSqlQuery ()
query.exec_ ("select id from [Sarah].[dbo].fraga")
list_view = QListView()
model = QStandardItemModel(list_view)
item = QStandardItem("Select all")
model.appendRow(item)
item.setCheckable(True)
list_view.clicked.connect(state_changed)
while (query.next()):
item1 = QStandardItem(str(query.value(0)))
model.appendRow(item1)
item1.setCheckable(True)
list_view.setModel(model)
list_view.show()
return app.exec_()
def state_changed(index):
row = index.row()
if row == 0:
print ("Hello")
if __name__ == '__main__':
app = QApplication(sys.argv)
list_view = QListView()
model = QStandardItemModel(list)
main()

Categories