Why doesn't menu get added? - qt

I'm clearly missing something here; why doesn't the File menu get added in this little example app?
import sys
from PySide.QtGui import *
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setWindowTitle('Test')
layout = QHBoxLayout()
self.widget = QWidget()
self.widget.setLayout(layout)
self.setCentralWidget(self.widget)
self.exitAction = QAction('Exit', self, shortcut=QKeySequence.Quit, triggered=self.close)
self.fileMenu = self.menuBar().addMenu('File')
self.fileMenu.addAction(self.exitAction)
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
EDIT:
Ok, it looks like this is actually a unicode issue.
Here's another example app:
from __future__ import unicode_literals, print_function, division
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.dummyAction = QAction(self.tr('dummy'), self, triggered=self.dummy)
self.gameMenu = self.menuBar().addMenu(self.tr('ddddummy'))
print (self.tr('dummy'))
self.gameMenu.addAction(self.dummyAction)
layout = QHBoxLayout()
self.widget = QWidget()
self.widget.setLayout(layout)
self.setCentralWidget(self.widget)
def dummy(self):
pass
locale = QLocale.system().name()
qtTranslator = QTranslator()
app = QApplication(sys.argv)
if qtTranslator.load('qt_' + locale, ':/'):
app.installTranslator(qtTranslator)
w = Window()
w.show()
sys.exit(app.exec_())
This app doesn't have 'File' or 'Quit' or 'Exit' -- but it works if I comment out the from __future__ line, or surround the quoted strings like self.tr(str('foo')) instead of self.tr('foo')
EDIT 2:
from __future__ import unicode_literals
import sys
from PySide.QtGui import *
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
print self.tr('foo')
app = QApplication(sys.argv)
Window().show()
sys.exit(app.exec_())
This should print 'foo', but prints nothing.

At first glance, your code seems perfectly normal, and it does function just as expected on windows or linux. The issue here is that on OSX, the operating system enforces a standard interface on the menu. Whereas on other operating systems the menu is nested right under your app, and your app owns it...on OSX, the operating system owns it. Hence it is shown in the global menu area.
That being said, OSX is filtering out some reserved keywords like "Quit" or "Exit". The reason for this is because the quit functionality is a standard that is automatically placed in your Application menu. When you run it as a basic python script, the menu will be called "Python". But if you bundle it into an app, it will be named accordingly for your bundled app.
This link here, while not an exact explanation, does mention the differences for a menu on OSX.
For a quick example of fixing your menu, see what happens when you do:
self.exitAction = QAction('Kwit', self)
OSX will not filter out that one. But I suppose its better to follow the native standards which make all app experiences the same on the platform. You would definitely include the "Quit" menu action as you have it now, so that your app will be cross-platform if run on linux or windows and just expect that OSX will relocate it for you.

I've come across this thread because I'm struggling with a similar issue. Here's what I've found...
About your EDIT 2:
Your code will correctly print 'foo' if you substitute the line
Window().show()
for the lines
w = Window()
w.show()
as you had in your original code. Apparently the return type on the constructor causes chaining to be an issue in python?
I was able to reproduce your EDIT 1 by commenting out the from __future__ line. Otherwise, the code below works as expected in OS X (Mountain Lion 10.8.3 with brewed python). Specifically, the following code puts the "About" action under the "Python" application menu that OS X creates, and also creates a "Help" menu containing the "Website" action.
import sys
from PySide.QtGui import QApplication,QMainWindow, QWidget, QAction
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.create_menus()
self.create_main_frame()
def create_menus(self):
self.aboutAction = QAction('About', self, triggered=self.on_about)
self.websiteAction = QAction('Website', self, triggered=self.on_website)
self.help_menu = self.menuBar().addMenu('Help')
self.help_menu.addAction(self.aboutAction)
self.help_menu.addAction(self.websiteAction)
def create_main_frame(self):
self.mainWidget = QWidget()
self.setCentralWidget(self.mainWidget)
def on_website(self):
pass
def on_about(self):
pass
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I should draw attention to an important point that this thread helped me discover. I've seen a lot of advice for OS X that indicates you should create menu_bar = QMenuBar() independently of QMainWindow and then bind with self.setMenuBar(menu_bar) where self represents QMainWindow. This, in fact, didn't work for me. Instead, what did work, is grabbing the menu bar reference directly from the QMainWindow class itself. For example, and like above, when adding a menu, use self.help_menu = self.menuBar().addMenu('Help') as above.

Related

PyCharm loadUi findChild - unresolved attribute reference [duplicate]

Let's say I have a ui file created in Qt Designer that I want to load dynamically to then manipulate the widgets, such as:
example.py:
from PyQt5 import QtWidgets, uic
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
uic.loadUi('example.ui', self)
# No code completion here for self.myPushButton:
self.myPushButton.clicked.connect(self.handleButtonClick)
self.show()
Is there a standard / convenient way of enabling code completion for the widgets loaded this way in PyCharm (2017.1.4)?
At the moment I am using this (written in the constructor after the ui file is loaded):
self.myPushButton = self.myPushButton # type: QtWidgets.QPushButton
# Code completion for myPushButton works at this point
I also thought of this, but it does not seem to do the trick:
assert isinstance(self.myPushButton, QtWidgets.QPushButton)
# PyCharm does not even recognise myPushButton as an attribute of self at this point
Finally, I also thought of using python stubs, such as:
example.pyi:
class MyWidget(QtWidgets.QWidget):
def __init__(self):
self.myPushButton: QtWidgets.QPushButton = ...
However, myPushButton is properly recognised in code outside example.py but not in code inside example.py itself, which is kind of the opposite of what I wanted.
I am also considering taking my first approach but with all those lines put in a private method that will never get called, such as:
example.py:
from PyQt5 import QtWidgets, uic
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
uic.loadUi('example.ui', self)
# Code completion now works here for self.myPushButton:
self.myPushButton.clicked.connect(self.handleButtonClick)
self.show()
def __my_private_method_never_called():
self.myPushButton = self.myPushButton # type: QtWidgets.QPushButton
# Or even this (it should have the same effect if this
# function is never called, plus it is less verbose):
self.myPushButton = QtWidgets.QPushButton()
# If I want to make sure that this is never called
# could raise an error at some point:
raise YouShouldNotHaveCalledThisError()
This seems to work fine, and it also allows me to group all my type hinting code together, isolated from the rest. I could even make some script to write all those lines for me by parsing the ui files. I am just wondering if people reading my code would find this approach very unorthodox, even if I comment clearly why am I writing a technically useless private function.
If anybody is interested, I made the script I mentioned to parse the .ui files and generate stub code ready to be copied to my class:
ui_stub_generator.py:
from __future__ import print_function
import os
import sys
import xml.etree.ElementTree
def generate_stubs(file):
root = xml.etree.ElementTree.parse(file).getroot()
print('Stub for file: ' + os.path.basename(file))
print()
print(' def __stubs(self):')
print(' """ This just enables code completion. It should never be called """')
for widget in root.findall('.//widget'):
name = widget.get('name')
if len(name) > 3 and name[:2] == 'ui' and name[2].isupper():
cls = widget.get('class')
print(' self.{} = QtWidgets.{}()'.format(
name, cls
))
print(' raise AssertionError("This should never be called")')
print()
def main():
for file in sys.argv[1:]:
generate_stubs(file)
if __name__ == '__main__':
main()
This only parses widgets whose names start with 'ui' followed by an uppercase letter, such as 'uiMyWidget', which is the naming convention that I typically follow in the Qt Designer. By doing this, the widgets with names automatically generated by the Qt Designer are ignored (if I cared about these, I would have given them a proper name). It should be straightforward to update this for any other naming conventions, or other type of objects, such as actions.
For convenience, I have set this up as an external tool in PyCharm as well; see screenshot here (change the paths as appropriate). That way, I only have to right-click my ui file in the project window, then External Tools -> Stub Generator for Qt UI Files, and I get the following output in the Run window ready to be copied:
C:\ProgramData\Anaconda3\python.exe D:\MyProject\bin\ui_stub_generator.py D:\MyProject\my_ui_file.ui
Stub for file: my_ui_file.ui
def __stubs(self):
""" This just enables code completion. It should never be called """
self.uiNameLabel = QtWidgets.QLabel()
self.uiOpenButton = QtWidgets.QPushButton()
self.uiSplitter = QtWidgets.QSplitter()
self.uiMyCombo = QtWidgets.QComboBox()
self.uiDeleteButton = QtWidgets.QPushButton()
raise AssertionError("This should never be called")
Process finished with exit code 0

Why QDialog not shows any widgets until job completed? [duplicate]

This question already has answers here:
Equivalent to time.sleep for a PyQt application
(5 answers)
Closed 2 years ago.
im new to pyqt5,i tried to open dialog and push some text into that dialog
my dialog contain one plaintext ,progressbar and pushbutton
when i run the code its popup the dialog but not shown any thing ,after code execution completes its showing all the widgets and with text
but i need to open the dialog and i want update progress bar
My code
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (QDialog,QPlainTextEdit,QScrollArea,QProgressBar,QPushButton)
import sys
import time
class PrograssDialog():
def ShowDialog(self,Dialogs):
try:
self.Pd=Dialogs
self.Pd.setWindowTitle("Script Excution... ")
self.Pd.resize(500,500)
self.ScrArea=QScrollArea(self.Pd)
self.ScrArea.move(0,0)
self.ScrArea.resize(500,300)
self.TextArea=QPlainTextEdit(self.Pd)
self.TextArea.move(0,0)
self.TextArea.resize(500,300)
self.TextArea.insertPlainText(str("Start : %s" % time.ctime())+"\n")
self.Prograssbar=QProgressBar(self.Pd)
self.Prograssbar.setGeometry(QtCore.QRect(0, 350, 450, 23))
self.Prograssbar.setMaximum(100)
self.Cancelbutton=QPushButton("Cancel",self.Pd)
self.Cancelbutton.setGeometry(QtCore.QRect(360, 400, 93, 28))
self.Cancelbutton.clicked.connect(self.StopExcution)
self.Pd.show()
except Exception as msg:
import sys
tb = sys.exc_info()[2]
print("Error_analysis " + str(msg)+ str(tb.tb_lineno))
def AddMessage(self,Message):
self.TextArea.insertPlainText(str(Message)+"\n")
# print("message added")
def SetPercentage(self,Number):
self.Prograssbar.setValue(Number)
# print("percent added")
def StopExcution(self):
sys.exit()
app = QApplication(sys.argv)
ui=PrograssDialog()
ui.ShowDialog(QDialog())
for i in range(100):
ui.AddMessage("Hello")
ui.SetPercentage(i)
time.sleep(0.5)
sys.exit(app.exec_())
There are various problems with your code, I'll try to address all of them.
The main reason for the issue you are facing is that no blocking functions (like time.sleep) should happen in the main Qt thread (which is the thread that shows the GUI elements and allow interactions with them); blocking functions prevent the UI to correctly draw and refresh its contents, if you want to do an operation at specific intervals, you have to use a QTimer;
You should not use a basic python object subclass for this kind of situations, especially since you're only using just one dialog; you should subclass from QDialog instead and implement
To "exit" your program you should not use sys.exit (you are already using it), but use QApplication.quit() instead; also, since you already imported sys at the beginning, there's no need to import it again in the exception;
Function and variable names should not be capitalized; while you can use any casing style you want for your own code, it's common (and highly suggested) practice to always use lowercase initials, and it's also a convention you should stick to when sharing code with others, especially on Q&A sites like StackOverflow; read more on the official Style Guide for Python Code;
Always avoid fixed geometries for children widgets: what others see on their computers will probably be very different from what you see on yours, and you might end up with an unusable interface; use layout managers instead, so that the widgets can resize themselves if required;
You added a scroll area but you never use it; since you're using the same geometry for the text area I believe that you thought you were using for that, but there's no need as the text area already is a scroll area;
Here is how the code could look like in order to achieve what you want:
import time
from PyQt5 import QtCore, QtWidgets
class ProgressDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
layout = QtWidgets.QVBoxLayout(self)
self.textArea = QtWidgets.QPlainTextEdit()
layout.addWidget(self.textArea)
self.textArea.insertPlainText(str("Start : %s" % time.ctime())+"\n")
self.textArea.setReadOnly(True)
self.progressBar = QtWidgets.QProgressBar()
layout.addWidget(self.progressBar)
self.cancelButton = QtWidgets.QPushButton('Cancel')
layout.addWidget(self.cancelButton)
self.cancelButton.clicked.connect(QtWidgets.QApplication.quit)
self.countTimer = QtCore.QTimer()
self.countTimer.timeout.connect(self.timeout)
def startCounter(self, maximum, sleepSeconds):
self.progressBar.reset()
self.progressBar.setMaximum(maximum)
# QTimer interval is in milliseconds
self.countTimer.setInterval(sleepSeconds * 1000)
self.countTimer.start()
def timeout(self):
if self.progressBar.value() == self.progressBar.maximum():
self.countTimer.stop()
return
self.setPercentage(self.progressBar.value() + 1)
self.addMessage('Hello')
def setPercentage(self, value):
self.progressBar.setValue(value)
def addMessage(self, message):
self.textArea.insertPlainText(str(message) + '\n')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
dialog = ProgressDialog()
dialog.show()
dialog.startCounter(100, .5)
sys.exit(app.exec_())

Disable keyboard input for QFontComboBox

Is it possible to disable keyboard input for a QFontComboBox? The following code:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class FontComboBox(QFontComboBox):
def __init__(self, parent=None):
super(FontComboBox, self).__init__(parent)
if __name__ == '__main__':
app = QApplication(sys.argv)
fonts = FontComboBox()
fonts.show()
sys.exit(app.exec_())
gives me (Mac OS X 10.8, PyQt4.8) a combobox which is editable and lets the user input basically anything. QtCreator, on the other hand, has this nice solution:
which looks more like an ordinary QComboBox. I've searched far and wide but can't seem to find settings for disabling keyboard input/changing the look of QFontComboBox. Any ideas?
A QFontComboBox is a QComboBox (i.e. a subclass of it), so all you need to do is:
fonts.setEditable(False)

Qt - Can not show a Qt.Tool window after parent is shown?

I basically have a QMainWindow, and a Dialog window whose constructor is shown below;
class VisualTool(QtGui.QDialog):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.WidgetBoard = Ui_Aesthetics_Tool()
self.WidgetBoard.setupUi(self)
self.setWindowFlags(QtCore.Qt.Tool) # <-
As you can see, I would like to treat the Dialog as a Tool window (it's exactly the type of window I need). The tool window should be shown after a button click on the QMainWindow, and for interaction with QMainWindow to continue.
Before my QMainWindow is shown, calling .show() on my tool window spawns it correctly.
However, if I attempt to show my Tool window AFTER showing QMainWindow (such as after a button click), calling .show() and .exec() have no effect whatsoever.
(There's not even any flicker of a window. There's no spawn whatsoever!)
Once the window is shown, I can not change the Window Flags. It has no effect.
How can I get this Tool window to show?!
Thanks!
PyQt4
python 2.7.2
windows 7
I'm not sure I understand what your issue really is. This test code snippet seems to function fine, though I have no idea what your missing Ui_Aesthetics_Tool() code does to modify the tool window:
#!/usr/bin/env python
import sys
from PyQt4 import QtCore, QtGui
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.resize(640,480)
self.button = QtGui.QPushButton("Click me")
self.setCentralWidget(self.button)
self.button.clicked.connect(self.showTool)
def showTool(self):
tool = VisualTool(self)
tool.show()
class VisualTool(QtGui.QDialog):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
# self.WidgetBoard = Ui_Aesthetics_Tool()
# self.WidgetBoard.setupUi(self)
self.setWindowFlags(QtCore.Qt.Tool)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Main()
myapp.show()
sys.exit(app.exec_())
I am able to launch multiple tool windows.
And as for using exec_(), that is a modal blocking call and probably not what you want.

Single key shortcut in PyQt on a Mac?

I can't get single key shortcuts to work properly in PyQt on Mac. For example, if I change the QKeySequence below to Qt.Key_Tab it works fine, but Qt.Key_Control doesn't work. Am I doing something wrong?
I've tried Qt.CTRL and "Ctrl" too, but they don't work either.
shortcut = QShortcut(QKeySequence(Qt.Key_Control), self, self.show_dialog)
shortcut.setContext(Qt.ApplicationShortcut)
I'm unable to test this myself, but the modifiers map to different keys on a Mac: see the note at the end of the details section for QKeySequence.
It looks like you need to use Qt.Key_Meta.
Use this to see exactly which keys are being pressed:
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(200, 200)
def keyPressEvent(self, event):
print 'key: %s -' % hex(event.key()),
print 'modifiers:', hex(int(event.modifiers()))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
EDIT
NB: Qt does not allow modifier keys (in any combination) to be set as shortcuts.

Resources