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

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_())

Related

How to make round edges for the main window in PyQt?

The question is extremely simple, but I am not succeeding at it at the moment:
What is the simplest way to make round edges to the main window in PyQt/PySide code? I have tried lots of things that can be found online but none works. Here is th simplest example (that is supposed to work in my opinion, but doesn't):
from PySide6.QtWidgets import QApplication, QMainWindow
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setStyleSheet("border-radius: 10px;")
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())

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

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.

How can I make modern search bar in PySide2?

I was making my own web browser until I realized that making a searchbar was a difficult task. I tried removing the title bar, but it didn't work out as expected because of pyside2 limitations. Then, I made my own searchbar but it wasn't interactive and the design was awful. So, I was looking around to see how I can make a draggable searchbar without a titlebar with stylesheets included and I found no recourses online. Therefore, StackOverflow was my last thought on how I can resolve and ask for information about this issue. Please leave any comments if I made any mistakes, Thank you! :D
import sys
from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtWebEngine import *
from PySide2.QtWebEngineWidgets import *
class BrowserEngine(QWidget):
def __init__(self, *args, **kwargs):
super(BrowserEngine, self).__init__()
self.webview = QWebEngineView(self)
self.webview.load("https://www.duckduckgo.com")
self.setMinimumSize(800, 600)
self.mainLayout = QVBoxLayout()
self.mainLayout.addWidget(self.webview)
self.mainLayout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.mainLayout)
class BrowserWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(BrowserWindow, self).__init__()
self.setWindowTitle("DuckDuckGo")
self.widget = BrowserEngine()
self.setCentralWidget(self.widget)
if (__name__ == '__main__'):
app = QApplication(sys.argv)
module = BrowserWindow().show()
sys.exit(app.exec_())
Use QlineEdit and setPlaceHolderText, if you want some effects to use like google search completer, use QCompleter. Hope it helps you. As far as I come across, there is nothing called as search bar option in pyqt5.

How to bring PyQt QProcess window to front?

I am starting a QProcess and would like to bring the resulting window on top everytime a command is written to QProcess.
However, if my process is "gnuplot.exe" (Win7) the plot window will be updated but always stays behind the PyQt window. I haven't yet found a way to bring it to front, or make it active, or put the focus on, or however you would call it.
Something like
self.process. ...
raise(), show(), activateWindow(), SetForegroundWindow, WindowStaysOnTopHint ... ???
These posts didn't help me further
How to find the active PyQt window and bring it to the front
https://forum.qt.io/topic/30018/solved-bring-to-front-window-application-managed-with-qprocess/3
Bring QProcess window to front (running Qt Assistant)
Here is the code:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import subprocess
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.button1 = QPushButton('New plot',self)
self.button1.clicked.connect(self.button1_clicked)
self.process = QProcess(self)
self.process.start(r'C:\Programs\gnuplot\bin\gnuplot.exe', ["-p"])
self.n = 1
def button1_clicked(self):
plotcmd = "plot x**"+str(self.n)+"\n"
self.process.write(plotcmd.encode())
self.n += 1
response = self.process.readAllStandardOutput()
print(str(response, encoding="utf-8"))
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique")
window = MyWindow()
window.show()
sys.exit(app.exec_())
Based on the comment and links from #buck54321, I got to the following version which seems to (almost) work.
The solution in the above links seem to be more general and a bit too complicated for my case, since I know the exact name of my window which I want to bring to front.
To my original code I just had to add three lines. I was not familiar with win32gui.
However, with my code, only after the button is pressed the second time the window will be brought to front. After the first button press hwnd is still 0 which leads to a crash. I guess this is because gnuplot.exe will start without opening a window, and opens a window only after the first command has been sent.
If anybody can show me how to bring the window to front even after the first button click that would be great.
If there are limitation or improvements I would be happy to learn about them.
Here is the code:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import subprocess
import win32gui ####### new line
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.button1 = QPushButton('New plot',self)
self.button1.clicked.connect(self.button1_clicked)
self.process = QProcess(self)
self.process.start(r'C:\Programs\gnuplot\bin\gnuplot.exe', ["-p"])
self.n = 1
def button1_clicked(self):
plotcmd = "set term wxt 999\n plot x**"+str(self.n)+"\n"
self.process.write(plotcmd.encode())
self.n += 1
hwnd = win32gui.FindWindow(None, "Gnuplot (window id : 999)") ####### new line
response = self.process.readAllStandardOutput()
print(str(response, encoding="utf-8"))
if hwnd != 0: win32gui.SetForegroundWindow(hwnd) ####### new line
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique")
window = MyWindow()
window.show()
sys.exit(app.exec_())

Why doesn't this custom QWidget display correctly

I'm retrying this question with a much better code example.
The code below, in its current form, will display a green shaded QWidget in a window, which is what I want. However, when commenting out the line:
self.widget = QWidget(self.centralwidget)
and uncommenting,
self.widget = Widget_1(self.centralwidget)
the green box doesn't display. The Widget_1 class is a simple subclass of QWidget, so I'm trying to wrap my head around where the breakdown is occurring. There are no error messages, and the print("Test") line within the Widget_1 class is outputting just fine, so I know everything is being called properly.
I'm not looking to use any type of automated layouts for reasons I don't need to go into here. Can you help me to understand why the green rectangle isn't displaying, and what correction I would need to make in order to utilize the Widget_1 class?
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt5.QtCore import QRect
import sys
class Main_Window(object):
def setupUi(self, seating_main_window):
seating_main_window.setObjectName("seating_main_window")
seating_main_window.setEnabled(True)
seating_main_window.resize(400, 400)
self.centralwidget = QWidget(seating_main_window)
self.centralwidget.setObjectName("centralwidget")
########### The following two lines of code are causing the confusion #######
# The following line, when uncommented, creates a shaded green box in a window
self.widget = QWidget(self.centralwidget) # Working line
# The next line does NOT create the same shaded green box. Where is it breaking?
# self.widget = Widget_1(self.centralwidget) # Non-working line
self.widget.setGeometry(QRect(15, 150, 60, 75))
self.widget.setAutoFillBackground(False)
self.widget.setStyleSheet("background: rgb(170, 255, 0)")
self.widget.setObjectName("Widget1")
seating_main_window.setCentralWidget(self.centralwidget)
class Widget_1(QWidget):
def __init__(self, parent=None):
super().__init__()
self.setMinimumSize(10, 30) # I put this in thinking maybe I just couldn't see it
print("Test") # I see this output when run when Widget_1 is used above
class DemoApp(QMainWindow, Main_Window):
def __init__(self):
super().__init__()
self.setupUi(self)
if __name__ == '__main__': # if we're running file directly and not importing it
app = QApplication(sys.argv) # A new instance of QApplication
form = DemoApp() # We set the form to be our ExampleApp (design)
form.show() # Show the form
app.exec_() # run the main function
Accoriding to this Qt Wiki article:
How to Change the Background Color of QWidget
you must implement paintEvent in a custom QWidget subclass in order to use stylesheets. Also, since the widget is not part of a layout, you must give it a parent, otherwise it will not be shown. So your Widget_1 class must look like this:
from PyQt5.QtWidgets import QStyleOption, QStyle
from PyQt5.QtGui import QPainter
class Widget_1(QWidget):
def __init__(self, parent=None):
super().__init__(parent) # set the parent
print("Test")
def paintEvent(self, event):
option = QStyleOption()
option.initFrom(self)
painter = QPainter(self)
self.style().drawPrimitive(QStyle.PE_Widget, option, painter, self)
super().paintEvent(event)

Resources