First, you should know I'm very new to Python. I'm currently working on an app to increase my knowledge of this language.
I'm having a QListWidget which contains items based on a widget I created. This widget contains buttons like one to update or another to delete the item.
So I'm having troubles to do this action. For example, If I click on the update button, I want to open a window and load content by getting the item values.
How could I do that ?
This is my actual code
MainWindow
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QMainWindow, QHBoxLayout
from CustomWidgets.fb_line import fb_line
from Data.data_saver import data_saver
from MainWindows import Ui_MainWindow
from Windows.Ajout import Ajout
from Windows.Parametres import Parametres
from Windows.custom_fb_line import custom_fb_line
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.c_fb_l = custom_fb_line()
self.parametres = Parametres()
self.ajout = Ajout()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn_parametre.clicked.connect(self.button_open_parametres)
self.ui.btn_ajouter.clicked.connect(self.button_open_ajout)
self.ui.btn_annuler.clicked.connect(self.button_annuler)
self.ui.btn_fermer.clicked.connect(self.close)
self.addLines()
def button_open_parametres(self):
self.parametres.show()
def button_open_ajout(self):
self.ajout.show()
def button_annuler(self):
self.ui.lw_dossier.clear()
self.addLines()
def addLines(self):
fbs = ds.read_data(data)
listWidget = self.ui.lw_dossier
listWidget.reset()
for fb in fbs:
item_widget = fb_line(fb)
item = QtWidgets.QListWidgetItem(listWidget)
item.setSizeHint(QSize(0, 50))
listWidget.addItem(item)
listWidget.setItemWidget(item, item_widget)
listWidget.show()
data = "./Data/data.pkl"
ds = data_saver()
Widget :
from PyQt5 import QtWidgets
from Objects.enum import enum
from custom_fb_line import Ui_custom_fb_line
class fb_line(QtWidgets.QWidget):
def __init__(self, fb, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
self.ui = Ui_custom_fb_line()
self.ui.setupUi(self)
self.ui.le_source.setText(fb.dossier_source)
self.ui.le_cible.setText(fb.dossier_cible)
self.ui.le_cron.setText("tous les " + str(fb.cron.number) + " " + enum().get_types()[fb.cron.type])
self.ui.btn_delete.clicked.connect(self.delete)
self.ui.btn_modifier.clicked.connect(self.modifier)
self.ui.btn_demarrer.clicked.connect(self.demarrer)
def delete(self):
print('delete ')
def modifier(self):
print('modifier')
def demarrer(self):
print('démarrer')
So print does actually show in console but I can't figure out how to achieve button click to update or delete items.
Never mind, I found a way, which is really simple in fact. Being new to python, I didn't know how to pass arguments to methods plugged to buttons. Here is how to do that for anyone who could have same issues
self.ui.btn_delete.clicked.connect(lambda: self.delete(fb))
def delete(self, fb):
print('delete ')
print(fb.dossier_source)
Related
I want do dynamically change the layout in Qt. For example, I want to change the QHBoxLayout to QVBoxLayout through a button. My test code is:
from PyQt5.QtWidgets import *
import sys
class SubWidget(QWidget):
def __init__(self):
super().__init__()
self.lay = QHBoxLayout()
self.label1 = QLabel('left')
self.label2 = QLabel('right')
self.lay.addWidget(self.label1)
self.lay.addWidget(self.label2)
self.setLayout(self.lay)
def change(self):
self.lay.removeWidget(self.label1)
self.lay.removeWidget(self.label2)
self.lay = QVBoxLayout()
self.setLayout(self.lay)
self.lay.addWidget(self.label2)
self.lay.addWidget(self.label1)
class Widget(QWidget):
def __init__(self):
super().__init__()
lay = QVBoxLayout()
self.btn = QPushButton('change layout')
self.btn.clicked.connect(self.btnClick)
self.subWidget = SubWidget()
lay.addWidget(self.btn)
lay.addWidget(self.subWidget)
self.setLayout(lay)
def btnClick(self, check=False):
self.subWidget.change()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Widget()
win.show()
app.exec_()
The code output GUI is:
And I hope it change to the following picture after click change layout button:
Any suggestion is appreciated~~~
The point of the solution is
Make sure old layout is deleted, included both python wrapping reference and core Qt object, that's for deleteLater() is used.
Make sure new layout is assigned strictly after old was deleted, that's for need to use destroyed()-switchlayout() signal-slot chain.
I reproduced example with PySide6 (don't forget to switch on your version of PyQt or PySide package):
# from PyQt5.QtWidgets import *
from PySide6.QtWidgets import *
import sys
class SubWidget(QWidget):
def __init__(self):
super().__init__()
self.lay = QVBoxLayout()
self.label1 = QLabel('left')
self.label2 = QLabel('right')
self.lay.addWidget(self.label1)
self.lay.addWidget(self.label2)
self.setLayout(self.lay)
def change(self):
self.lay.removeWidget(self.label1)
self.lay.removeWidget(self.label2)
self.lay.deleteLater()
self.lay.destroyed.connect(self.switchlayout)
def switchlayout(self):
# print("***destroyed")
self.lay = QHBoxLayout()
self.lay.addWidget(self.label2)
self.lay.addWidget(self.label1)
self.setLayout(self.lay)
self.adjustSize()
class Widget(QWidget):
def __init__(self):
super().__init__()
lay = QVBoxLayout()
self.btn = QPushButton('change layout')
self.btn.clicked.connect(self.btnClick)
self.subWidget = SubWidget()
lay.addWidget(self.btn)
lay.addWidget(self.subWidget)
self.setLayout(lay)
def btnClick(self, check=False):
self.subWidget.change()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Widget()
win.show()
app.exec_()
Today I spent 3 hours in figuring why my QScrollArea was not working. And finally found that the reason was the components which I was dynamically adding to the ScrollArea didn't have any Layout so it was not able to compute the minimum size of it while adding.
So If anyone is adding elements dynamically to the QScrollArea, make sure your widgets have some area. I saw lot of posts where people where facing the same issue and there was no direct answer that layout is the cause so this is like a post notifying to the all users to make sure you first have a layout in all of your widgets you want to append to QScrollArea.
Thank you
An example would be
import sys
from PyQt4 import QtCore, QtGui, uic
mainBase, mainForm = uic.loadUiType("main.ui")
labelBase, labelForm = uic.loadUiType("labelsForScroll.ui")
dataBase, dataForm = uic.loadUiType("data.ui")
emptyBase, emptyForm = uic.loadUiType("empty.ui")
class Data(dataBase, dataForm):
def __init__(self, parent=None):
super(dataBase, self).__init__(parent)
self.setupUi(self)
def setSl(self, num):
self.sl.setText(str(num))
class Label(labelBase, labelForm):
def __init__(self, parent=None):
super(labelBase, self).__init__(parent)
self.setupUi(self)
class Empty(emptyBase, emptyForm):
def __init__(self, parent=None):
super(emptyBase, self).__init__(parent)
self.setupUi(self)
class Main(mainBase, mainForm):
def __init__(self, parent=None):
super(mainBase, self).__init__(parent)
self.setupUi(self)
self.__l = Label()
#self.vert.addWidget(l)
self.__maxSlNum = 0
self.__datLis =[]
#self.addField()
self.__e = Empty()
self.__e.layout().addWidget(self.__l)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.__e)
QtCore.QObject.connect(self.add, QtCore.SIGNAL("clicked()"), self.addField)
def addField(self):
d = Data()
self.__maxSlNum+=1
d.setSl(self.__maxSlNum)
self.__datLis.append(d)
lay = self.__e.layout()
lay.addWidget( d)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setStyle("cleanlooks")
m = Main()
m.show()
sys.exit(app.exec_())
links for the ui file can be found here links
Sorting QTreeView with QSortFilterProxyModel is extremely slow (compared to basic QTreeWidget). What's wrong with the code? How can I speed-up?
# coding: utf-8
import sys
from PyQt5.QtWidgets import (QTreeView, QAbstractItemView,
QMainWindow, QApplication)
from PyQt5.QtSql import (QSqlDatabase, QSqlQuery, QSqlQueryModel)
from PyQt5.QtCore import QSortFilterProxyModel
class Database:
def __init__(self):
self.db = QSqlDatabase.addDatabase("QODBC")
self.db.setDatabaseName(
r'Driver={SQL Server Native Client 11.0};Server={(localdb)\v11.0};')
self.db.open()
class Model(QSqlQueryModel):
def __init__(self, parent=None):
super().__init__(parent)
query = QSqlQuery()
query.prepare('SELECT * FROM Companies WHERE Name LIKE ?')
query.bindValue(0, '%elektro%')
query.exec_()
self.setQuery(query)
class TreeView(QTreeView):
def edit(self, index, trigger, event):
if trigger == QAbstractItemView.DoubleClicked:
return False
return QTreeView.edit(self, index, trigger, event)
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.db = Database()
self.model = Model()
sortFilterModel = QSortFilterProxyModel()
sortFilterModel.setSourceModel(self.model)
treeView = TreeView(self)
treeView.setModel(sortFilterModel)
treeView.hideColumn(0)
treeView.hideColumn(9)
treeView.setIndentation(0)
treeView.setSelectionMode(QAbstractItemView.MultiSelection)
treeView.setAllColumnsShowFocus(True)
treeView.setSortingEnabled(True)
treeView.clicked.connect(self.row_id)
self.setCentralWidget(treeView)
def row_id(self, index):
if index.isValid():
id_ = index.sibling(index.row(), 0).data()
print(id_)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec_()
Some tips (from a C++ perspective, so i could be missing something )
QTreeView is known for being slower than a QTableView and consume a lot of memory And you are using a plain table model anyways, so, try with a QTableview.
Also, in a real tree hierarchical model hiding column 0 would hide all child items.
Try using QSqlTableModel instead of a QSqlQueryModel, it has convenience setSort, setFilter methods and only fetchs visible rows. So i would be probably faster.
I have the following code to display am image using pyQt:
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
window.setGeometry(opts.posx, opts.posy, opts.width, opts.height)
pic = QtGui.QLabel(window)
pic.setGeometry(5, 5, opts.width-10, opts.height-10)
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
window.show()
sys.exit(app.exec_())
I would like to wrap up this code possibly in the form of a class, and be able to set a different image during runtime, using signals, socket, threads I really do not know. I would imagine something like:
class MyImage(object):
def __init(self, args):
some setup code
self.pic = whatever
def set_image(self, filename):
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
With the original code I just call sys.exit(app.exec_()) which makes the code 'freeze'. But I want to send a signal (and a filename) from a different running python code. Any suggestion how this can be handled easily and straightforward? Maybe overwriting the app.exec_ method?
Something like this should work for you:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtGui, QtCore
class ImageChanger(QtGui.QWidget):
def __init__(self, images, parent=None):
super(ImageChanger, self).__init__(parent)
self.comboBox = QtGui.QComboBox(self)
self.comboBox.addItems(images)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.comboBox)
class MyWindow(QtGui.QWidget):
def __init__(self, images, parent=None):
super(MyWindow, self).__init__(parent)
self.label = QtGui.QLabel(self)
self.imageChanger = ImageChanger(images)
self.imageChanger.move(self.imageChanger.pos().y(), self.imageChanger.pos().x() + 100)
self.imageChanger.show()
self.imageChanger.comboBox.currentIndexChanged[str].connect(self.changeImage)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.label)
#QtCore.pyqtSlot(str)
def changeImage(self, pathToImage):
pixmap = QtGui.QPixmap(pathToImage)
self.label.setPixmap(pixmap)
if __name__ == "__main__":
import sys
images = [ "/path/to/image/1",
"/path/to/image/2",
"/path/to/image/3",
]
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow(images)
main.show()
sys.exit(app.exec_())
I'm trying to create the simplest example of controlling a plot in one window from buttons and functions in another window. I know I'm going to need more things in the window with the plot, so, I need to create my plot figure withing a QT window.
This is what I have so far:
My control window:
import sys
from PyQt4.QtCore import (QSize, SIGNAL)
from PyQt4.QtGui import (QApplication, QDialog, QPushButton, QVBoxLayout)
import PlotWindow
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.pushButton1 = QPushButton()
self.pushButton1.setMaximumSize(QSize(110, 24))
self.pushButton1.setObjectName("pushButton1")
self.pushButton1.setText("Open Plot")
self.pushButton2 = QPushButton()
self.pushButton2.setMaximumSize(QSize(110, 24))
self.pushButton2.setObjectName("pushButton2")
self.pushButton2.setText("Update Plot")
layout = QVBoxLayout()
layout.addWidget(self.pushButton1)
layout.addWidget(self.pushButton2)
self.setLayout(layout)
self.setWindowTitle("Plot Control")
self.connect(self.pushButton1,SIGNAL("clicked()"),self.plotNew)
def plotNew(self):
print "new plot window"
self.dialog = PlotWindow.PlotWindow()
self.dialog.show()
app = QApplication(sys.argv)
form = Window()
form.open()
app.exec_()
My plot window :
import matplotlib
import numpy as np
from PyQt4 import QtGui
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
class PlotWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(PlotWindow, self).__init__(parent)
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111)
plt.draw()
Obviously my plot window is where I'm having the biggest problems. I've gone through lots of examples, but none of them seem to show how to place a plot within another window.
Thanks
Well, I can answer part of my own question, but I would like comments on whether or not this is the best way to accomplish. The following code shows the changes I've made to bring up a second window with a plot inside.
import sys
from PyQt4 import QtGui
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class PlotCanvas(FigureCanvas):
def __init__(self, parent):
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
# self.x = np.arange(0.0,0.3,0.01)
# self.y = np.cos(2*np.pi*self.x)
# self.axes.plot(self.x,self.y)
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
class PlotWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setWindowTitle("Plot Figure")
self.main_widget = QtGui.QWidget(self)
vbl = QtGui.QVBoxLayout(self.main_widget)
qmc = PlotCanvas(self.main_widget)
ntb = NavigationToolbar(qmc, self.main_widget)
vbl.addWidget(qmc)
vbl.addWidget(ntb)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
I still need help with setting the data for the plot from the other window.
Thanks