I'm currently trying to implement some kind of file browser / "explorer" into a programme... I'm using Python and PySide in connection with the Qt-window-toolkit. More or less this youtube-video shows the behaviour I want to have at the end. However, this tutorial used C++ as programming language and I haven't been able yet to reason the right python code from the C++ example.
Basically, my problem is to get the right column (file view) showing the content of the folder clicked in the left column (tree-style folder view).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PySide import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(600, 600)
self.fileBrowserWidget = QtGui.QWidget(self)
self.setCentralWidget(self.fileBrowserWidget)
self.dirmodel = QtGui.QFileSystemModel()
# Don't show files, just folders
self.dirmodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.AllDirs)
self.folder_view = QtGui.QTreeView(parent=self);
self.folder_view.setModel(self.dirmodel)
self.folder_view.clicked[QtCore.QModelIndex].connect(self.clicked)
# Don't show columns for size, file type, and last modified
self.folder_view.setHeaderHidden(True)
self.folder_view.hideColumn(1)
self.folder_view.hideColumn(2)
self.folder_view.hideColumn(3)
self.selectionModel = self.folder_view.selectionModel()
self.filemodel = QtGui.QFileSystemModel()
# Don't show folders, just files
self.filemodel.setFilter(QtCore.QDir.NoDotAndDotDot | QtCore.QDir.Files)
self.file_view = QtGui.QListView(parent=self);
self.file_view.setModel(self.filemodel)
splitter_filebrowser = QtGui.QSplitter()
splitter_filebrowser.addWidget(self.folder_view)
splitter_filebrowser.addWidget(self.file_view)
splitter_filebrowser.setStretchFactor(0,2)
splitter_filebrowser.setStretchFactor(1,4)
hbox = QtGui.QHBoxLayout(self.fileBrowserWidget)
hbox.addWidget(splitter_filebrowser)
def set_path(self):
self.dirmodel.setRootPath("")
def clicked(self, index):
# get selected path of folder_view
index = self.selectionModel.currentIndex()
dir_path = self.dirmodel.filePath(index)
###############################################
# Here's my problem: How do I set the dir_path
# for the file_view widget / the filemodel?
###############################################
self.filemodel.setRootPath(dir_path)
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
main.set_path()
sys.exit(app.exec_())
As you can see in my code, I've already tried to use the setRootPath-function... however, that doesn't seem to be the correct one. So I wonder, what I've got to do for getting this to work?
You need to set the root index to the appropriate one in the file model. You can do this by adding the following line to the end of the clicked() function:
self.file_view.setRootIndex(self.filemodel.index(dir_path))
I was able to figure it out from my experience using Qt in C++. The documentation for Qt in C++ is really quite good if you can figure out how it translates to Python. I was able to figure this out by looking at the QFileSystemModel documentation.
You need to set the root index of the files list view:
def clicked(self, index):
# the signal passes the index of the clicked item
dir_path = self.filemodel.filePath(index)
root_index = self.filemodel.setRootPath(dir_path)
self.file_view.setRootIndex(root_index)
Related
Im trying to make a small app that would help me rename pictures. Since i want to manually order them, i had the idea to simply show a window with thumbnails inside in a grid ( or small scale images, doesnt matter ), and then drag and drop reorder them as i see fit. Afterwards its just click a button and they get properly named acording to the order.
Is there any container or something that would allow its inside widgets to be moved around like that while also properly displaying the inside widgets?
The ways im thinking of currently since i cant find anything else, is to make the whole background a canvas, move x/y on drag/drop of the pictures and then calculate where im dropping it off and manually reorder the whole canvas again and keep redrawing.
Im open to different python solution if anyone has them, but after checking wxwidgets and tkinter, i havent found anything that would be a solution to this without a lot of manual code.
After ekhumoro hint, i was able to solve it.
Heres a sample code that reads the current folder of its files, shows them as "thumbnails", and allows reordering.
#!/usr/bin/python
import sys, os
from PyQt5.QtWidgets import (QListWidget, QWidget, QMessageBox,
QApplication, QVBoxLayout,QAbstractItemView,QListWidgetItem )
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtWidgets import QListView
class Example(QWidget):
def __init__(self):
super().__init__()
self.icon_size = 200
self.initUI()
def loadImageItem(self, fajl,folder=None):
icon = QIcon()
item = QListWidgetItem()
if folder is not None:
pot = os.path.join(folder,fajl)
else:
pot = fajl
icon.addFile(pot,size=QSize(self.icon_size,self.icon_size))
item.setIcon(icon)
item.setTextAlignment(Qt.AlignBottom)
return item
def initUI(self):
vbox = QVBoxLayout(self)
listWidget = QListWidget()
#make it icons
listWidget.setDragDropMode(QAbstractItemView.InternalMove)
listWidget.setFlow(QListView.LeftToRight)
listWidget.setWrapping(True)
listWidget.setResizeMode(QListView.Adjust)
listWidget.setMovement(QListView.Snap)
listWidget.setIconSize(QSize(200,200))
folder = os.getcwd()
#folder = "/mnt/Data/pictures/2022-10-30 Sveta Katarina/izbor/1"
files = os.listdir(folder)
files = [f for f in files if os.path.isfile(os.path.join(folder,f))]
for foo in files:
listWidget.addItem(self.loadImageItem(foo,folder=folder))
vbox.addWidget(listWidget)
self.setLayout(vbox)
self.setGeometry(10, 10, 1260, 820)
self.setWindowTitle('Image renamer')
self.show()
def main():
App = QApplication(sys.argv)
ex = Example()
sys.exit(App.exec())
if __name__ == '__main__':
main()
I am having trouble figuring out how to use Qt to create translation files for a python apllication.
I'm using python 2.7, Qt version 5.9.1 and PyQt4 4.12.1 to create my GUI on OSX 10.11.6.
For now I just wanted to translate a few words on my code.
For what I understand, I have to use QtLinguist to open a .ts file, translate the words and create a .qm file, which will then be used by python.
From Qt Linguist page I get that I need to use a .pro project file, that will be read by pylupdate4, etc...
Now, I do I create a .pro file?
I tried running:
$ qmake -project myfile.py
$ pylupdate4 myfile.pro -ts file.ts
but the resulting .pro file can't be read by pylupdate4 (XML error: Parse error at line 1, column 1 [...])
From this Tutorial, I tried:
$ pylupdate4 myfile.py -ts file.ts
Which creates an empty .ts file, that Qt Linguist can't open.
Can someone give my any tip on what might be wrong, the 15 tabs I have open in my browser are not helping.
Here's my python code if you need it:
import sys
import os.path as osp
import os
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow,self).__init__()
# Set MainWindow geometry, use settings of last session. If it's first session,
# use defaulted settings
self.settings = QtCore.QSettings('Paul',QtCore.QSettings.NativeFormat)
self.resize(self.settings.value("size", QtCore.QSize(500, 300)).toSize())
self.move(self.settings.value("pos", QtCore.QPoint(5, 5)).toPoint());
self.initUI()
def closeEvent(self, e):
#Save MainWindow geometry session when closing the window
self.settings.setValue("size",self.size())
self.settings.setValue("pos",self.pos())
e.accept()
def initUI(self):
self.hbox = QtGui.QVBoxLayout(self) # Create Vertival box layout to put the buttons
self.myButtons = QtGui.QPushButton('button',self) #create push button
self.myButtons.setStyleSheet("""QPushButton { background-color: red; font:bold 20px}""")
self.myButtons.setToolTip('Push this button')
self.myButtons.setText(self.tr(QtCore.QString('yes')))
comboBox=QtGui.QComboBox(self) #create drop down menu
comboBox.addItem('Portugues')
comboBox.addItem('English')
self.hbox.addWidget(comboBox,1,QtCore.Qt.AlignRight) #add drop down menu to box layout
self.hbox.addStretch(3) # set separation between buttons
self.myButtons.clicked.connect(self.buttonClicked) # what should the button do
self.hbox.addWidget(self.myButtons,1,QtCore.Qt.AlignRight) #add button to box layout
self.setWindowTitle('Test2')
self.show()
def buttonClicked(self):
msbox= QtGui.QMessageBox()
choice=msbox.warning(self,'ok',"This button doesn't do anything!!!")
if choice == QtGui.QMessageBox.No:
print('nanan')
else:
print('Bye')
self.settings.setValue("size",self.size());
self.settings.setValue("pos",self.pos());
sys.exit()
def main():
app = QtGui.QApplication(sys.argv)
translator = QtCore.QTranslator()
translator.load("~/basefiles/translations/qt_pt.qm")
app.installTranslator(translator)
ex = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
When you use self.tr you must pass the string, not the QString variable, in your case it changes:
self.myButtons.setText(self.tr(QtCore.QString('yes')))
to
self.myButtons.setText(self.tr("yes"))
And run everything again.
In reference to a previous question, I need some help with keeping references in my application.
First a snippet from my code.
from PyQt4 import QtGui
import os, os.path
import sys
class mainWindowHandler():
equationEditor = []
_listview = None
_window = None
def __init__(self):
return
def showAddEquation(self):
"""Creates a new instance of the dynamic editor for adding an equation"""
#create a horizontal split layout
window = QtGui.QWidget()
layout = QtGui.QHBoxLayout()
current = len(self.equationEditor) - 1
de = QtGui.QPushButton(str(current))
self.equationEditor.append(de)
de.clicked.connect(self.clicked)
#fill list view with items from equation collection
listview = QtGui.QListWidget()
for equation in self.equationEditor:
item = QtGui.QListWidgetItem()
item.setText(equation.text())
listview.addItem(item)
layout.addWidget(listview)
layout.addWidget(de)
window.setWindowTitle("Equation {}".format(str(current))
window.setLayout(layout)
self._window = window
self._listview = listview
window.show()
return window
def clicked(self):
"""Method for handling the button events in the solver settings\n
signal = the button hit\n"""
return self.showAddEquation()
if __name__ == "__main__":
path = os.path.dirname(os.path.abspath(__file__))
app = QtGui.QApplication(sys.argv)
ewh = mainWindowHandler()
window = ewh.showAddEquation()
sys.exit(app.exec_())
The application will (later) create a window that allows the manipulation of certain settings - in my code example represented by the QPushButton. These settings are later written to a txt-file, but until then I save them in form of there widget. I simply add the widget to a collection and recall them from there. That works well on the Python-level.
Now, I have a button that creates a new instance of the window from inside of the window itself. That works too. But only until the third instance. At that point I loose the reference to my QPushButton on the Qt-level. I get the
wrapped C/C++ object of type `QPushButton` has been deleted
error when trying to retrieve the buttons from my collection (equationEditor). In Python they are still there, but obviously the corresponding Qt-objects where destroyed because I somewhere mishandled the references.
Can someone point out a better solution or how I can keep the references?
Thanks...
Edit:
As there seem to be some confusions I will try to explain the functionality a bit more in detail.
The program starts and creates a window "Equation 1" with a QListViewand a QPushButton "1". In the list view all available QPushButton are listed (at start only 1 item). In my actual program the QPushButton is QWidget with some text fields and the QPushButton.
If the user clicks "1" then the button "1" should disappear and a new instance of QPushButton named "2" should appear at the position of "1". Additionally, the listview should now hold two items "1" and "2" and the window should have the title "Equation 2". If it is a new window or the same as before with new contents is not relevant. Both variants would be okay. The former is the way it is implemented at the moment. Visible should by only one window at a time.
All instances of QPushButton should by collected in a small list (called equationEditor) to keep them in the memory. In my actual program this is used for saving all changes made in the widgets without writing the changes to a temp file.
Later, if the user selects item "1" in the QListView then the current visible QPushButton should be replaced by the QPushButton "1" (from the collection equationEditor) or if he selects the second item the QPushButton "2" should be shown.
Why?
The widget that will be used later contains a lot of editable data. As the user could edit that at any time it is easier to keep the widgets in memory without showing them instead of repopulating all the data again. As soon as the user selects one in the QListView the corresponding widget should be shown in the window so that he can edited the data in the widget again.
It's quite hard to understand what exactly you're trying to do. Looking at your code I wonder why it is even working twice before failing.
Btw. I just saw, that there is a quite accurate description of why it's failing in the previous post, given by Schollii
Anyways, I think you should make a new class for the equation window. The main class can then keep track of all opened windows in the equationEditor list. It can also add the values of the other opened windows to a new one, once created.
Here is how it would look like
from PyQt4 import QtGui
import os, os.path
import sys
class ShowAddEquation(QtGui.QWidget):
"""Creates a new instance of the dynamic editor for adding an equation"""
def __init__(self,parent=None):
super(ShowAddEquation, self).__init__(parent=parent)
#create a horizontal split layout
layout = QtGui.QHBoxLayout()
self.current = 0
self.de = QtGui.QPushButton(str(self.current))
self.listview = QtGui.QListWidget()
layout.addWidget(self.listview)
layout.addWidget(self.de)
self.setWindowTitle("Equation Editor")
self.setLayout(layout)
self.show()
def setCurrent(self, current):
self.current=current
self.de.setText(str(self.current))
class mainWindowHandler():
equationEditor = []
def __init__(self):
return
def clicked(self):
se = ShowAddEquation()
self.equationEditor.append(se)
se.de.clicked.connect(self.clicked)
current = len(self.equationEditor) - 1
se.setCurrent(current)
for equation in self.equationEditor:
item = QtGui.QListWidgetItem()
item.setText(str(equation.current))
se.listview.addItem(item)
if __name__ == "__main__":
path = os.path.dirname(os.path.abspath(__file__))
app = QtGui.QApplication(sys.argv)
ewh = mainWindowHandler()
ewh.clicked()
sys.exit(app.exec_())
So, after understanding the approach given in the first answer I have solved my problem. Here is the working code
# -*- coding: utf-8 -*-
"""
Created on Sat Sep 3 14:31:15 2016
"""
from PyQt4 import QtGui
from PyQt4 import QtCore
import os, os.path
import sys
class mainWindowHandler():
equationEditor = []
_listview = None
_window = None
def __init__(self):
return
def showAddEquation(self):
"""Creates a new instance of the dynamic editor for adding an equation"""
#create a horizontal split layout
self._window = QtGui.QWidget()
layout = QtGui.QHBoxLayout()
self._listview = QtGui.QListWidget()
layout.addWidget(self._listview)
self._listview.clicked[QtCore.QModelIndex].connect(self.changed)
self._window.setLayout(layout)
#populate the right side of the layout with a button
self.clicked()
self._window.show()
return self._window
def clicked(self):
"""Make a new button instance and add it to the window and the collection"""
window = self._window
layout = window.layout()
current = len(self.equationEditor) - 1
de = QtGui.QPushButton(str(current))
self.equationEditor.append(de)
de.clicked.connect(self.clicked)
#close the currently shown button
item = layout.takeAt(1)
if item is not None:
item.widget().close()
layout.addWidget(de)
#fill list view with items from material collection
item = QtGui.QListWidgetItem()
item.setText(de.text())
self._listview.addItem(item)
self._window.setWindowTitle("Equation Editor {}".format(str(current)))
def changed(self, index):
"""hide the object on the right side of the layout and show the button at position index in the collection"""
layout = self._window.layout()
item = layout.takeAt(1)
item.widget().close()
# insert the selected button from the collection
de = self.equationEditor[index.row()]
layout.insertWidget(1, de)
self._window.setWindowTitle("Equation Editor {}".format(str(index.row() - 1)))
de.show()
if __name__ == "__main__":
path = os.path.dirname(os.path.abspath(__file__))
app = QtGui.QApplication(sys.argv)
ewh = mainWindowHandler()
window = ewh.showAddEquation()
sys.exit(app.exec_())
I am trying a couple of sample programs that are not working in the last image of Debian for BBB. They work in a regular Xubuntu 13.10 distribution ad on windows but I have not been able to identify why Qpixmap is not working on this image. The regular widgets work Ok but the Qpixmap is not showing the image. The pyqt version installed is the 4.9.
One of the examples that I am using is the following.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
class Imagen(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout(self)
pixmap = QtGui.QPixmap("test.png")
lbl = QtGui.QLabel(self)
lbl.setPixmap(pixmap)
hbox.addWidget(lbl)
self.setLayout(hbox)
self.move(300, 200)
self.setWindowTitle('Test')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Imagen()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Assuming that your png and python files are in the same directory add the following code:
Import os.path
import os.path as osp
add the following code to initUI()
...
path = osp.join(osp.direname(__file__), 'test.png')
pixmap = QtGui.QPixmap(path)
...
Relative paths are relative to the current directory, which is not necessarily the same as the directory the script itself is in. So either use an absolute pathname, or cd to the directory the image file is in before running the script.
However, if you're interested in solving this general issue properly, the best approach is to learn how to use the Qt Resource System and the pyrcc tool. This allows you to embed icons (or any files you like) directly in your application, and thus completely side-steps any potential problems with locating files.
I use the QWebView (python 3.3 + pyside 1.1.2 + Qt 4.8) as FileViewer. Picture, Text, HTML, ... all fine, but PDF has a display problem. I tested two possible ways.
internal pdf viewer: after use webview.load(file) it loads, but
the screen is blank, after loading another file, all works fine, it
shows the file
pdf.js: after use setContent() with filebase, it
loads the webviewer.html/.js with a white page and the loading circle. The
screen only refresh if I resize the form or use the scrollbars, but
then all is fine
I don't find an event for "plugin/javascript finished loading", so I could force a repaint or so.
Here an example code for case 1:
import sys
from PySide import QtCore, QtGui, QtWebKit ##UnusedWildImport
class DialogTest(QtGui.QDialog):
def __init__(self, parent = None):
super(DialogTest, self).__init__(parent)
self.resize(620, 600)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.PreviewBox = QtWebKit.QWebView()
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.PrivateBrowsingEnabled, True)
self.PreviewBox.settings().setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)
self.PreviewBox.loadFinished.connect(self._loadfinished)
self.button_test1 = QtGui.QPushButton("File 1")
self.button_test1.clicked.connect(self._onselect1)
self.button_test2 = QtGui.QPushButton("File 2")
self.button_test2.clicked.connect(self._onselect2)
layout_Buttons = QtGui.QHBoxLayout()
layout_Buttons.addWidget(self.button_test1)
#layout_Buttons.addStretch()
layout_Buttons.addWidget(self.button_test2)
layout_Main = QtGui.QVBoxLayout()
layout_Main.addLayout(layout_Buttons)
layout_Main.addWidget(self.PreviewBox)
self.setLayout(layout_Main)
def Execute(self):
self.show()
self.exec_()
def _onselect1(self):
self.PreviewBox.load(QtCore.QUrl().fromLocalFile("c:\\tmp\\test1.pdf"))
def _onselect2(self):
self.PreviewBox.load(QtCore.QUrl().fromLocalFile("c:\\tmp\\test2.pdf"))
def _loadfinished(self, ok):
#self.PreviewBox.repaint()
pass
app = QtGui.QApplication(sys.argv)
DialogTest().Execute()
Edit: Workaround
Case 1 (webkit plugin) has an otherbug, it takes the focus to itself, so this solution isn't acceptable to me. I played with the pdf.js again and found a workaroud:
self.PreviewBox.setHtml(content, baseUrl = QtCore.QUrl().fromLocalFile(path))
self.PreviewBox.hide()
QtCore.QTimer.singleShot(700, self.PreviewBox.show)
The hide() must be after the content filling and the timer haven't to be too low.
//jay
I just solved a similar problem cleaning the QWebView before every pdf load.
Be careful with the loadFinished() signal.
In your example:
self.PreviewBox.load(QUrl('about:blank'))
or, in case we don't like 'about:blank' this may be a more portable solution:
self.PreviewBox.setHtml('<html><head></head><title></title><body></body></html>')