How to pass changing/updating data to another pop-up window? - python

I got the following code for Python GUI. I found I cannot update the data running in the main window to the pop-up window.
In this example I use some random data to mimic the video frames and use a timer to update the data (the updateData() funciton).
I want to see the green curve in the pop-up window changes... But I cannot find an effective way to pass the changing data to the pop-up window.
Should I use the pyqtSignal() to define a custom signal? I read this page. Should I define the updateData() slot function as a custom signal, then connect to the other slot function plot_data() ?
"""
1/15/2021 Yuanhang Zhang
"""
from PyQt5.QtWidgets import QMainWindow,QApplication,QGridLayout,QWidget, QPushButton,QDockWidget
from PyQt5.QtCore import Qt,QTimer
from PyQt5.QtGui import QPixmap,QFont
import numpy as np
import pyqtgraph as pg
from pyqtgraph import GradientWidget
class power_window(QMainWindow):
# this window has no parent. It will appear as a free-floating window as we want.
def __init__(self,parent):
super().__init__()
self.setWindowTitle("Total power in the frame")
self.main_widget = QWidget()
self.main_layout = QGridLayout()
self.main_widget.setLayout(self.main_layout)
self.setCentralWidget(self.main_widget)
self.plot_plt = pg.PlotWidget() # this is our plot canvas
self.plot_plt.showGrid(x=True,y=True) # show grid
self.main_layout.addWidget(self.plot_plt, 1, 0, 3, 3)
# self.plot_plt.setYRange(max=100,min=0)
self.data_list = []
parent.timer.timeout.connect(self.plot_data) # update plot
# parent.updateData.change.connect(self.plot_data) # not working, may need to use pyqtSignal for custom signals
self.frame_sum_data = parent.updateData()
self.plot_data()
def plot_data(self):
self.data_list.append(self.frame_sum_data)
self.plot_plt.plot().setData(self.data_list,pen='g') # change the color of the pen
class CameraGUI(QMainWindow):
def __init__(self):
super().__init__()
## Create some random data to mimic video data
# scale: Standard deviation (spread or “width”) of the distribution.
# loc: Mean (“centre”) of the distribution.
self.data = np.random.normal(size=(15, 30, 30), loc=1024, scale=200).astype(np.uint16)
self.i = 0
self.power_gui = None # for the pop up window
self.initializeUI()
def initializeUI(self):
"""
Initialize the window and display its contents to the screen
"""
self.setGeometry(100,100,1000,800) # (x, y, width, height)
self.setWindowTitle('Camera GUI')
self.main_widget = pg.GraphicsLayoutWidget()
self.setCentralWidget(self.main_widget) # set the default widget of the MainWindow
self.createCameraWidgets()
self.show()
def updateLUT(self): ## change colormap (color look up table)
lut = self.gradient.getLookupTable(256)
return lut
def action_on_button_clicked(self): # for the pop-up windown
# https://www.learnpyqt.com/tutorials/creating-multiple-windows/
if self.power_gui is None:
# self.power_gui = power_window(np.sum(self.data[self.i]))
self.power_gui = power_window(self)
self.power_gui.show()
def createCameraWidgets(self):
"""
Setup widgets using QGridLayout
"""
self.gradient = GradientWidget(parent = self.main_widget,orientation='top')
self.gradient.sigGradientChanged.connect(self.updateLUT)
self.dock_widget = QDockWidget(self)
self.dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
# set initial location of dock widget in main window
self.addDockWidget(Qt.TopDockWidgetArea,self.dock_widget)
self.button = QPushButton('Power',self)
self.button.clicked.connect(self.action_on_button_clicked) ## --> not updating the data ??
self.dock_widget.setWidget(self.button)
## add view box
self.view1 = self.main_widget.addViewBox(row = 1, col = 0)
self.view2 = self.main_widget.addViewBox(row = 1, col = 1)
## lock the aspect ratio so pixels are always square
self.view1.setAspectLocked(True)
self.view2.setAspectLocked(True)
## Create image item
self.img1 = pg.ImageItem(border='w')
self.img2 = pg.ImageItem(border='w')
self.view1.addItem(self.img1)
self.view2.addItem(self.img2)
# timer of the main window
self.timer = QTimer()
self.timer.setInterval(200) # every 200 ms will emit timeout signal
self.timer.timeout.connect(self.updateData) # when timeout signal is emit, it will run updatedata() function
self.timer.start()
print(np.sum(self.data[self.i])) # this will only run once
print(self.i)
def updateData(self):
## Display the data
self.img1.setImage(self.data[self.i],lut = self.updateLUT()) # when i changes, set to display in img1
self.data2 = np.log(np.abs(np.fft.fft2(self.data[self.i]))) # calculate data2 beased on data[i]
self.i = (self.i+1) % self.data.shape[0] # update i
self.img2.setImage(self.data2,lut = self.updateLUT())
# print(self.i)
print(np.sum(self.data[self.i])) # this will keep running every 200 ms
return np.sum(self.data[self.i])
## run the program
if __name__ =="__main__":
import sys
app = QApplication(sys.argv)
window = CameraGUI()
window.updateData()
sys.exit(app.exec_())

I did some changes to your power_window class:
class power_window(QMainWindow):
# this window has no parent. It will appear as a free-floating window as we want.
def __init__(self,parent):
super().__init__()
self.setWindowTitle("Total power in the frame")
self.main_widget = QWidget()
self.main_layout = QGridLayout()
self.main_widget.setLayout(self.main_layout)
self.setCentralWidget(self.main_widget)
self.plot_plt = pg.PlotWidget() # this is our plot canvas
self.plot_plt.showGrid(x=True,y=True) # show grid
self.main_layout.addWidget(self.plot_plt, 1, 0, 3, 3)
# self.plot_plt.setYRange(max=100,min=0)
self.parent = parent
self.data_list = []
parent.timer.timeout.connect(self.plot_data) # update plot
# parent.updateData.change.connect(self.plot_data) # not working, may need to use pyqtSignal for custom signals
self.plot_data()
def plot_data(self):
self.frame_sum_data = self.parent.updateData()
self.data_list.append(self.frame_sum_data)
self.plot_plt.plot().setData(self.data_list,pen='g') # change the color of the pen
I think the problem is because your program calls the updateData() on the __init__ so it only gets the first value and never again updates that value.
Tell me if it does what you want.

Above is what the demo looks like. The pop-up window shows the 'real-time' sum-up values in the left view. The right view shows the FFT of the left.

Related

How to replace a widget with another in PyQt5

In my GUI application, I'm displaying a camera stream to a user. Now the thing is that the user will be able to see stream from only one camera at a time and in order to see streams from other cameras he must enter the credentials of the new camera like username, password and camera IP.
I want to do this using a dialog box. I was able to do that but everytime a new window popped up. I do how to switch between different cameras using QStackedLayout but this time I can't use that because the camera objects are created at runtime.
All I want is that on press of a button a dialog box should appear and the camera must be replaced once the credentials are entered.
code:
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
class CameraWidget(QtWidgets.QWidget):
"""Independent camera feed
Uses threading to grab IP camera frames in the background
#param width - Width of the video frame
#param height - Height of the video frame
#param stream_link - IP/RTSP/Webcam link
#param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
"""
def __init__(self, username, password, camera_ip, width=0, height=0, stream_link=0, aspect_ratio=False, parent=None, deque_size=1):
super(CameraWidget, self).__init__(parent)
# Initialize deque used to store frames read from the stream
self.deque = deque(maxlen=deque_size)
# Slight offset is needed since PyQt layouts have a built in padding
# So add offset to counter the padding
self.screen_width = 640
self.screen_height = 480
self.maintain_aspect_ratio = aspect_ratio
self.camera_stream_link = 'rtsp://{}:{}#{}/Streaming/Channels/2'.format(username, password, camera_ip)
# Flag to check if camera is valid/working
self.online = False
self.capture = None
self.video_frame = QtWidgets.QLabel()
self.load_network_stream()
# Start background frame grabbing
self.get_frame_thread = Thread(target=self.get_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
# Periodically set video frame to display
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.set_frame)
self.timer.start(.5)
print('Started camera: {}'.format(self.camera_stream_link))
def load_network_stream(self):
"""Verifies stream link and open new stream if valid"""
def load_network_stream_thread():
if self.verify_network_stream(self.camera_stream_link):
self.capture = cv2.VideoCapture(self.camera_stream_link)
self.online = True
self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
self.load_stream_thread.daemon = True
self.load_stream_thread.start()
def verify_network_stream(self, link):
"""Attempts to receive a frame from given link"""
cap = cv2.VideoCapture(link)
if not cap.isOpened():
return False
cap.release()
return True
def get_frame(self):
"""Reads frame, resizes, and converts image to pixmap"""
while True:
try:
if self.capture.isOpened() and self.online:
# Read next frame from stream and insert into deque
status, frame = self.capture.read()
if status:
self.deque.append(frame)
else:
self.capture.release()
self.online = False
else:
# Attempt to reconnect
print('attempting to reconnect', self.camera_stream_link)
self.load_network_stream()
self.spin(2)
self.spin(.001)
except AttributeError:
pass
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtWidgets.QApplication.processEvents()
def set_frame(self):
"""Sets pixmap image to video frame"""
if not self.online:
self.spin(1)
return
if self.deque and self.online:
# Grab latest frame
frame = self.deque[-1]
# Keep frame aspect ratio
if self.maintain_aspect_ratio:
self.frame = imutils.resize(frame, width=self.screen_width)
# Force resize
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
h, w, ch = self.frame.shape
bytesPerLine = ch * w
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix)
def get_video_frame(self):
return self.video_frame
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, username, password, camera_ip, parent=None):
super(MainWindow, self).__init__(parent)
# Top frame
self.top_frame = QtWidgets.QFrame()
self.top_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
self.camera = CameraWidget(username, password, camera_ip)
self.top_layout = QtWidgets.QHBoxLayout()
self.top_layout.addWidget(self.camera.get_video_frame())
self.top_frame.setLayout(self.top_layout)
# Bottom frame
self.btm_frame = QtWidgets.QFrame()
self.btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.button = QtWidgets.QPushButton('Change Camera')
self.button.clicked.connect(self.onClick)
self.btm_layout = QtWidgets.QHBoxLayout()
self.btm_layout.addStretch()
self.btm_layout.addWidget(self.button)
self.btm_layout.setContentsMargins(5, 5, 5, 5)
self.btm_frame.setLayout(self.btm_layout)
self.widget = QtWidgets.QWidget()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.top_frame, 20)
self.layout.addWidget(self.btm_frame,1)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
def onClick(self):
"""
I want this function to open a dialog box
asking user to enter new cameras credentials
and display it.
"""
if __name__ == '__main__':
# Create main application window
app = QtWidgets.QApplication([])
app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
w = MainWindow('admin', 'vaaan#123', '192.168.1.51')
w.showMaximized()
sys.exit(app.exec_())
Initial answer
Is this close to what you have in mind?
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
class CameraWidget(QtWidgets.QWidget):
# no change
...
class ChangeDialog(QtWidgets.QDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, *kwargs)
QBtn = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
buttonBox = QtWidgets.QDialogButtonBox(QBtn)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
vlayout = QtWidgets.QVBoxLayout()
self.usernameEdit = QtWidgets.QLineEdit()
self.passwordEdit = QtWidgets.QLineEdit()
self.passwordEdit.setEchoMode(QtWidgets.QLineEdit.Password)
self.ipAddrEdit = QtWidgets.QLineEdit()
vlayout.addWidget(self.usernameEdit)
vlayout.addWidget(self.passwordEdit)
vlayout.addWidget(self.ipAddrEdit)
self.layout.addLayout(vlayout)
self.layout.addWidget(buttonBox)
#property
def username(self):
return self.usernameEdit.text()
#property
def password(self):
return self.passwordEdit.text()
#property
def ipAddress(self):
return self.ipAddrEdit.text()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, username, password, camera_ip, parent=None):
super(MainWindow, self).__init__(parent)
# Top frame
self.top_frame = QtWidgets.QFrame()
self.top_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
self.camera = CameraWidget(username, password, camera_ip)
self.top_layout = QtWidgets.QHBoxLayout()
self.top_layout.addWidget(self.camera.get_video_frame())
self.top_frame.setLayout(self.top_layout)
# Bottom frame
self.btm_frame = QtWidgets.QFrame()
self.btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.button = QtWidgets.QPushButton('Change Camera')
self.button.clicked.connect(self.onClick)
self.btm_layout = QtWidgets.QHBoxLayout()
self.btm_layout.addStretch()
self.btm_layout.addWidget(self.button)
self.btm_layout.setContentsMargins(5, 5, 5, 5)
self.btm_frame.setLayout(self.btm_layout)
self.widget = QtWidgets.QWidget()
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.top_frame, 20)
self.layout.addWidget(self.btm_frame,1)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.changeDialog = ChangeDialog()
self.changeDialog.accepted.connect(self.changeCamera)
def changeCamera(self):
self.camera = CameraWidget(
self.changeDialog.username,
self.changeDialog.password,
self.changeDialog.ipAddress)
# not sure if this is necessary
self.top_layout.takeAt(0)
self.top_layout.addWidget(self.camera.get_video_frame())
def onClick(self):
"""
I want this function to open a dialog box
asking user to enter new cameras credentials
and display it.
"""
self.changeDialog.exec()
if __name__ == '__main__':
# Create main application window
app = QtWidgets.QApplication([])
app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
w = MainWindow('admin', 'vaaan#123', '192.168.1.51')
w.showMaximized()
sys.exit(app.exec_())
Without being able to actually see something it's hard to tell if something is missing, but this should be the right direction.
Answering comments
About mandatory fields
First of all this proposal is very rough.
You should add QLabels before each QLineEdit.
Then you should code some validation logic. You do this by removing the default "OK" button that I put in and put your own button. When this button is pressed, you check that each input self (the dialog) is valid.
If this is the case you can call accept(). Otherwise you can use setFocus() on the first input that is invalid.
Display previously entered data
In my proposal I created a dialog that is stored with the MainWindow.
It is never destroyed so all the data is still alive. When you display the dialog for the second time it still holds the previous data.
You can create a new dialog object each time if you prefer, or clear all inputs.

Qt - Show widget or label above all widget

I want to display a loading screen every time a user presses a button (a process that takes a few seconds runs).
I want something like this
QSplashScreen does not help me because that is only used before opening the application and a QDialog is not useful for me because I want that by dragging the window the application will move along with the message Loading...
What do I have to use?
The only (safe) way to achieve this is to add a child widget without adding it to any layout manager.
The only things you have to care about is that the widget is always raised as soon as it's shown, and that the geometry is always updated to the parent widget (or, better, the top level window).
This is a slightly more advanced example, but it has the benefit that you can just subclass any widget adding the LoadingWidget class to the base classes in order to implement a loading mechanism.
from random import randrange
from PyQt5 import QtCore, QtGui, QtWidgets
class Loader(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
self.gradient = QtGui.QConicalGradient(.5, .5, 0)
self.gradient.setCoordinateMode(self.gradient.ObjectBoundingMode)
self.gradient.setColorAt(.25, QtCore.Qt.transparent)
self.gradient.setColorAt(.75, QtCore.Qt.transparent)
self.animation = QtCore.QVariantAnimation(
startValue=0., endValue=1.,
duration=1000, loopCount=-1,
valueChanged=self.updateGradient
)
self.stopTimer = QtCore.QTimer(singleShot=True, timeout=self.stop)
self.focusWidget = None
self.hide()
parent.installEventFilter(self)
def start(self, timeout=None):
self.show()
self.raise_()
self.focusWidget = QtWidgets.QApplication.focusWidget()
self.setFocus()
if timeout:
self.stopTimer.start(timeout)
else:
self.stopTimer.setInterval(0)
def stop(self):
self.hide()
self.stopTimer.stop()
if self.focusWidget:
self.focusWidget.setFocus()
self.focusWidget = None
def updateGradient(self, value):
self.gradient.setAngle(-value * 360)
self.update()
def eventFilter(self, source, event):
# ensure that we always cover the whole parent area
if event.type() == QtCore.QEvent.Resize:
self.setGeometry(source.rect())
return super().eventFilter(source, event)
def showEvent(self, event):
self.setGeometry(self.parent().rect())
self.animation.start()
def hideEvent(self, event):
# stop the animation when hidden, just for performance
self.animation.stop()
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
color = self.palette().window().color()
color.setAlpha(max(color.alpha() * .5, 128))
qp.fillRect(self.rect(), color)
text = 'Loading...'
interval = self.stopTimer.interval()
if interval:
remaining = int(max(0, interval - self.stopTimer.remainingTime()) / interval * 100)
textWidth = self.fontMetrics().width(text + ' 000%')
text += ' {}%'.format(remaining)
else:
textWidth = self.fontMetrics().width(text)
textHeight = self.fontMetrics().height()
# ensure that there's enough space for the text
if textWidth > self.width() or textHeight * 3 > self.height():
drawText = False
size = max(0, min(self.width(), self.height()) - textHeight * 2)
else:
size = size = min(self.height() / 3, max(textWidth, textHeight))
drawText = True
circleRect = QtCore.QRect(0, 0, size, size)
circleRect.moveCenter(self.rect().center())
if drawText:
# text is going to be drawn, move the circle rect higher
circleRect.moveTop(circleRect.top() - textHeight)
middle = circleRect.center().x()
qp.drawText(
middle - textWidth / 2, circleRect.bottom() + textHeight,
textWidth, textHeight,
QtCore.Qt.AlignCenter, text)
self.gradient.setColorAt(.5, self.palette().windowText().color())
qp.setPen(QtGui.QPen(self.gradient, textHeight))
qp.drawEllipse(circleRect)
class LoadingExtension(object):
# a base class to extend any QWidget subclass's top level window with a loader
def startLoading(self, timeout=0):
window = self.window()
if not hasattr(window, '_loader'):
window._loader = Loader(window)
window._loader.start(timeout)
# this is just for testing purposes
if not timeout:
QtCore.QTimer.singleShot(randrange(1000, 5000), window._loader.stop)
def loadingFinished(self):
if hasattr(self.window(), '_loader'):
self.window()._loader.stop()
class Test(QtWidgets.QWidget, LoadingExtension):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
# just a test widget
textEdit = QtWidgets.QTextEdit()
layout.addWidget(textEdit, 0, 0, 1, 2)
textEdit.setMinimumHeight(20)
layout.addWidget(QtWidgets.QLabel('Timeout:'))
self.timeoutSpin = QtWidgets.QSpinBox(maximum=5000, singleStep=250, specialValueText='Random')
layout.addWidget(self.timeoutSpin, 1, 1)
self.timeoutSpin.setValue(2000)
btn = QtWidgets.QPushButton('Start loading...')
layout.addWidget(btn, 2, 0, 1, 2)
btn.clicked.connect(lambda: self.startLoading(self.timeoutSpin.value()))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
test = Test()
test.show()
sys.exit(app.exec_())
Please check Qt::WindowFlags. The Qt::SplashScreen flag will give you splash screen experience without usage QSplashScreen (you can use it with all widget as show) or, better, use QDialog with this flag.
For moving, probably fine solution is not available but you can just use parent moveEvent to emmit signal. For example:
Main window:
moveEvent -> signal moved
Dialog:
signal move -> re-center window.
Its look as not hard.
By the way, I think block all GUI during application run is not the best solution. You you think use QProgressBar?
You can use this slot: void QWidget::raise().
Raises this widget to the top of the parent widget's stack.
After this call the widget will be visually in front of any overlapping sibling widgets.

how to open a QDialog widget on the center position of the main window

Im looking for a way to open a QDialog widget on the center position of the main window.
I have set the position of the mainwindow to center.
centerPoint = qtw.QDesktopWidget().availableGeometry().center()
qtRectangle.moveCenter(centerPoint)
to folow the dialog widget to the postion of the main window
i have set it to
msgb.move(self.pos().x(), self.pos().y())
the dialog window follows the positon of the main window , but it opens on the top left side of the main window, how can I change its position to the center of the main window ?
#!/usr/bin/env python
"""
startscreen
base window remit to specific tests
"""
import os
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
class Startscreen(qtw.QWidget):
'''
remit to one of three tests if widgets toggled/clicked
hide its self after
'''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# your code will go here
# interface
# position
qtRectangle = self.frameGeometry()
centerPoint = qtw.QDesktopWidget().availableGeometry().center()
qtRectangle.moveCenter(centerPoint)
self.move(qtRectangle.topLeft())
# size
self.resize(700, 410)
# frame title
self.setWindowTitle("Lambda")
# heading
heading_label = qtw.QLabel("Lambda Version 1.0")
heading_label.setAlignment(qtc.Qt.AlignHCenter)
# active user
activeuser_label = qtw.QLabel('Benutzer: ' + os.getlogin())
activeuser_label.setStyleSheet("background-color: rgb(234, 246, 22)")
activeuser_label.setAlignment(qtc.Qt.AlignRight | qtc.Qt.AlignTop)
# groubox for widget positioning
self.groupbox = qtw.QGroupBox(self)
# groupbox.setAlignment(qtc.Qt.AlignHCenter)
# layout and widgets
vlayout = qtw.QVBoxLayout()
vlayout.setAlignment(qtc.Qt.AlignHCenter)
self.particlesize_radiobutton = qtw.QRadioButton("test1")
vlayout.addWidget(self.particlesize_radiobutton)
self.dimensionalchange_radiobutton = qtw.QRadioButton("test2")
vlayout.addWidget(self.dimensionalchange_radiobutton)
self.dimensionalchangecook_radiobutton = qtw.QRadioButton("test3")
vlayout.addWidget(self.dimensionalchangecook_radiobutton)
self.select_button = qtw.QPushButton('select')
vlayout.addWidget(self.select_button)
self.groupbox.setLayout(vlayout)
# mainlayout
main_layout = qtw.QFormLayout()
main_layout.addRow(activeuser_label)
main_layout.addRow(heading_label)
main_layout.setVerticalSpacing(40)
main_layout.addRow(self.groupbox)
self.setLayout(main_layout)
# functionality
self.select_button.clicked.connect(self.open_box)
self.show()
def open_box(self):
msgb = qtw.QMessageBox()
msgb.setWindowTitle("title")
msgb.setText("hier sthet was")
msgb.move(self.pos().x(), self.pos().y())
run = msgb.exec_()
# msgb = qtw.QMessageBox()
# msgb.addButton()
# if x open new windwo
#
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
w = Startscreen()
sys.exit(app.exec_())
A widget has a position relative to its parent, and if it does not have a parent then it will be relative to the screen. And in the case of msgb it belongs to the second case so you will have to convert the coordinate of the center of the window to global coordinates (that is to say with respect to the screen). Even doing the above it will not be centered because the position is with respect to the topleft, that is, the msgb topleft will be in the center of the screen which is not desirable so you have to also take into account the size of the msgb. And the size of the msgb before and after it is displayed is different so with a QTimer it will be enough:
def open_box(self):
msgb = qtw.QMessageBox()
msgb.setWindowTitle("title")
msgb.setText("hier sthet was")
qtc.QTimer.singleShot(
0,
lambda: msgb.move(
self.mapToGlobal(self.rect().center() - msgb.rect().center())
),
)
run = msgb.exec_()

Wrong widget order using vbox layout PyQt

I am trying to put a QLabel widget on top of (ie before) a QLineEdit widget edit.
But it keeps appearing after the QLineEdit widget. My code,
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Flags
self.randFlag = False
self.sphereFlag = False
self.waterFlag = False
# Poly names
self.pNames = QtGui.QLabel("Import file name", self) # label concerned
self.polyNameInput = QtGui.QLineEdit(self) # line edit concerned
# Polytype selection
self.polyTypeName = QtGui.QLabel("Particle type", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("")
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
def onActivated(self, text):
# Do loads of clever stuff that I'm not at liberty to share with you
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
poly = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The window I get is below.
What am I missing? I thought QVbox allowed to stack things vertically in the order that you add the items to the main widget. (Are these sub-widget objects called widgets?)
The problem is because you are adding self.pNames label to layout twice.
#portion of your code
...
self.layout.addWidget(self.pNames) # here
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.pNames) # and here
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
...
The first time you add the QLabel, it gets added before the LineEdit and when you add it second time, it just moves to the bottom of LineEdit. This happens because there is only one object of QLabel which is self.pNames. It can be added to only one location. If you want to use two labels, consider creating two separate objects of QLabel

Removing thin borders between widgets in Qt

In particular I have 2 questions:
1) How can I remove those thin lines between the widgets? setMargin(0) and setSpacing(0)
are already set.
2) In a further step I want to remove the window title bar with FramelessWindowHint.
To drag the window, I'll bind a mouseevent on the upper dark yellow widget. Right now, the upper widget is a QTextEdit with suppressed keyboard interactions. For the draging purpose, I doubt this widget is good... So the question is, what other widgets are good to create a colored handle to drag the window? Perhaps QLabel?
EDIT: Here is the code. I only used QTestEdit-Widgets.
from PyQt4.QtGui import *
from PyQt4 import QtGui,QtCore
import sys
class Note(QWidget):
def __init__(self, parent = None):
super(QWidget, self).__init__(parent)
self.createLayout()
self.setWindowTitle("Note")
def createLayout(self):
textedit = QTextEdit()
grip = QTextEdit()
grip.setMaximumHeight(16) #reduces the upper text widget to a height to look like a grip of a note
grip.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) #suppresses the scroll bar that appears under a certain height
empty = QTextEdit()
empty.setMaximumHeight(16)
empty.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
resize = QTextEdit()
resize.setMaximumHeight(16)
resize.setMaximumWidth(16)
resize.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout = QVBoxLayout()
layout.addWidget(grip)
layout.addWidget(textedit)
layout.setMargin(0)
layout.setSpacing(0)
layoutBottom=QHBoxLayout()
layoutBottom.addWidget(empty)
layoutBottom.addWidget(resize)
layout.addLayout(layoutBottom)
self.setLayout(layout)
# Set Font
textedit.setFont(QFont("Arial",16))
# Set Color
pal=QtGui.QPalette()
rgb=QtGui.QColor(232,223,80) #Textwidget BG = yellow
pal.setColor(QtGui.QPalette.Base,rgb)
textc=QtGui.QColor(0,0,0)
pal.setColor(QtGui.QPalette.Text,textc)
textedit.setPalette(pal)
empty.setPalette(pal)
pal_grip=QtGui.QPalette()
rgb_grip = QtGui.QColor(217,207,45)
pal_grip.setColor(QtGui.QPalette.Base,rgb_grip)
textc_grip=QtGui.QColor(0,0,0)
pal.setColor(QtGui.QPalette.Text,textc_grip)
grip.setPalette(pal_grip)
resize.setPalette(pal_grip)
resize.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
empty.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
grip.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
#textedit.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) #total text widget lock
#textedit.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) #Lock?
#http://qt-project.org/doc/qt-4.8/qt.html#TextInteractionFlag-enum
#self.setWindowFlags(QtCore.Qt.FramelessWindowHint) #removes the title bar
#self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) #to make the window stay on top
class Main():
def __init__(self):
self.notes=[]
self.app = QApplication(sys.argv)
self.app.setQuitOnLastWindowClosed(False);
self.trayIcon = QSystemTrayIcon(QIcon(r"C:\Users\Thomas\Desktop\SimpleNotes.ico"), self.app)
self.menu = QMenu()
self.newWindow = self.menu.addAction("New note")
self.separator = self.menu.addSeparator()
self.hideNotes = self.menu.addAction("Hide all notes")
self.showNotes = self.menu.addAction("Show all notes")
self.separator = self.menu.addSeparator()
self.saveNotes = self.menu.addAction("Save notes")
self.loadNotes = self.menu.addAction("Load notes")
self.separator = self.menu.addSeparator()
self.showHelp = self.menu.addAction("Show help")
self.showAbout = self.menu.addAction("Show about")
self.separator = self.menu.addSeparator()
self.exitAction = self.menu.addAction("Quit notes")
self.exitAction.triggered.connect(self.close)
self.newWindow.triggered.connect(self.newNote)
self.trayIcon.setContextMenu(self.menu)
self.trayIcon.show()
self.app.exec()
def newNote(self):
print("Create new note entry has been clicked")
note=Note()
note.show()
self.notes.append(note)
print(self.notes)
def hideNotes(self):
pass
def showNotes(self):
pass
def saveNotes(self):
pass
def loadNotes(self):
pass
def showHelp(self):
pass
def showAbout(self):
pass
def close(self):
self.trayIcon.hide()
self.app.exit()
print("Exit menu entry has been clicked")
if __name__ == '__main__':
Main()
The answer from thuga was good enough, so i post it here:
textedit.setFrameShape(QtGui.QFrame.NoFrame)
and
grip.setFrameShape(QtGui.QFrame.NoFrame)
made the line disappear.
for 1. I've used:
textEdit.setFrameStyle(QtGui.QFrame.NoFrame)
ui->fr200->setFrameShape(QFrame::NoFrame);
ui->fr201->setFrameShape(QFrame::NoFrame);
ui->fr202->setFrameShape(QFrame::NoFrame);
ui->fr203->setFrameShape(QFrame::NoFrame);
ui->fr204->setFrameShape(QFrame::NoFrame);
ui->fr205->setFrameShape(QFrame::NoFrame);
ui->fr206->setFrameShape(QFrame::NoFrame);
ui->fr207->setFrameShape(QFrame::NoFrame);
ui->fr208->setFrameShape(QFrame::NoFrame);

Categories