I have an application that consists of multiple uis(windows) and a main script, that should tie them all together into one program. Here is the video showing it: https://youtu.be/B4-PKmbyvjY.
The first problem is that when the active window changes, the application icon on the taskbar flashes. And it's very annoying. The second problem is that my computer is quite strong, and the delay between switching windows is not particularly noticeable. But when my friend started this program on his laptop, the delay between hiding one window and opening another was huge. For a whole second there was a desktop instead of the program. And so it is with every window switch.
Here's a main.py script:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, pyqtSlot
from downloadPage import DownloadPage
from mainPage import MainPage
from managerPage import ManagerPage
from registrationPage import RegistrationPage
from loginPage import LoginPage
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
def switch(_from, _to):
# Open new window on the position of the last opened window
# Otherwise it just appears in the center
lastPos = _from.pos()
modifiedPos = (lastPos.x() + _from.width() // 2,
lastPos.y() + _from.height() // 2)
newPos = (modifiedPos[0] - _to.width() // 2,
modifiedPos[1] - _from.height() // 2)
_to.move(*newPos)
_to.show()
_from.hide()
if __name__ == '__main__':
app = QApplication(sys.argv)
# Pages (uis)
registerWin = RegistrationPage()
mainWin = MainPage()
downloadWin = DownloadPage()
managerWin = ManagerPage()
loginWin = LoginPage()
mainWin.show()
# Switch windows on button clicks
loginWin.registerButton.clicked.connect(
lambda: switch(loginWin, registerWin))
registerWin.loginButton.clicked.connect(
lambda: switch(registerWin, loginWin))
mainWin.downloadButton.clicked.connect(
lambda: switch(mainWin, downloadWin))
mainWin.browseButton.clicked.connect(
lambda: switch(mainWin, managerWin))
downloadWin.returnButton.clicked.connect(
lambda: switch(downloadWin, mainWin))
# After register/login was successful
#pyqtSlot(str)
def successfulLogin(login):
mainWin.upperText.setText(
f'Welcome, {login}! What brings you here today?')
switch(registerWin, mainWin)
registerWin.successfulRegister.connect(successfulLogin)
loginWin.successfulLogin.connect(successfulLogin)
sys.exit(app.exec_())
I've heard about QTabWidget, but i'm not quite sure if it's the solution in my case. There should be buttons to switch between tabs and my program just can't allow that.
Thanks in advance for any help
Related
I am currently exploring the possibilities of displaying and working with PowerPoint presentations in a GUI using PyQt5/PyQt6 and Python. For that I found the most promising solution to be using a QAxWidget. Loading and displaying the pptx-file works fine, but unfortunately I noticed glitches when resizing the window of the GUI.
As a minimal example I used the following tutorial from the official Qt docs:
https://doc.qt.io/qtforpython/examples/example_axcontainer__axviewer.html?highlight=qaxwidget
After the QAxWidget()-initialization i just added the following line:
self.axWidget.setControl(r"C:\path\to\file\presentation.pptx")
Full code (taken from: https://doc.qt.io/qtforpython/examples/example_axcontainer__axviewer.html?highlight=qaxwidget):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
"""PySide6 Active Qt Viewer example"""
import sys
from PyQt5.QtWidgets import qApp
from PySide6.QtAxContainer import QAxSelect, QAxWidget
from PySide6.QtGui import QAction
from PySide6.QtWidgets import (QApplication, QDialog,
QMainWindow, QMessageBox, QToolBar)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
toolBar = QToolBar()
self.addToolBar(toolBar)
fileMenu = self.menuBar().addMenu("&File")
loadAction = QAction("Load...", self, shortcut="Ctrl+L", triggered=self.load)
fileMenu.addAction(loadAction)
toolBar.addAction(loadAction)
exitAction = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close)
fileMenu.addAction(exitAction)
aboutMenu = self.menuBar().addMenu("&About")
aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
aboutMenu.addAction(aboutQtAct)
self.axWidget = QAxWidget()
self.axWidget.setControl(r"C:\path\to\file\presentation.pptx")
self.setCentralWidget(self.axWidget)
def load(self):
axSelect = QAxSelect(self)
if axSelect.exec() == QDialog.Accepted:
clsid = axSelect.clsid()
if not self.axWidget.setControl(clsid):
QMessageBox.warning(self, "AxViewer", f"Unable to load {clsid}.")
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
availableGeometry = mainWin.screen().availableGeometry()
mainWin.resize(availableGeometry.width() / 3, availableGeometry.height() / 2)
mainWin.show()
sys.exit(app.exec())
When resizing the window of the GUI, there appear some glitches underneath:
An animation that shows the result while resizing:
Unfortunately I haven't found many resources I could use where a QAxWidget is used in combination with Python to figure this out myself. That's why I'm here to ask if anyone out there might have a solution for getting rid of those glitches.
I got rid of the glitches by installing an event filter to the QAxWidget using self.axWidget.installEventFilter(self).
This will call the eventFilter()-method of the QMainWindow which I set up like this: (ReportDefinitionTool is the subclass of QMainWindow here.)
def eventFilter(self, widget: QWidget, event: QEvent):
if event.type() == QEvent.Resize and widget is self.pptx_axwidget:
self.pptx_axwidget.setFixedHeight(int(self.pptx_axwidget.width() / 16 * 9))
return super(ReportDefinitionTool, self).eventFilter(widget, event)
Since the PowerPoint-presentation is displayed in a 16:9 format, this will make sure the QAxWidget does only occupy this space. The glitchy space from the initial question came from the unused space of the QAxWidget.
Here is my code:
import sys
from PyQt5.Qt import QUrl
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist
from PyQt5.QtWidgets import QApplication, QWidget
class Demo(QWidget):
def __init__(self):
super(Demo, self).__init__()
self.playlist = QMediaPlaylist(self)
self.player = QMediaPlayer(self)
self.player.setPlaylist(self.playlist)
self.playlist.addMedia(QMediaContent(
QUrl.fromLocalFile('mypath')))
self.playlist.addMedia(QMediaContent(
QUrl.fromLocalFile('mypath')))
self.playlist.setPlaybackMode(QMediaPlaylist.Loop)
self.playlist.setCurrentIndex(1)
self.player.setVolume(80)
self.player.play()
self.player.durationChanged.connect(self.print_durantion)
print(self.player.duration()) # 0
def print_durantion(self, d):
print(d) # never printed
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
I'm trying to get the duration of the file, but always get 0. It says in QtAssistant that the value may not be available when initial playback begins and tells us to use durantionChanged to receive status notifications.
But how will the durationChanged signal be emitted if duration is always 0(the value d is never printed)?
Is there any way to get duration when a media file is played? Any helps would be appreciated. (I tested the above code on MacOS)
You are attaching the durationChanged signal after you select the media. This signal is triggered only once as the media is selected and not after.
If you place it before setCurrentIndex it should work as expected, but you may need to attaching it before linking the playlist.
Here is a working mediaplayer demo application in PyQt5 to confirm the signal works for your current PyQt5 install.
I've been trying to do animation with VTK, so I've been using TimerEvent. When I tried to move over to the Qt binding, it broke. The problem is that as soon as I interact with the view (say scrolling to zoom, or clicking to rotate) the timer stops. Here's a simple minimal example:
import vtk
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5 import Qt
message = "tick"
def onTimerEvent(object, event):
global message
print(message)
if message == "tick":
message = "tock"
else:
message = "tick"
app = Qt.QApplication([])
mainWindow = Qt.QMainWindow()
renderer = vtk.vtkRenderer()
vtkWidget = QVTKRenderWindowInteractor(mainWindow)
vtkWidget.GetRenderWindow().AddRenderer(renderer)
mainWindow.setCentralWidget(vtkWidget)
vtkWidget.GetRenderWindow().GetInteractor().Initialize()
timerId = vtkWidget.CreateRepeatingTimer(100)
vtkWidget.AddObserver("TimerEvent", onTimerEvent)
mainWindow.show()
app.exec_()
This script should display the words "tick" and "tock" over and over again, but stop as soon as you click inside the window.
One odd behavior is that pressing "T" to switch to trackball interaction style seems to have some effect. If I press T and then click inside the window, the timer only stops running while I'm clicking: when I let go it starts up again. If I then press J to go back to "joystick mode", the problem returns: clicking stops the timer forever.
Python 3.6, VTK 8, Qt 5.
The problem is reproducible in Linux 16.04, VTK8.1.1 and Qt5.5.1.
As you are using Qt, a workaround for your problem is to use QTimer(). It is a solution if you want to work with a timing.
This is your minimal example changing the TimerEvent for QTimer():
import vtk
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5 import Qt
from PyQt5.QtCore import QTimer
message = "tick"
def onTimerEvent():
global message
print(message)
if message == "tick":
message = "tock"
else:
message = "tick"
app = Qt.QApplication([])
mainWindow = Qt.QMainWindow()
renderer = vtk.vtkRenderer()
vtkWidget = QVTKRenderWindowInteractor(mainWindow)
vtkWidget.GetRenderWindow().AddRenderer(renderer)
mainWindow.setCentralWidget(vtkWidget)
vtkWidget.GetRenderWindow().GetInteractor().Initialize()
#timerId = vtkWidget.CreateRepeatingTimer(100)
#vtkWidget.AddObserver("TimerEvent", onTimerEvent)
timer = QTimer()
timer.timeout.connect(onTimerEvent)
timer.start(100)
mainWindow.show()
app.exec_()
I just started dwelling into the realm of Qt (coming from PyGTK) and I'm using PySide. So I found this great example on another answer here on stack exchange.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
app = QApplication(sys.argv)
web = QWebView()
web.settings().setAttribute(
QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
# or globally:
# QWebSettings.globalSettings().setAttribute(
# QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
web.load(QUrl("http://www.google.com"))
web.show()
inspect = QWebInspector()
inspect.setPage(web.page())
inspect.show()
sys.exit(app.exec_())
My question is as follows, how do I make the inspector show up in the same window instead of a new one? I understand I need to add the QWebInspector to another widget inside the main window (a vbox for example), what I want to know is how to connect that event to the signal the context menu "Inspect" triggers. In PyGTK I would need to use .connect() but I can't find the right SIGNAL for this specific action.
Thanks for your time guys/gals
It shouldn't be necessary to do anything special for the context menu to work. Just add an inspector widget to your layout, and hide() it to start with. The default context menu action can then show() the inspector as needed.
A slightly trickier issue is how to hide the inspector again once it's shown, as there doesn't seem to be a corresponding context menu item for that.
The demo script below simply creates a keyboard shortcut to hide/show the inspector:
from PySide import QtGui, QtCore, QtWebKit
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = QtWebKit.QWebView(self)
self.view.settings().setAttribute(
QtWebKit.QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
self.inspector = QtWebKit.QWebInspector(self)
self.inspector.setPage(self.view.page())
self.inspector.hide()
self.splitter = QtGui.QSplitter(self)
self.splitter.addWidget(self.view)
self.splitter.addWidget(self.inspector)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.splitter)
QtGui.QShortcut(QtGui.QKeySequence('F7'), self,
self.handleShowInspector)
def handleShowInspector(self):
self.inspector.setShown(self.inspector.isHidden())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.view.load(QtCore.QUrl('http://www.google.com'))
window.show()
sys.exit(app.exec_())
I would like to ask, as I write in the title, how to open a new window and close (completely) the current window?
There are two .py files. They are independent from each other, that means, each of them can be run on its own.
In MyApp.py there is a button. If the button is clicked, I want to close the current window/file and open the new window/file.
In my code below I can open the new window/file, but the old window is not closed. And if I close the second window, the old window will be closed as well. I have tried some methods with my knowledge/search, but none of them worked fine.
Here is the code for MyApp.py
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import NewApp
# NewApp.py in the same folder
# in NewApp.py there is a class NewApp
qtCreatorFile_MyApp = "F:\\MyApp.ui"
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
uic.loadUi(qtCreatorFile_MyApp, self)
self.show()
self.btn_inMyApp.clicked.connect(self.closeMyApp_OpenNewApp)
def closeMyApp_OpenNewApp(self):
self.Open = NewApp.NewApp()
self.Open.show()
# My Problem: How to close the MyApp completely and open NewApp?
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MyApp()
sys.exit(app.exec_())
NewApp.py has similar code structure. For the purpose of integrity of this quesiton, I paste the code as follows.
qtCreatorFile_NewApp = "F:\\NewApp.ui"
class NewApp(QWidget):
def __init__(self):
super(NewApp, self).__init__()
uic.loadUi(qtCreatorFile_NewApp, self)
self.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = NewApp()
sys.exit(app.exec_())
Thank you for the help!