After making a few interfaces using the PySide6 library I keep having this issue where the custom background that I initialize turns black when calling a setStyleSheet statement after the program has gone through a qthread process.
This is how I initialize the background:
background = QPixmap("Data\\background.png")
palette = QPalette()
palette.setBrush(QPalette.Window, background)
self.show()
self.setPalette(palette)
And this is how I make the qthread:
class Signaller(QObject):
progress = Signal(int)
finished = Signal()
class Generate(QThread):
def __init__(self):
QThread.__init__(self)
self.signaller = Signaller()
def run(self):
self.signaller.progress.emit(0)
#do stuff
self.signaller.progress.emit(1)
self.signaller.finished.emit()
class Main(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setEnabled(False)
QApplication.processEvents()
self.progressBar = QProgressDialog("Generating...", None, 0, 1, self)
self.progressBar.setWindowTitle("Status")
self.progressBar.setWindowModality(Qt.WindowModal)
self.worker = Generate()
self.worker.signaller.progress.connect(self.set_progress)
self.worker.signaller.finished.connect(self.patch_finished)
self.worker.start()
def set_progress(self, progress):
self.progressBar.setValue(progress)
def patch_finished(self):
box = QMessageBox(self)
box.setWindowTitle("Done")
box.setText("Mod generated !")
box.exec()
self.setEnabled(True)
Yet calling this statement after the program's gone through the qthread at least once removes the background:
self.seed_field.setStyleSheet("color: #ffffff")
And it doesn't happen if the qthread hasn't triggered beforehand
Here is a fully working example:
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
import sys
class Signaller(QObject):
progress = Signal(int)
finished = Signal()
class Generate(QThread):
def __init__(self):
QThread.__init__(self)
self.signaller = Signaller()
def run(self):
self.signaller.progress.emit(0)
#do stuff
self.signaller.progress.emit(1)
self.signaller.finished.emit()
class Main(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setStyleSheet("QWidget{background:transparent; color: #ffffff; font-family: Cambria; font-size: 18px}"
+ "QMessageBox{background-color: #1d1d1d}"
+ "QDialog{background-color: #1d1d1d}"
+ "QProgressDialog{background-color: #1d1d1d}"
+ "QPushButton{background-color: #1d1d1d}"
+ "QSpinBox{background-color: #1d1d1d}"
+ "QLineEdit{background-color: #1d1d1d}"
+ "QLineEdit[text=\"\"]{color: #666666}"
+ "QMenu{background-color: #1d1d1d}"
+ "QToolTip{border: 0px; background-color: #1d1d1d; color: #ffffff; font-family: Cambria; font-size: 18px}")
grid = QGridLayout()
grid.setSpacing(10)
box_1_grid = QGridLayout()
self.box_1 = QGroupBox("Test")
self.box_1.setLayout(box_1_grid)
grid.addWidget(self.box_1, 0, 0, 1, 1)
button_1 = QPushButton("QThread")
button_1.clicked.connect(self.button_1_pressed)
box_1_grid.addWidget(button_1, 0, 0)
button_2 = QPushButton("StyleSheet")
button_2.clicked.connect(self.button_2_pressed)
box_1_grid.addWidget(button_2, 0, 1)
self.setLayout(grid)
self.setFixedSize(1280, 720)
background = QPixmap("background.png")
palette = QPalette()
palette.setBrush(QPalette.Window, background)
self.show()
self.setPalette(palette)
center = QScreen.availableGeometry(QApplication.primaryScreen()).center()
geo = self.frameGeometry()
geo.moveCenter(center)
self.move(geo.topLeft())
QApplication.processEvents()
def button_1_pressed(self):
self.setEnabled(False)
QApplication.processEvents()
self.progressBar = QProgressDialog("Generating...", None, 0, 1, self)
self.progressBar.setWindowTitle("Status")
self.progressBar.setWindowModality(Qt.WindowModal)
self.worker = Generate()
self.worker.signaller.progress.connect(self.set_progress)
self.worker.signaller.finished.connect(self.patch_finished)
self.worker.start()
def button_2_pressed(self):
self.box_1.setStyleSheet("color: #ffffff")
def set_progress(self, progress):
self.progressBar.setValue(progress)
def patch_finished(self):
box = QMessageBox(self)
box.setWindowTitle("Done")
box.setText("QThread done")
box.exec()
self.setEnabled(True)
def main():
app = QApplication(sys.argv)
main = Main()
sys.exit(app.exec())
if __name__ == '__main__':
main()
I had a similar problem, I solved it by setting the background not with a pixel, but with some_object.setStyleSheet(background: url("some/url")
Related
I am new to Cefpython and PyQt5 both. I have tried to follow the tutorial in the cefpython repository.
I was trying to embed cefpython inside a pyqt application and haven't achieved any success, what's wrong here?
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QWindow
from PyQt5.QtWidgets import *
from cefpython3 import cefpython as cef
from navbar import NavigationBar
class ChromiumApplication(QApplication):
def __init__(self):
super().__init__([])
self.timer = self.create_timer()
def create_timer(self):
timer = QTimer()
timer.timeout.connect(self.on_timeout)
timer.start(10)
return timer
def on_timeout(self):
cef.MessageLoopWork()
class ChromiumBrowserWindow(QMainWindow):
DEFAULT_TITLE = "Chromium Browser"
DEFAULT_WIDTH = 800
DEFAULT_HEIGHT = 600
def __init__(self):
super().__init__()
self.chrome = None
self.web_view = None
self.setWindowTitle(self.DEFAULT_TITLE)
self.init_window()
self.show()
def init_window(self):
self.resize(self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
self.web_view = WebViewWidget(parent=self)
self.chrome = NavigationBar(parent=self, browser=self.web_view.browser)
layout = QGridLayout()
layout.addWidget(self.chrome, 0, 0)
layout.setColumnStretch(0, 1)
layout.addWidget(self.web_view, 1, 0)
layout.setRowStretch(1, 2)
layout.setContentsMargins(0, 0, 0, 0)
frame = QFrame()
frame.setLayout(layout)
self.setCentralWidget(frame)
self.web_view.init_browser()
def closeEvent(self, event):
if self.web_view.browser is not None:
self.web_view.browser.CloseBrowser(True) # force=True
self.web_view.browser = None # required to close cleanly
class WebViewWidget(QWidget):
DEFAULT_URL = "https://www.google.com"
HANDLERS = []
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
self.browser = None
self.browser_window = None
self.timer = None
def init_browser(self):
self.browser_window = QWindow()
window_config = cef.WindowInfo()
rect_pos_and_size = [0, 0, self.width(), self.height()]
window_config.SetAsChild(self.get_window_handle(), rect_pos_and_size)
self.browser = cef.CreateBrowserSync(window_config, url=self.DEFAULT_URL)
self.set_handlers()
def get_window_handle(self):
return int(self.browser_window.winId())
def set_handlers(self):
for handler in self.HANDLERS:
self.browser.SetClientHanlder(handler(self))
if __name__ == "__main__":
sys.excepthook = cef.ExceptHook
cef.Initialize()
app = ChromiumApplication()
window = ChromiumBrowserWindow()
app.exec()
app.timer.stop()
cef.Shutdown()
I know the issue is with my code because the example provided by cefpython works perfectly on my machine. I have no clue what I did wrong here, any suggestions will help!
EDIT: (code for navigation bar)
from PyQt5.QtWidgets import (
QApplication,
QFrame,
QHBoxLayout,
QPushButton,
QLineEdit,
)
class NavigationBar(QFrame):
def __init__(self, parent=None, browser=None):
super().__init__()
self.parent = parent
self.browser = browser
self.back_btn = self.create_button("Back")
self.back_btn.clicked.connect(self.on_back)
self.forward_btn = self.create_button("Forward")
self.forward_btn.clicked.connect(self.on_forward)
self.refresh_btn = self.create_button("Refresh")
self.refresh_btn.clicked.connect(self.on_refresh)
self.url_bar = self.create_url_bar()
self.url_bar.returnPressed.connect(self.on_search)
self.frame_layout = QHBoxLayout()
self.init_layout()
def create_button(self, name):
button_icon_path = f"./{name}.svg"
button = QPushButton()
button.setStyleSheet(f"""
QPushButton {{
background-image: url("{button_icon_path}");
background-repeat: no-repeat;
background-position: center;
background-color: rgba(0, 0, 0, 0.1);
border: 10px;
border-radius: 8px;
padding: 10px;
}}
QPushButton:hover {{
background-color: rgba(0, 0, 0, 0.5);
}}
QPushButton:pressed {{
background-color: none;
}}
""")
return button
def create_url_bar(self):
search = QLineEdit()
search.setStyleSheet("""QLineEdit {
min-width: 300px;
padding: 10px;
margin-left: 50px;
margin-right: 30px;
border-width: 10px;
border-radius: 8px;
background-color: rgba(0, 0, 0, 0.2);
color: white;
}
QLineEdit:hover {
background-color: #454549;
}
""")
return search
def init_layout(self):
self.setStyleSheet("""
background: #2A292E;
max-height: 40px;
""")
self.frame_layout.addWidget(self.back_btn, 0)
self.frame_layout.addWidget(self.forward_btn, 0)
self.frame_layout.addWidget(self.refresh_btn, 0)
self.frame_layout.addWidget(self.url_bar, 1)
self.setLayout(self.frame_layout)
def on_back(self):
if self.browser is not None:
self.browser.GoBack()
def on_forward(self):
if self.browser is not None:
self.browser.GoForward()
def on_refresh(self):
if self.browser is not None:
self.browser.Reload()
def on_search(self):
if self.browser is not None:
url = self.url_bar.text()
self.browser.LoadUrl(url)
if __name__ == "__main__":
app = QApplication([])
nav = NavigationBar()
nav.show()
app.exec()
The problem is that the QWindow used to render the browser is hidden. The solution is to create a QWidget using QWidget::createWindowContainer() and add it using a layout.
import sys
from cefpython3 import cefpython as cef
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QWindow
from PyQt5.QtWidgets import QApplication, QFrame, QGridLayout, QMainWindow, QVBoxLayout, QWidget
from navbar import NavigationBar
class ChromiumApplication(QApplication):
def __init__(self):
super().__init__([])
self.timer = self.create_timer()
def create_timer(self):
timer = QTimer()
timer.timeout.connect(self.on_timeout)
timer.start(10)
return timer
def on_timeout(self):
cef.MessageLoopWork()
class ChromiumBrowserWindow(QMainWindow):
DEFAULT_TITLE = "Chromium Browser"
DEFAULT_WIDTH = 800
DEFAULT_HEIGHT = 600
def __init__(self):
super().__init__()
self.chrome = None
self.web_view = None
self.setWindowTitle(self.DEFAULT_TITLE)
self.init_window()
self.show()
def init_window(self):
self.resize(self.DEFAULT_WIDTH, self.DEFAULT_HEIGHT)
self.web_view = WebViewWidget()
self.chrome = NavigationBar(parent=self, browser=self.web_view.browser)
frame = QFrame()
self.setCentralWidget(frame)
layout = QGridLayout(frame)
layout.addWidget(self.chrome, 0, 0)
layout.setColumnStretch(0, 1)
layout.addWidget(self.web_view, 1, 0)
layout.setRowStretch(1, 2)
layout.setContentsMargins(0, 0, 0, 0)
def closeEvent(self, event):
if self.web_view.browser is not None:
self.web_view.browser.CloseBrowser(True)
del self.web_view.browser
class WebViewWidget(QWidget):
DEFAULT_URL = "https://www.google.com"
HANDLERS = []
def __init__(self, parent=None):
super().__init__(parent)
self._browser = None
self._browser_widget = None
lay = QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
self.init_browser()
#property
def browser(self):
return self._browser
#browser.deleter
def browser(self):
self._browser = None
def init_browser(self):
browser_window = QWindow()
window_config = cef.WindowInfo()
window_config.SetAsChild(
int(browser_window.winId()), list(self.rect().getRect())
)
self._browser = cef.CreateBrowserSync(window_config, url=self.DEFAULT_URL)
self._browser_widget = QWidget.createWindowContainer(browser_window)
self.layout().addWidget(self._browser_widget)
self.set_handlers()
def set_handlers(self):
for handler in self.HANDLERS:
self.browser.SetClientHanlder(handler(self))
def resizeEvent(self, event):
if self.browser and self._browser_widget:
self.browser.SetBounds(*self._browser_widget.geometry().getRect())
self.browser.NotifyMoveOrResizeStarted()
if __name__ == "__main__":
sys.excepthook = cef.ExceptHook
cef.Initialize()
app = ChromiumApplication()
window = ChromiumBrowserWindow()
app.exec()
app.timer.stop()
cef.Shutdown()
I have two QWidget windows as in the pictures. The first picture where I get the input of the variable, the other picture where I process the variable to show the user. However, I did not manage to use the variable in the other class (QWidget). To summarize it ;
This is the window I have entered my data.
This is the second window and class where I want to process and show the result. I need to use the variable defined in the first class. I have a calculation function in the second class that does the calculation like 2math. piMain. Diameter. Then I need to recall this function to show the result again.
You can find the all code below.
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
import sys
import math
from PyQt5.QtGui import QPixmap,QFont
import sqlite3
class Main(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Calculation")
self.setGeometry(450,100,1250,600)
self.UI()
self.show()
def UI(self):
self.mainDesign()
self.layouts()
def mainDesign(self):
self.setStyleSheet('background-color:white')
#CUSTOMER INFORMATION BUTTONS AND TEXT###
#### CALCULATE BUTTONS###
self.buttonCalcBillet = QPushButton("Calculate")
self.buttonCalcBillet.setStyleSheet(
'background-color: orange;'
'color: black;'
)
### CALCULATE BUTTONS CLICKED ###
self.buttonCalcBillet.clicked.connect(self.billetCalculationResults)
######
self.Title = QLabel("Some Maths")
self.Title.setAlignment(QtCore.Qt.AlignHCenter)
self.Title.setStyleSheet('font-size: 18pt; font-family: arial,sans-serif')
self.DiameterLabel = QLabel("Diameter")
self.DiameterLabel.setStyleSheet('font-size: 12pt; font-family: arial,sans-serif')
self.DiameterQline = QLineEdit()
self.DiameterQline.setPlaceholderText("Please Enter Diameter in mm")
self.DiameterQline.setStyleSheet(
'font-family:Hack,monospace;'
'font:12px;'
'mind-width:20em;'
)
def layouts(self):
#####LAYOUTS#########
self.mainLayout = QHBoxLayout()
self.billetLayout = QFormLayout()
###ADDING CHILD LAYOUTS TO MAIN LAYOUTS######
self.mainLayout.addLayout(self.billetLayout,350)
###CALCULATION BUTTON WIDGETS###
self.billetLayout.addRow(self.Title)
self.billetLayout.addRow(self.DiameterLabel, self.DiameterQline)
self.billetLayout.addRow(self.buttonCalcBillet)
####SETTING MAIN LAYOUT###
self.setLayout(self.mainLayout)
def billetCalculationResults(self):
self.billetCalculation = BilletCalculationResults()
self.GetValues()
self.close()
def GetValues(self):
self.Diameter = float(self.DiameterQline.text())
class BilletCalculationResults(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Calculation Results")
self.setGeometry(450,150,350,600)
####CONSTRUCTION OF THE FIRST BILLET CLASS ###
self.UI()
self.show()
def UI(self):
self.billetCalculationPageDesign()
self.billetCalculationLayouts()
def billetCalculationPageDesign(self):
### BILLET RESULTS OF CALCULATION PAGE DESIGN ###
self.billetCalSurfaceAreaLabel = QLabel("Surface Area : ")
self.billetCalSurfaceAreaLabel.setStyleSheet('font-size: 12pt; font-family: arial,sans-serif')
self.billetCalSurfaceAreaLabelResult = QLabel(" : ")
self.billetCalSurfaceAreaLabelResult.setStyleSheet('font-size: 12pt; font-family: arial,sans-serif')
def billetCalculationLayouts(self):
## BILLET RESULTS OF CALCULATION PAGE DESIGN ###
self.billetMainLayout = QFormLayout()
self.billetMainLayout.addRow(self.billetCalSurfaceAreaLabel,self.billetCalSurfaceAreaLabelResult)
self.setLayout(self.billetMainLayout)
##def calculation():
## Something like : return Main.Diameter * 2 * math.pi
def main():
APP = QApplication(sys.argv)
window = Main()
sys.exit(APP.exec())
if __name__== '__main__':
main()
To pass and argument to another class you can pass it like this:
self.billetCalculation = BilletCalculationResults(self.GetValues())
And to use it in class BilletCalculationResult init method like this:
def __init__(self, diameter):
self.diameter = diameter
Full code below:
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
import sys
import math
from PyQt5.QtGui import QPixmap, QFont
import sqlite3
class Main(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Calculation")
self.setGeometry(450, 100, 1250, 600)
self.UI()
self.show()
def UI(self):
self.mainDesign()
self.layouts()
def mainDesign(self):
self.setStyleSheet('background-color:white')
# CUSTOMER INFORMATION BUTTONS AND TEXT###
#### CALCULATE BUTTONS###
self.buttonCalcBillet = QPushButton("Calculate")
self.buttonCalcBillet.setStyleSheet(
'background-color: orange;'
'color: black;'
)
### CALCULATE BUTTONS CLICKED ###
self.buttonCalcBillet.clicked.connect(self.billetCalculationResults)
######
self.Title = QLabel("Some Maths")
self.Title.setAlignment(QtCore.Qt.AlignHCenter)
self.Title.setStyleSheet('font-size: 18pt; font-family: arial,sans-serif')
self.DiameterLabel = QLabel("Diameter")
self.DiameterLabel.setStyleSheet('font-size: 12pt; font-family: arial,sans-serif')
self.DiameterQline = QLineEdit()
self.DiameterQline.setPlaceholderText("Please Enter Diameter in mm")
self.DiameterQline.setStyleSheet(
'font-family:Hack,monospace;'
'font:12px;'
'mind-width:20em;'
)
def layouts(self):
#####LAYOUTS#########
self.mainLayout = QHBoxLayout()
self.billetLayout = QFormLayout()
###ADDING CHILD LAYOUTS TO MAIN LAYOUTS######
self.mainLayout.addLayout(self.billetLayout, 350)
###CALCULATION BUTTON WIDGETS###
self.billetLayout.addRow(self.Title)
self.billetLayout.addRow(self.DiameterLabel, self.DiameterQline)
self.billetLayout.addRow(self.buttonCalcBillet)
####SETTING MAIN LAYOUT###
self.setLayout(self.mainLayout)
def billetCalculationResults(self):
self.billetCalculation = BilletCalculationResults(self.GetValues())
self.close()
def GetValues(self):
return float(self.DiameterQline.text())
class BilletCalculationResults(QWidget):
def __init__(self, diameter):
self.diameter = diameter
super().__init__()
self.setWindowTitle("Calculation Results")
self.setGeometry(450, 150, 350, 600)
####CONSTRUCTION OF THE FIRST BILLET CLASS ###
self.UI()
self.show()
def UI(self):
self.billetCalculationPageDesign()
self.billetCalculationLayouts()
def billetCalculationPageDesign(self):
### BILLET RESULTS OF CALCULATION PAGE DESIGN ###
print(self.calculation())
self.billetCalSurfaceAreaLabel = QLabel("Surface Area : ")
self.billetCalSurfaceAreaLabel.setStyleSheet('font-size: 12pt; font-family: arial,sans-serif')
self.billetCalSurfaceAreaLabelResult = QLabel(f"{str(self.calculation())}")
self.billetCalSurfaceAreaLabelResult.setStyleSheet('font-size: 12pt; font-family: arial,sans-serif')
def billetCalculationLayouts(self):
## BILLET RESULTS OF CALCULATION PAGE DESIGN ###
self.billetMainLayout = QFormLayout()
self.billetMainLayout.addRow(self.billetCalSurfaceAreaLabel, self.billetCalSurfaceAreaLabelResult)
self.setLayout(self.billetMainLayout)
def calculation(self):
return self.diameter * 2 * math.pi
def main():
APP = QApplication(sys.argv)
window = Main()
sys.exit(APP.exec())
if __name__ == '__main__':
main()
I would like to display the current date and time on the top of multiple windows, so I created a class of this top widget. It works, however when I switch to another window, the timer keeps running on the previous window too. How can I stop the timer right before switching window, perhaps keep the same instance running on the new window?
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QWidget
import sys
from datetime import datetime
CurrentWindow = None
class TopBar(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.labelTime = QtWidgets.QLabel(self)
self.labelTime.setStyleSheet("background-color: rgba(0, 0, 0, 0); color: white")
background = QtWidgets.QWidget(self)
background.setStyleSheet("background-color: rgba(0, 191, 255, 0.6)")
background.setGeometry(0, 0, 480, 30)
hbox = QHBoxLayout(background)
hbox.setContentsMargins(10, 0, 10, 0)
hbox.addWidget(self.labelTime, alignment=QtCore.Qt.AlignRight)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.displayTime)
self.timer.start()
self.displayTime()
def displayTime(self):
print(self.parent())
self.labelTime.setText(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
class Window1(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(480, 320)
self.centralwidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.centralwidget)
widgetTop = QtWidgets.QWidget(self.centralwidget)
widgetTop.setGeometry(0, 0, 480, 30)
layoutTop = QHBoxLayout(widgetTop)
layoutTop.addWidget(TopBar())
layoutTop.setContentsMargins(0, 0, 0, 0)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setText('Go to Window2')
self.pushButton.clicked.connect(self.goToWindow2)
layoutCenter = QHBoxLayout(self.centralwidget)
layoutCenter.addWidget(self.pushButton, alignment=QtCore.Qt.AlignCenter)
self.show()
def goToWindow2(self):
global CurrentWindow
CurrentWindow = Window2()
self.close()
class Window2(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.resize(480, 320)
self.centralwidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.centralwidget)
widgetTop = QtWidgets.QWidget(self.centralwidget)
widgetTop.setGeometry(0, 0, 480, 30)
layoutTop = QHBoxLayout(widgetTop)
layoutTop.addWidget(TopBar())
layoutTop.setContentsMargins(0, 0, 0, 0)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setText('Go to Window1')
self.pushButton.clicked.connect(self.goToWindow1)
layoutCenter = QHBoxLayout(self.centralwidget)
layoutCenter.addWidget(self.pushButton, alignment=QtCore.Qt.AlignCenter)
self.show()
def goToWindow1(self):
global CurrentWindow
CurrentWindow = Window1()
self.close()
if __name__=='__main__':
app = QApplication(sys.argv)
ex = Window1()
sys.exit(app.exec_())
I don't think that the QTimer execution slows down the application.
But I will still show you how to stop it from another window. To do this you must access the object so you must become a member of the class, and then when you change the window you stop it with the stop method of the QTimer.
class Window1(QMainWindow):
# ...
def initUI(self):
# ...
layoutTop = QHBoxLayout(widgetTop)
self.topbar = TopBar()
layoutTop.addWidget(self.topbar)
layoutTop.setContentsMargins(0, 0, 0, 0)
# ...
def goToWindow2(self):
global CurrentWindow
self.topbar.timer.stop()
CurrentWindow = Window2()
self.close()
class Window2(QMainWindow):
# ...
def initUI(self):
# ...
layoutTop = QHBoxLayout(widgetTop)
self.topbar = TopBar()
layoutTop.addWidget(self.topbar)
layoutTop.setContentsMargins(0, 0, 0, 0)
# ...
def goToWindow1(self):
global CurrentWindow
self.topbar.timer.stop()
CurrentWindow = Window1()
self.close()
If you still consider that the cause of the error is to have several QTimer then in the following code there will only be one TopBar and they will change the widget using a QStackedWidget
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QWidget
import sys
from datetime import datetime
class TopBar(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setAttribute(QtCore.Qt.WA_StyledBackground)
self.labelTime = QtWidgets.QLabel()
self.labelTime.setStyleSheet("background-color: rgba(0, 0, 0, 0); color: white")
self.setStyleSheet("background-color: rgba(0, 191, 255, 0.6)")
self.setFixedHeight(30)
hbox = QHBoxLayout(self)
hbox.setContentsMargins(10, 0, 10, 0)
hbox.addWidget(self.labelTime, alignment=QtCore.Qt.AlignRight)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.displayTime)
self.timer.start()
self.displayTime()
def displayTime(self):
self.labelTime.setText(datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
class Window(QWidget):
changeWindow = QtCore.pyqtSignal(int)
def changeTo(self, index):
def callback():
self.changeWindow.emit(index)
return callback
class Window1(Window):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.pushButton = QtWidgets.QPushButton()
self.pushButton.setText("Go to Window2")
self.pushButton.clicked.connect(self.changeTo(1))
layoutCenter = QHBoxLayout(self)
layoutCenter.addWidget(self.pushButton, alignment=QtCore.Qt.AlignCenter)
class Window2(Window):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.pushButton = QtWidgets.QPushButton()
self.pushButton.setText("Go to Window1")
self.pushButton.clicked.connect(self.changeTo(0))
layoutCenter = QHBoxLayout(self)
layoutCenter.addWidget(self.pushButton, alignment=QtCore.Qt.AlignCenter)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.initUI()
def initUI(self):
self.resize(480, 320)
self.centralwidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.centralwidget)
self.topbar = TopBar()
lay = QtWidgets.QVBoxLayout(self.centralwidget)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(self.topbar)
stacked_widget = QtWidgets.QStackedWidget()
lay.addWidget(stacked_widget)
for w in (Window1(), Window2()):
stacked_widget.addWidget(w)
if isinstance(w, Window):
w.changeWindow.connect(stacked_widget.setCurrentIndex)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I'm trying to create a widget like the message box at the bottom in this picture:
It should overlay the main widget. I have 2 ways to do this:
With a QFrame and rounded corners
With a mask
But there's a problem with each of them:
The QFrame approach seems like a better idea but the background isn't transparent. This means that even though the borders have a radius, the background is still making it a rectangle. It's somewhat noticeable in the picture. Unfortunately, this doesn't seem to work. Same for self.setStyleSheet("background:transparent").
The mask looks very pixelated, and it's not the expected behavior because the mask used can only be a simple QRegion. These don't have squircles, which would be ideal.
Here's what the code looks like:
class Message(QFrame):
"""
A temporary message to show information in the GUI.
"""
def __init__(self, msg: str, *args, destroy_time: int = None):
super().__init__(*args)
# Main layout
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
# The label
label = QLabel(msg)
label.setFont(Fonts.text)
label.setStyleSheet(f"color: {Colors.fg};"
"padding: 20px;")
self.layout.addWidget(label)
self.setStyleSheet(f"background-color: {Colors.bg};"
"border-radius: 30px;")
# region = QRegion(self.x(), self.y(), self.sizeHint().width(),
# self.sizeHint().height(), QRegion.Ellipse)
# self.setMask(region)
self.adjustSize()
Edit for S. Nick: Your solution only works if the Message widget is the only widget in the application. The intended usage is this:
class MainWindow(QWidget):
def __init__(self, player: QWidget):
super().__init__()
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(player)
msg = Message("I can't make this work", self)
where player is the main widget, and the message overlays it when it appears. Imagine it being an image, which is overlayed by the message. Sorry for not explaining myself correctly.
Try it:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Message(QWidget): #(QFrame): #
def __init__(self, msg: str, *args, destroy_time: int = None):
super().__init__(*args)
self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) # <---
self.setAttribute(Qt.WA_TranslucentBackground) # <---
Colors_fg = "#fa0"
Colors_bg = "rgba( 155, 155, 155, 150)" # <---
# Main layout
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
# The label
label = QLabel(msg, alignment=Qt.AlignCenter)
label.setFont(QFont("Times", 17, QFont.Bold, italic=True)) #(Fonts.text)
label.setStyleSheet(f"color: {Colors_fg};"
"padding: 0px;")
self.layout.addWidget(label)
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.setStyleSheet(f"background-color: {Colors_bg};"
"min-height: 70px;"
"max-height: 70px;"
"width: 200px;"
"border-radius: 30px;"
)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.adjustSize()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Message("Hello \nWorld")
w.resize(400, 200)
w.show()
sys.exit(app.exec_())
Update
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Message(QDialog): #(QFrame):
def __init__(self, msg: str, *args, destroy_time: int = None):
super().__init__(*args)
self.setWindowFlags(self.windowFlags() |
Qt.FramelessWindowHint |
Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.widget = QWidget(self)
self.widget.setObjectName('Custom_Widget')
layout = QVBoxLayout(self)
layout.addWidget(self.widget)
self.layout = QGridLayout(self.widget)
self.layout.addItem(QSpacerItem(
40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 0)
self.layout.addWidget(QPushButton('r', self,
clicked=self.accept,
objectName='closeButton'), 0, 1)
# The label
label = QLabel(msg)
label.setFont(QFont("Times", 17, QFont.Bold, italic=True)) #((Fonts.text)
self.layout.addWidget(label, 2, 0, 5, 2, alignment=Qt.AlignCenter)
self.adjustSize()
def mousePressEvent(self, event):
self.old_Pos = event.globalPos()
self.old_width = self.width()
self.old_height = self.height()
def mouseMoveEvent(self, event):
if (event.buttons() == Qt.LeftButton):
delta = QPoint (event.globalPos() - self.old_Pos)
if (self.old_Pos.x() > self.x() + self.old_width - 20) or \
(self.old_Pos.y() > self.y() + self.old_height - 20):
w = self.old_width+delta.x() if self.old_width+delta.x() > 500 else 500
h = self.old_height+delta.y() if self.old_height+delta.y() > 400 else 400
self.setFixedSize(w, h)
else:
self.move(self.x() + delta.x(), self.y() + delta.y())
self.old_Pos = event.globalPos()
class MainWindow(QWidget):
def __init__(self, player: QWidget):
super().__init__()
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(player)
self.msg = Message("I can't make this work")#, self)
self.msg.show()
Stylesheet = """
#Custom_Widget {
background: rgba( 155, 155, 155, 150);
border-radius: 20px;
border: 2px solid #ff2025;
}
#closeButton {
min-width: 36px;
min-height: 36px;
font-family: "Webdings";
qproperty-text: "r";
border-radius: 10px;
}
#closeButton:hover {
color: #ccc;
background: red;
}
"""
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet(Stylesheet)
w = MainWindow(QPushButton("player"))
w.resize(400, 200)
w.show()
sys.exit(app.exec_())
Update 2
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Message(QFrame): # QDialog
def __init__(self, msg: str, *args, destroy_time: int = None):
super().__init__(*args)
# self.setWindowFlags(self.windowFlags() |
# Qt.FramelessWindowHint #|Qt.WindowStaysOnTopHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.widget = QWidget(self)
self.widget.setObjectName('Custom_Widget')
layout = QVBoxLayout(self)
layout.addWidget(self.widget)
self.layout = QGridLayout(self.widget)
self.layout.addItem(QSpacerItem(
40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 0)
self.layout.addWidget(QPushButton('r', self,
# clicked=self.accept,
clicked=self.close,
objectName='closeButton'), 0, 1)
# The label
label = QLabel(msg)
label.setFont(QFont("Times", 15, QFont.Bold, italic=True)) #((Fonts.text)
self.layout.addWidget(label, 2, 0, 5, 2, alignment=Qt.AlignCenter)
self.adjustSize()
class MainWindow(QWidget):
def __init__(self, player: QWidget):
super().__init__()
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(player)
player.clicked.connect(self._msg)
def _msg(self):
self.msg = Message("I can't make this work\nself.msg.setGeometry(10, 10, 480, 150)", self)
self.msg.setGeometry(10, 10, 480, 150)
self.msg.show()
Stylesheet = """
#Custom_Widget {
background: rgba( 155, 155, 155, 150);
border-radius: 20px;
border: 2px solid #ff2025;
}
#closeButton {
min-width: 36px;
min-height: 36px;
font-family: "Webdings";
qproperty-text: "r";
border-radius: 10px;
}
#closeButton:hover {
color: #ccc;
background: red;
}
"""
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet(Stylesheet)
w = MainWindow(QPushButton("player"))
w.resize(500, 400)
w.show()
sys.exit(app.exec_())
Here is the code:
#!/usr/bin/env python3
import sys, time
from PySide import QtCore, QtGui
import base64
# Usage: Toast('Message')
class Toast(QtGui.QDialog):
def __init__(self, title, msg, duration=2):
QtGui.QDialog.__init__(self)
self.duration = duration
self.title_label = QtGui.QLabel(title)
self.title_label.setAlignment(QtCore.Qt.AlignLeft)
self.msg_label = QtGui.QLabel(msg)
self.icon_button = QLabelButton()
img_b64 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAITgAACE4BjDEA7AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABJdEVYdENvcHlyaWdodABQdWJsaWMgRG9tYWluIGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL3B1YmxpY2RvbWFpbi9Zw/7KAAAB2ElEQVRIibWVPW/TUBiFz7mJTBFSGgnUqmABRgpMUYi53pCK1IWxUxd2BgYk/goDAzuq+AFILEhIZUuq/ACPrYRKGSJPdHkPQx3UOK7tJOKd7Guf57nXH++lJFRVr9e70el03pLcBnAnH/4t6SzLsvdpml5U5duVdABhGDLLsj6AjSvD9wFshWHIujzrVgBcrqLb7b6U9AoASH6aTqdf62YPAK6WDiBN0wszO52dm9lpEzhQs4LhcNhzzj13zj2TtDUXJH+Z2bGZ/ZhMJulSApL03r+WtNdoluS38Xj8USWw0kcUx/F+UzgASNqL43i/7NqCwHu/A+CgKfxKHeTZagGAPsnWsvQ8028ieLIsvCq7IJD0eFV6WXZO4L3fzFvCSkVy23u/ea2A5KNV4dcx5gRm9nBdQZFRfAcP1hUUGXMC59zagiLjn2AwGNwCsPCjrFA7OWteEATBrqRG3bWqJLkgCHZn523gsrnFcdwi+YXkrGEJAMxMs+OSonNutukwF9DMWiQpSUyS5Kmku+vOvKzM7KxtZu8A3PwfAgB/2iQ/m9m9qrtIxgBuF4bPJY1qBD8b7clJkryQ9KYg/TAajb7XZRt9NVEUHUk6BHAC4ETSYRRFR02yfwEMBLRPQVtfqgAAAABJRU5ErkJggg=="
pixmap = QtGui.QPixmap()
pixmap.loadFromData(base64.b64decode(img_b64))
self.icon_button.setPixmap(pixmap)
self.icon_button.resize(20, 20)
self.connect(self.icon_button, QtCore.SIGNAL("clicked()"), self.close)
title_layout = QtGui.QVBoxLayout()
title_layout.addWidget(self.title_label)
title_layout.addWidget(self.msg_label)
layout = QtGui.QHBoxLayout()
layout.addWidget(self.icon_button)
layout.addLayout(title_layout)
self.setGeometry(0, 0, 200, 70)
self.setLayout(layout)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
# self.setStyleSheet("border: 1px solid red; border-radius: 5px;")
self.toastThread = ToastThread(self.duration)
self.toastThread.finished.connect(self.close)
self.toastThread.start()
class ToastThread(QtCore.QThread):
def __init__(self, n_seconds):
QtCore.QThread.__init__(self)
self.n_seconds = n_seconds
def run(self):
time.sleep(self.n_seconds)
class QLabelButton(QtGui.QLabel):
def __init(self, parent):
QLabel.__init__(self, parent)
def mouseReleaseEvent(self, ev):
self.emit(QtCore.SIGNAL('clicked()'))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
program = Toast("hi", "there", 10)
program.show()
sys.exit(app.exec_())
Apparent the image label on the left is taking too much space. How can I fix this?
A horizontal layout will give each widget an equal amount of space by default, but you can adjust the ratio like this:
layout.addWidget(self.icon_button, 1)
layout.addLayout(title_layout, 3)
So that gives the title three times as much space as the icon.
This should work, just add:
self.icon_button.setFixedWidth(30)
by default it seperates the two widgets with equal width (=100)