application window title with file path for multiple main windows - python

I have an application with two main windows. I want both of then to have the standard title which contains the file name and application name. But this works strange because both files show the file name but only the second window shows the application name. The first shows just "x.py" while the second "y.py - My App". Anybody has an idea why is that and how to solve it? Is this a bug or is it expected behaviour?
from qtpy.QtWidgets import QApplication, QMainWindow
app = QApplication([])
app.setApplicationDisplayName("My App")
wnd1 = QMainWindow()
wnd2 = QMainWindow()
wnd1.setWindowFilePath("x.py") # in most cases it shows only "x.py" - this is wrong
wnd2.setWindowFilePath("y.py") # correctly shows "y.py - My App"
wnd1.show()
wnd2.show()
app.exec_()
Tested on Ubuntu 16.04., PyQt 5.8.2.
UPDATE: So I also discovered it behaves non-deterministically. Sometimes both application titles appear correctly. Sometimes only one. This seems like a bug.

As a workaround for this likely bug I am going to override the setWindowFilePath() for my main window classes. This will give me another benefit such as showing the full file path instead of just file name and also indicate that the file is unnamed if it is a new file which has not yet been saved or loaded, which is what I want anyway. It also works well with changing window-modified state. I know I am sacrificing the 100 % 'native' look but... I can live with it.
def setWindowFilePath(self, filePath):
super(MainWindow, self).setWindowFilePath(filePath)
if not filePath:
filePath = "unnamed"
self.setWindowTitle("{}[*] - {}".format(filePath, qApp.applicationDisplayName()))
Maybe somebody will find a better solution.

Related

Python3.8 with PySide2 Class to Class Usage

My GUI essentially wraps various backend PowerShell scripts that perform some automated functions. Kind of beside the point, but alright, here's where I'm stuck at.
I've got my interface designed in Qt Designer, outputted to a .ui file, converted to a .py file via PySide2-UIC, and a mainwindow class that is a subclass of the main window class I created in Qt Designer. All is well. No issues with any of that.
I'm now on to a part in my programming that I'm capturing form data from QWidgets (which is working) to a list. I've got a completely custom written class that is meant to handle taking that user input, setting other variables like filenames or paths to certain configuration files that are needed, and executing a subprocess PowerShell command with all of that information. Where I'm stuck at is trying to determine what the right place is to instantiate this custom object, inside my MainWindow class, outside my MainWindow class? But if so, where? Here's some simplified code to help explain my dilemma.
Interface Sequence
App start
MainWindow appears
User browses to form with input controls
User enters info like (IP address, username, password)
User clicks button that is connected to a method in the class
Method recurses through the child widgets on the page and captures info into a dictionary via finding qLabels and qLineEdit (buddies)
Questions:
How do I call the next method (only once even though the capturing of data is recursive)? I'm thinking about just connecting the signal to a second method that handles taking the captured data and sending/setting it into the custom class object. However, when I instantiate my custom object inside of the MainWindow class and I try to reference the object by self.customObject.sendUsesrInput(self.userInputVariable), PyCharm doesn't think self is defined inside this particular method. It doesn't properly highlight the word "self" like in the rest of the class definition, and it suggests that I need to import self.
Update
I was able to clear the errors around "import self" in PyCharm. It had something to do with improper spaces vs. tabs, even though I only ever use the tab key to do indentation. Might need to go and check my inpection settings closer. The other questions still stand though. Where is the best place to call methods on my custom class to "form a command", and "run a command", should that be executed by the mainWindow class, or should I set a flag on the customObject class that then triggers those other actions? Or more generally, should an object be in charge of executing it's own functions/methods, something tells me not usually, but I can't be sure. Also, if there are any books on the matter, I'd be happy to do my own research. I'm currently reading "Rapid GUI Programming" but not sure if this topic is covered in the later chapters just yet.
So I guess my question is, where do I handle the customObject class, in the mainWindow class, or in some other place? If so, where?
I apologize if this question is NOT clear. I promise to update as necessary to help work through this.
Here's come simplified code examples:
class customClass(object): # this is actually in a separate file but for argv sake
def __init__(self):
self.userInput = ""
self.file1 = ""
self.file2 = ""
self.otherstuff...
def setUserInput(self, uinput):
self.userInput = uinput
def dostuffwithdata(self):
# method to execute subprocess command
class MainWindow( QMainWindow ):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.customObject = customClass.customCmdClass()
self.cmddata = dict()
self.ui.toolButton.clicked.connect(self.getformdata)
def getformdata(self):
# recurses through QWidgets and grabs QLabels and QLineEdit.Text() and updates dict()
for w in widgets:
if w is qlabel:
k = w.text()
v = w.buddy().text()
self.cmddata.update({k: v})
""" all the above works fine. what doesn't work is this part"""
# at this point I want to send the collected data to the customObject for processing
def senddatatocustomObject(self):
self.customObject.setUserInput(self.cmddata) """but this says that 'self' isn't defined.
I know it has to be because of the object in an object, or something I'm doing wrong here.
**Update**: figured this out. PyCharm was freaking out about some sort of
perceived indentation error despite there not appearing to actually be one.
Was able to correct this. """
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
In an effort to close this out, I offer this answer to my previously posted question about where to put the "logic" and flow in my code.
Given that this is a graphical application without any back-end services, it makes the most sense to put most of the user-interaction logic and flow within the MainWindow object because that is essentially the control center of my program. When a user clicks or enters something, it is going to happen on the MainWindow, when a state changes, it happens (mostly) on the MainWindow or is directly tied to the MainWindow in some way. Therefore, it makes sense to include the majority of my method calls, user-input-flow logic, and other code, in the MainWindow class/object of my program.
My other classes and objects are there to capture state and to perform actions on different sets of data, but in most cases, these auxiliary classes/objects will/should be controlled by the MainWindow of my application.
This is certainly not the only way to write this application or others, but I believe this at least answers my previously posted question(s).

How to create a file dialog that returns file paths for both selected files AND directories?

I'm trying to use QFileDialog to get a list of the paths of selected folders AND directories.
I know how to do one or the other using QFileDialog.getOpenFileNames and QFileDialog.getExistingDirectory, but not both at the same time.
The C++ docs and other questions elsewhere didn't seem to help me no matter how much I googled.
I am using PyQt5 5.14.2 and Python 3.8.2 on Windows.
Edit:
I've managed to conjure up the following solution not using the native Windows dialog and it works but seems 'hacky'. Can anyone think of a better solution?
from PyQt5 import QtWidgets
main_window = QtWidgets.QApplication([])
dlg = QtWidgets.QFileDialog()
dlg.setFileMode(QtWidgets.QFileDialog.Directory)
dlg.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True)
_list = dlg.findChild(QtWidgets.QListView, 'listView')
if _list:
_list.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
tree = dlg.findChild(QtWidgets.QTreeView, 'treeView')
if tree:
tree.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
selected = None
if dlg.exec():
selected = dlg.selectedFiles()
print(selected)
There is a bug opened several years ago asking for this feature. Feel free to vote for it to get the attention of Qt developers

does not show icons

I just installed Python3 (3.5.2) and Pyqt5 (5.8.2) and I am following this tutorial to learn and make a GUI: http://zetcode.com/gui/pyqt5/firstprograms/
I'm trying to run the 2nd example but program is returning an error (which also happened on the 1st one, but since it had no image i took no notice) which is the following:
QApplication: invalid style override passed, ignoring it.
No XVisualInfo for format QSurfaceFormat(version 2.0, options QFlags<QSurfaceFormat::FormatOption>(), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SwapBehavior(SingleBuffer), swapInterval 1, profile QSurfaceFormat::OpenGLContextProfile(NoProfile))
Falling back to using screens root_visual.
What is the meaning of this? Am i missing some packages?
I installed pyqt first with this command:
sudo -H pip3 install PyQt5
but Python3 was not acknowledging its existence so i searched the apt ubuntu repos and installed with:
sudo apt install python3-PyQt5
I also tried to reference the image by full path /foo/bar/image.png and nothing
What is the problem?
EDIT #1
The code that i am using is from example 2:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This example shows an icon
in the titlebar of the window.
author: Jan Bodnar
website: zetcode.com
last edited: January 2015
"""
import sys
import os
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon
base_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(base_dir)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('image.png'))
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
After your post i reinstalled all my packages. The error is slightly different but the result is the same:
python3 example_02.py
QApplication: invalid style override passed, ignoring it.
Screencapture:
Notice that you are having no icons at all for all applications, not just for the PyQt icon example. This is because by default, certain environments turn off the icons in the titlebar. You have to enable them.
For instance in Xfce Desktop Environment, we can use the xfce4-settings-editor tool. In Settings/Settings Editor select xfwm4.
Find the show_app_icon option and check it. Change a theme back and forth to see the changes; they are not visible right away.
After this, you will see the icon in the titlebar of the PyQt5 example.
As for the warning; it is a recent thing and it has to do something
with the incopatibilities between Qt and GTK theming. I have not found
a solution to remove the warning so far.
So first off, you have no errors in your code. That's more akin to a warning but not even. What the following line is telling you
QApplication: invalid style override passed, ignoring it
is that your style option is invalid. If that were an error your script wouldn't run at all.
What I see right off the bat is this, you never supply a path to your image.
Now if the image is in the same root directory as the script then it should recognize said image without a path. But if you're attempting to do what I think you are it wouldn't work like that anyway. I think you're trying to create a launcher icon as well as a title bar icon, which typically goes hand in hand.
It appears to me that you've added it to Atom as some form of resource file. In which case most Ide's create a path for that file. Sometimes it's a path, other times a local url. QT its self does both when working with the QT creator.
I've never used Atom so I can't tell you how that works.
What I can say is this. you're using Linux which means .ico files are useless. I told you before linux doesn't handle icon files the same way windows does. This is most likely your problem.
So I sugesst you take a look at this
https://askubuntu.com/questions/476981/how-do-i-make-a-desktop-icon-to-launch-a-program
After you read that take a look at this if you have to https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Using_a_text_editor
Both of those links explain how to create a launcher icon for your program.
The following link will explain how to set the icon on the menu bar (title bar) in your program.
PyQt4 set windows taskbar icon
I hope this helps you out!
I study PyQt5 from author who give this question,also I have this problem that my icons can't show,I try some ways to catch it,that's what I do,hope it works!
First, it's important that you should use absolute path of the icons,for example:
self.setWindowIcon(QIcon("F:/Workspace/PyQT5-Study/images/web.png"))
but this not a good idea,so you can use second way like this:
from PyQt5.QtCore import QFileInfo
# ...
def initUI(self):
# ...
root = QFileInfo(__file__).absolutePath()
self.setWindowIcon(QIcon(root+'/images/web.png'))
# or
# import os
# current_dir = os.path.dirname(os.path.realpath(__file__))
# self.setWindowIcon(QIcon(os.path.join(current_dir, 'images/web.png')))
# ...
Last, if your icons also can't show, you should check this icon, if it's a legal icon.
In short, the normal images are unlimited so more, they can store many images and transform easily, but the icons have sure size,color kind,and more important,the icons have transparency, that means you can see the background, they have frame(not always straight). So you can use the web online tools to transform your image and try again,that really help me!
Also you should check the icon's source format, ensure you never change it, like .jpg to .png,and other. This will produce problem!
Wish you can solve the problem!
On windows be sure to use a real .ico file and a full path
iconpath = os.path.join(os.getcwd(),'qtlearning','assets','python.ico')
self.setWindowIcon(QIcon(iconpath))
I faced the exact same problem.
First things first. There is no setWindowIcon() method under QWidget or QMainWindow classes, in fact. you should be trying to set the QIcon at the Application level as follows.
app = QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon('home.png'))
Second, the icon thus created using this code does not reflect on the title of the window, instead it will reflect as an application icon as shown in the image below. the home.png
" icon for Application" in Ubuntu and not the " icon over the Window Title"
Finally, the path does not really matter, it can be an absolute path or a relative path, the system will consider either.
i just provided the full path of icon as simple as that

PyQt QFileDialog exec_ is slow

I'm using a custom QFileDialog because I want to select multiple directories.
But the exec_ function is very slow, and I can't figure out why. I'm using the newest version of PyQt.
Code Snippet:
from PyQt4 import QtGui, QtCore, QtNetwork, uic
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
uic.loadUi('gui.ui', self)
self.connect(self.multiPackerAddDirsBtn,
QtCore.SIGNAL('clicked()'), self.multiPackerAddDirs)
def multiPackerAddDirs(self):
dialog = QtGui.QFileDialog(self)
dialog.setFileMode(QtGui.QFileDialog.Directory)
dialog.setOption(QtGui.QFileDialog.ShowDirsOnly, True)
dialogTreeView = dialog.findChild(QtGui.QTreeView)
dialogTreeView.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection)
if dialog.exec_():
for dirname in dialog.selectedFiles():
self.multiPackerDirList.addItem(str(dirname))
print(str(dirname))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
The QFileDialog constructor creates a Qt dialog, whereas the static functions (like getSaveFileName) will create a native one (unless the DontUseNativeDialog option is set to True).
The native dialogs may be faster or slower than Qt's, depending on the platform in use.
For some plaforms, though, it appears the problem may be more acute. See this longstanding bug, which affects Windows XP and Windows 7 (amongst others) with Qt 4.7 / 4.8.
UPDATE
Just to be clear:
On Windows, the static function QFileDialog.getExistingDirectory opens the native "Browse For Folder" dialog, which only allows selecting a single directory. So Qt cannot provide a native dialog for selecting multiple directories, because Windows doesn't provide one.
The other main alternative is to use Qt's own, non-native file-dialog and monkey-patch it as suggested in this faq. However, as you've already discovered, this currently has the significant downside of being annoyingly slow due to bugs in the underlying implementation.
The only remaining alternatives are to either write your own directory-lister dialog, or try to think of another way of solving your immediate problem (i.e. without using a file-dialog).
I had very very slow performance from the default Qt file browser dialog. Listing a directory took ~5s and selecting a file took ~3s. Adding the "DontUseNativeDialog" option fixed my problem completely.
file_path = QtGui.QFileDialog.getSaveFileName( self, 'Title', path, "", "", QtGui.QFileDialog.DontUseNativeDialog )
print file_path

What are the steps to convert from using libglade to GtkBuilder? (Python)

I have a small project that uses libglade and use the following to load the xml file:
self.gladefile = "sdm.glade"
self.wTree = gtk.glade.XML(self.gladefile)
self.window = self.wTree.get_widget("MainWindow")
if (self.window):
self.window.connect("destroy", gtk.main_quit)
dic = { "on_button1_clicked" : self.button1_clicked,
"on_MainWindow_destroy" : gtk.main_quit}
self.wTree.signal_autoconnect(dic)
After converting my project in glade, what structural changes do I need to make?
I'm on Ubuntu 9.04.
You need to use gtk.Builder instead. This class can load any number of UI files, so you need to add them manually, either as files or as strings:
self.uifile = "sdm.ui"
self.wTree = gtk.Builder()
self.wTree.add_from_file(self.uifile)
Instead of get_widget, just use get_object on the builder class:
self.window = self.wTree.get_object("MainWindow")
if self.window:
self.window.connect("destroy", gtk.main_quit)
To connect the signals, just use connect_signals, which also takes a dictionary:
dic = { "on_button1_clicked" : self.button1_clicked,
"on_MainWindow_destroy" : gtk.main_quit}
self.wTree.connect_signals(dic)
It used to be the case (at least in GTK+ 2.12, not sure if it's still the same) that you could call connect_signals only once, any signals which are not connected during the first invocation will never be connected. This was different in glade, so be careful if you relied on that feature before.
Torsten's answer is correct, but a little incomplete, so in the spirit of http://xkcd.com/979/ here is the procedure I recently settled on after much trial-and-error:
Open yada.glade in Glade interface designer. Go to edit->project and change the project type to GtkBuilder and make sure it targets the latest version (2.24 as of this writing). Save the file, being sure that it saves in GtkBuilder format, and change the name from yada.glade to yada.ui
Open yada.py and change the following code:
gladefile = relativize_filename(os.path.join("glade", "yada.glade"))
self.wTree = gtk.glade.XML(gladefile, self.windowname)
to:
uifile = relativize_filename(os.path.join("glade", "yada.ui"))
self.wTree = gtk.Builder()
self.wTree.add_from_file(uifile)
Similarly change all instances of self.wTree.get_widget(...) to self.wTree.get_object(...)
Change self.wTree.signal_autoconnect(dic) to self.wTree.connect_signals(dic)
If your code depends on the name assigned the widget in the interface designer, change widget.get_name() to gtk.Buildable.get_name(widget). widget.get_name() now just returns the widget type. EDIT: You also need to change widget.set_name('my_widget') to gtk.Buildable.set_name(widget, 'my_widget').
Delete import gtk.glade
I found numerous unused signals defined in the yada.ui xml file, I had to open the xml file and manually delete them to eliminate the warnings they caused.

Categories

Resources