How can I add a custom widget when i use a .ui file for my gui? [duplicate] - python-3.x

This question already has answers here:
Easiest way to subclass a widget in Python for use with Qt Designer
(2 answers)
Custom Qt Widgets with python for Qt Designer
(3 answers)
Closed 18 days ago.
I'm relatively new to Python. I have a program that does a lot of cool stuff. However, I seem to be restricted with adding in custom widgets because I'm relying on a .ui file to load my entire gui like so:
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('images/structure.ui', self)
I want to be able to insert the below entire search widget into a specific part of my GUI.
https://learndataanalysis.org/create-an-instant-search-feature-to-search-data-in-your-table-in-pyqt5/
code:
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')
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.setStyleSheet('font-size: 35px; height: 60px;')
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_())
Do I have to edit my .ui file to make this happen? I tried creating widgets named search_field and table, and there was no impact. How can I simply create search_field as a qline edit, and table as a TableView or some sort of ListView Widget, and have the above code search properly?
I tried using the above code
edit:
basically, I just want to be able to ctrl + F and search for text in a textBrowser widget.

Related

Proper way of managing multiple windows in PySide?

I have this app where I have several settings windows that open when buttons from the main window are clicked. The windows are application modal, so only one is open at a time. I have two ideas as to how to manage them, but I'm not sure which one would be the proper way to do it. I don't particularly care how the values are stored, as long as I can pass them to other windows in the app and do stuff with them.
MainWindow class Option 1:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget()
layout = QVBoxLayout()
button = QPushButton('Show window')
layout.addWidget(button)
window = OtherWindow()
button.clicked.connect(window.show)
# I can pull the settings and pass them on to other windows if needed.
self.setCentralWidget(central)
MainWindow class Option 2:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.other_settings = {}
button = QPushButton('Show window')
button.clicked.connect(self.show_other)
def show_other(self):
other_window = OtherWindow()
if other_window.exec():
self.other_settings.update(other_window.settings)
OtherWindow class:
class OtherWindow(QDialog):
def __init__(self):
super().__init__()
self.settings = {}
# widgets
box = QSpinBox(objectName='spinbox')
box.valueChanged.connect(self.save_settings)
# and so on ...
def save_settings(self):
sender = self.sender()
self.settings[sender.objectName()] = sender.value()
If i've understood your question correctly, look at this link which might be useful.
Small addition from me is, look here.
from PyQt5.QtWidgets import QDialog
from ui_imagedialog import Ui_ImageDialog
class ImageDialog(QDialog):
def __init__(self):
super(ImageDialog, self).__init__()
# Set up the user interface from Designer.
self.ui = Ui_ImageDialog()
self.ui.setupUi(self)
# Make some local modifications.
self.ui.colorDepthCombo.addItem("2 colors (1 bit per pixel)")
# Connect up the buttons.
self.ui.okButton.clicked.connect(self.accept)
self.ui.cancelButton.clicked.connect(self.reject)
Once you've got your .ui files it is a good practice to use (second link, second example - mentoned above) pyside6-uic or pyuic5 - depending of your needs.
Then you're creating class which role is to setup desired QWidget ui converted python module.
So in QtDesigner you're creating a QWidget:
screen_qt_designer
then do all your stuff, that it would like to do - place over QLineEdit, QPushButtons, etc., then save that file in your project's folder. After that all you have to do is use pyuic or pyside6-uic with proper settings (look at --help to set the output filename).
Then all you have is a .ui file (which is used in case you would like to add some widget, or change the widget all all) and your generated python class that inherits QObject, eg:
class Ui_Form(object):
def setupUi(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
Form.resize(800, 600)
self.verticalLayout = QVBoxLayout(Form)
self.verticalLayout.setObjectName(u"verticalLayout")
self.label = QLabel(Form)
self.label.setObjectName(u"label")
Then create a another Python file, that inherits this generated class, eg.:
from PySide6.QtWidgets import QWidget
from CreateNewDatabase_ui import Ui_Form
class NewDatabaseWidget(QWidget):
def __init__(self):
super().__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
That's all. You have your prepared QWidget for adding it into a QStackedWidget. I use it in my own program and it fit me very well.
Edit:
I've forgot about 2 important notes:
Never ever edit your file generated by pyuic or pyside6-uic program. Whenever you wish to change the .ui file, all changes made to autogenerated code will be lost.
It's better to keep all signals/slots, logic mechanism in your QMainWindow class. It's better approach in case of code readability.

How can I put the RadioBoxes over the Layout?

I designed the following window. However, when running the code, the RadioBoxes stay behind the layout that contains the frame and the string. Could someone please tell me how to avoid this?
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Window')
self.setFixedSize(550,440)
self.LaySelf = QGridLayout()
self.initWidgets()
self.initUI()
self.show()
def initWidgets(self):
self.Panel = QFrame()
self.Panel.setFrameStyle(QFrame.StyledPanel)
self.Panel.setLineWidth(2)
self.Panel.setStyleSheet('background-color:#f4f2f1')
self.Btt = QRadioButton('Radio',self)
self.Label = QLabel(' '*40+'Hi')
def initUI(self):
self.LaySelf.addWidget(self.Panel,0,0,-1,6)
self.LaySelf.addWidget(self.Label,0,0,-1,6)
self.setLayout(self.LaySelf)
self.Btt.move(200,200)
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
In order to make radio button on top of QFrame you need to add it to the Frame. Currently the Radio Buttons stay behind Frame which is covering the layout.
Replace QRadioButton('Radio',self) with QRadioButton('Radio',self.Panel)
Also here is a link to another thread which demostrates Frame Usage with multiple QWidgets

PySide2 How to unassign current layout and assign a new one to a window widget? .count() gives attribute error?

I'm new to PySide2, started using it yesterday. I'm teaching myself from the API documentation and info I find online. I'm trying to make a small app with it to teach myself. My idea for the app was using a View like model, as you go pressing on certain buttons I delete the current layout on the window and present you with an entire new one. This foments modularity as each layout wouldn't be dependent on each other but instead fully individual. (Each part of the app is broadly different and requires almost all widgets to be deleted).
However whenever I search how to delete a layout or remove all of its widgets I always encounter a piece of code like this (This is actually from a question asked 6 months ago):
def clearLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.clearLayout(item.layout())
Yet when I implement something like that, even the exact same function and then call it using, self.clearLayout(self.layout) I get the following error:
File "GUI.py", line 45, in home
self.clearLayout(self.layout)
File "GUI.py", line 16, in clearLayout
while layout.count():
AttributeError: 'builtin_function_or_method' object has no attribute 'count'
I'm assuming I probably forgot to import something important, but I can't just seem to find what. Here is a list with my imports.
import sys
from PySide2.QtWidgets import QApplication, QLabel, QLineEdit, QWidget
from PySide2.QtWidgets import QDialog, QPushButton, QVBoxLayout, QLayout
I also installed PySide2 using pip install PySide2 on command line (Anaconda). Do I have to do something else?
Thank you so much, sorry post is so long, just wanted to give you the whole information from the beginning :)
EDIT:
Further testing has led me to realise I cannot use any of the virtual functions of QLayout, or its subclasses. Apparently I have to implement them myself within the code. I believe it could be this. I don't know how to implement them though. I'm going to keep trying.
EDIT 2: Some people asked for a reporducible example so here is the code that doesn't work.
import sys
from PySide2.QtWidgets import QApplication, QLabel, QLineEdit, QWidget
from PySide2.QtWidgets import QDialog, QPushButton, QVBoxLayout, QLayout
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Program")
self.setGeometry(300, 300, 300, 300)
self.start()
def clearLayout(self, layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
self.clearLayout(item.layout())
def start(self):
self.startbutton = QPushButton("Start App")
layout = QVBoxLayout()
layout.addWidget(self.startbutton)
self.setLayout(layout)
self.startbutton.clicked.connect(self.home)
def home(self):
self.clearLayout(self.layout)
self.message=QLabel("Welcome to homepage")
layout=QVBoxLayout()
layout.addWidget(self.message)
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication([])
window=Window()
window.show()
sys.exit(app.exec_())

How to see signals from QWidgets inside dynamically created QTabWidget pages?

EDIT : I've come up with a solution, and it's much more straightforward than I thought. Original code and question at the top. My solution after "The Question" below..
The Example
from PyQt4 import QtGui, QtCore
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class TabFiller(Form):
def __init__(self, parent=None):
Form.__init__(self, parent)
def TabButtonClicked(self):
print("Tab button pressed.")
def LineEditChanged(self):
print("LineEdit contents edited in tab page!")
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_filler = [] # create empty list for tab contents
tab_page = [] # create empty list for tab page
tab_count = 0
def CreateNewTab(self):
tab_title = "New Tab : " + str(self.tab_count)
self.tab_filler.append(TabFiller())
self.tab_filler[self.tab_count].label.setText(tab_title)
self.tab_page.append(self.tab_filler[self.tab_count])
self.tabWidget.addTab(self.tab_page[self.tab_count], tab_title)
self.tab_count += 1
def MainButtonPressed(self):
self.CreateNewTab()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
MainWindow contains a QTabWidget, which is a Button. clicked() signal has been defined in QtDesigner to be sent to the MainButtonPressed() function inside the MainWindow class.
Form widget also created in QTdesigner. Used to fill additional Tab Pages.
This contains a Button widget, and a LineEdit Widget.
The Question
I can't get my head around how I can tell which widget has been clicked or edited in each tab.
I know that each Tab Page is stored in the list called tab_page.
Within the MainWindow class, how would I receive a clicked() or finishedEditing() signal for a given widget in a currently active tab?
A Solution
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4 import QtGui
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_index = 1 # 1 because we already made a default tab in QtDesigner
def LineEditChanged(self):
findWidget = self.tabWidget.widget(self.tabWidget.currentIndex()).findChildren(QtGui.QLineEdit, "lineEdit")
if findWidget[0].isModified() == True:
print("LineEdit contents edited in tab page!")
print("Name of page edited :", "'", self.tabWidget.tabText(self.tabWidget.currentIndex()),"'")
def TabButtonPressed(self):
print("YOU DID IT!")
print("Current Tab Index = ", self.tabWidget.currentIndex())
def CreateNewTab(self, tabNum):
tab_title = "New Tab : " + str(self.tab_index)
self.tabWidget.addTab(Form(), tab_title)
def MainButtonPressed(self):
self.CreateNewTab(self.tab_index)
findWidget = self.tabWidget.widget(self.tab_index).findChildren(QtGui.QPushButton, "tabButton")
findWidget[0].clicked.connect(self.TabButtonPressed)
findWidget = self.tabWidget.widget(self.tab_index).findChildren(QtGui.QLineEdit, "lineEdit")
findWidget[0].editingFinished.connect(self.LineEditChanged)
self.tab_index += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Using this there's no need for storing each tab page object in a list. You basically use the QTabWidget to index your pages, and off you go.
If anyone has a more elegant way than this, please inform ;)
As outlined in my edited question, I did find the solution to this, which is to use the QTabWidget to "index" each dynamically created tab page.
In QtDesigner I created a main window with one QTabWidget and one button thusly;
Here's the object tree for that;
NOTE: I added a signal/slot for the "Click Me!" button in QtDesigner, so that when that button is clicked, the MainButtonPressed function is called.
To fill the tab pages, I also created a Form in QtDesigner, with a button and a QLineEdit widget;
And the object tree for that;
I'll reproduce the code here. NOTE: I've now updated this answer to use findChild rather than findChildren above:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4 import QtGui
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_index = 1 # 1 because we already made a default tab in QtDesigner
def LineEditChanged(self):
findWidget = self.tabWidget.widget(self.tabWidget.currentIndex()).findChild(QtGui.QLineEdit, "lineEdit")
if findWidget.isModified() == True:
print("LineEdit contents edited in tab page!")
print("Name of page edited :", "'", self.tabWidget.tabText(self.tabWidget.currentIndex()),"'")
def TabButtonPressed(self):
print("YOU DID IT!")
print("Current Tab Index = ", self.tabWidget.currentIndex())
def CreateNewTab(self, tabNum):
tab_title = "New Tab : " + str(self.tab_index)
self.tabWidget.addTab(Form(), tab_title)
def MainButtonPressed(self):
self.CreateNewTab(self.tab_index)
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QPushButton, "tabButton")
findWidget.clicked.connect(self.TabButtonPressed)
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QLineEdit, "lineEdit")
findWidget.editingFinished.connect(self.LineEditChanged)
self.tab_index += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
When run, pressing the "Click Me!" button on the main tab page creates a new tab, and adds the contents of the "filler" page to it.
The variable tab_index keeps track of how many tabs there are and allows you to reference the contents of each tab.
To find a widget in a tab, you use the findChild function of Qt;
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QPushButton, "tabButton")
Finding a specific widget is straightforward. You specify the type of widget you're looking for (QtGui.QPushButton) , and the name you assigned it in QtDesigner (tabButton)
In this case the found widget can be referenced by the variable findWidget.
You can then connect signals to function slots as usual;
findWidget.clicked.connect(self.TabButtonPressed)
In this case I used the new-style signal connection method to connect the clicked() signal to a function named TabButtonPressed in my program.
Rinse and repeat for each widget on the Tab Page you wish to do something with.
After that, it really is plain sailing ;)
I hope this information helps others in their GUI endeavours. You can probably use the same technique with the QToolBox widget.

PyQt and QtDesigner

I am trying to implement a LED in PyQt named disLDR1 by trying to change the background color. I am trying to use QPalette to change the BackgroundRole. But what is the equivalent of Qt::red?
Is this the correct way to set the background color or is there any other way?
#!/usr/bin/python -d
import sys
from PyQt4 import QtCore, QtGui
from main import Ui_Form
from PyQt4.QtGui import QPalette
class Top(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
for i in 1, 10:
self.setOff()
self.setOn()
def setOff(self):
self.pal = QPalette(self.ui.disLDR1.palette())
self.pal.setColor(self.ui.disLDR1.backgroundRole(), <<<RED COLOR>>>)
self.ui.disLDR1.setPalette(pal)
def setOn(self):
self.pal = QPalette(self.ui.disLDR1.palette())
self.pal.setColor(self.ui.disLDR1.backgroundRole(), <<<GREEN COLOR>>>)
self.ui.disLDR1.setPalette(pal)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Top()
myapp.show()
sys.exit(app.exec_())
EDIT: I have been trying to take help from In Qt, how do I set the background color of a widget like combobox or double spin box? but don't know what to substitute for QT::red
You can find the list of predefined Qt color objects at this link. In this case you would just need to use QtCore.Qt.red and QtCore.Qt.blue. You could also use the QColor class to generate arbitrary colors.
It's a matter of preference, but I personally think the easiest and most powerful way to go would be to use a style sheet.

Resources