I have a class to create a PyQt4 widget and another class to parse xml to get inputs. I want to create an UI dyanamically add buttons to the widget, reading from the xml(s) passed, which is not happening:
import sys
import xml.etree.ElementTree as ET
from PyQt4 import QtGui
ui = None
class userInterface(QtGui.QWidget):
def __init__(self):
super(userInterface, self).__init__()
def getFilesWindow(self):
self.filesSelected = []
fDialog = QtGui.QFileDialog.getOpenFileNames(self, 'Open file', '/Learning/Python/substance_XML_reading/', "SBS (*.sbs)")
for path in fDialog:
if path:self.filesSelected.append(path)
return self.filesSelected
class ParseXML():
def getXMLTags(self,fileList):
self.tags = []
if fileList:
print fileList
for eachFile in fileList:
fileToParse = ET.parse(eachFile)
root = fileToParse.getroot()
for child in root:
self.tags.append(child.tag)
return self.tags
def getSetUI(flist):
global ui
if flist:
tagsForBtns = ParseXML().getXMLTags(flist)
print tagsForBtns
for eachitem in tagsForBtns:
btn = QtGui.QPushButton(ui,eachitem)
def main():
app = QtGui.QApplication(sys.argv)
ui = userInterface()
fileListForUIGen = ui.getFilesWindow() # will return a list of files
getSetUI(fileListForUIGen) # Parses each file, gets tags, creates buttons and has to add to the current window..NOT WORKING
ui.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
In order to add buttons to a widget, you need to place them inside a QLayout
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
self.ui_lay = QtGui.QVBoxLayout()
self.setLayout(self.ui_lay)
def addButton(self, text):
btn = QtGui.QPushButton(text, self)
self.ui_lay.addWidget(btn)
...
for eachitem in tagsForBtns:
ui.addButton(eachitem)
Also, make sure to use global ui in your main() function if you're going to be doing it this way.
Personally, I don't see the reasons for splitting up all the function calls. I would just put them all in the UserInterface class.
Related
I'm building the desktop app. Before class MyMainWindow(QMainWindow, Ui_MainWindow): I have a section, that processes some initial data (code 1)
sample_directory_2 = []
sample_files_2 = []
for (dirpath, dirnames, filenames) in walk('./Processed'):
filenames = [f for f in filenames if not f[0] == '.']
sample_files_2.extend(filenames)
break
the_dir = "Processed"
paths_2 = [os.path.abspath(os.path.join(the_dir,filename)) for filename in os.listdir(the_dir) if not filename.startswith('.')]
sample_directory_2.append(sample_files_2)
sample_directory_2.append(paths_2)
processed_info = []
for i in range(len(sample_directory_2[0])):
file_info = []
sample_file_2 = sample_directory_2[0][i]
sample_path_2 = sample_directory_2[1][i]
sample_info_2 = pd.read_excel(ospath(sample_path_2), header = None, sheetname = 3)
sample_info_2 = sample_info_2.iloc[0][0:3]
file_info.append(sample_file_2)
sample_info_2_list = numpy.array(sample_info_2).tolist()
file_info.extend(sample_info_2_list)
processed_info.append(file_info)
After this section in class MyMainWindow(QMainWindow, Ui_MainWindow): I have the code that creates QTableList and sets values to its items(code 2):
self.clickSample_list.setRowCount(len(processed_info))
self.clickSample_list.setColumnCount(len(processed_info[0]))
labels = ['Имя', 'Массовые отклики', 'Процентранг', 'Валидность']
self.clickSample_list.setHorizontalHeaderLabels(labels)
for row in range(len(processed_info)):
for column in range(len(processed_info[row])):
self.clickSample_list.setItem(row, column, QTableWidgetItem(str(processed_info[row][column])))
Code 1 section takes pretty long time, and only after that, the UI begins to load.
I guess, what I need to do is: to make Code 1 a separate function and call it after UI loads. How to do that? Is there a command that calls the function after the loading of UI?
I think what you probably need to do is place the Code 1 section in a separate function in your class and call it at some event in the GUI.
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
#Initializing the UI
self.setupUi(self)
self.buttonName.clicked.connect(self.functionName)
def functionName(self):
...
#your code
...
Or if your Application restricts the use of this code outside the class then you can try calling it in the main function
from sys import argv, exit
def functionName():
...
#code
....
class Window(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
....
def main():
app = QApplication(argv)
gui = Window()
gui.show()
functionName()
exit(app.exec_())
How can I trigger the changing of an icon when the user clicks the Icon only for the items/rows in the list which are of type File. Each row in the treeview contains an object in the UserRole called TreeItem which stores if the item is marked as a favorite and also the filepath.
In short is there a way to know if a the Decoration 'icon' is clicked by the user?
The tool simply just goes through a directory recursively and collects the files and folders.
class TreeItem(object):
def __init__(self, filepath):
self.filepath = filepath
self.isFavorite = False
Dropbox links to icons
https://www.dropbox.com/s/3pt0ev2un7eoswh/file_off.svg?dl=0
https://www.dropbox.com/s/xext3m9d4atd3i6/file_on.svg?dl=0
https://www.dropbox.com/s/6d750av0y77hq0g/folder.svg?dl=0
Be sure to change the directory path for testing
Tool Code:
import sys
import os
from PySide import QtGui, QtCore, QtSvg
DIR_ICON_PATH = 'folder.svg'
FILE_ICON_OFF = 'file_off.svg'
FILE_ICON_ON = 'file_on.svg'
class TreeItem(object):
def __init__(self, filepath):
self.filepath = filepath
self.isFavorite = False
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# formatting
self.resize(550, 400)
self.setWindowTitle("Toychest")
# widgets
self.treeview = QtGui.QTreeView()
self.treeview.setHeaderHidden(True)
self.treeview.setUniformRowHeights(True)
self.treeview.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.source_model = QtGui.QStandardItemModel()
self.treeview.setModel(self.source_model)
# signals
self.treeview.doubleClicked.connect(self.doubleClickedItem)
# main layout
mainLayout = QtGui.QGridLayout()
mainLayout.setContentsMargins(0,0,0,0)
mainLayout.addWidget(self.treeview)
self.setLayout(mainLayout)
self.initDirectory('C:/Users/jmartini/Downloads')
# Functions
# ------------------------------------------------------------------------------
def initDirectory(self, path):
new_item = self.newItem(path)
self.readDirectory(path, new_item)
self.source_model.appendRow(new_item)
def readDirectory(self, path, parent_item):
directory = os.listdir(path)
for file_name in directory:
file_path = path + '/' + file_name
new_item = self.newItem(file_path)
parent_item.appendRow(new_item)
if os.path.isdir(file_path):
self.readDirectory(file_path, new_item)
def newItem(self, path):
# create Object
obj = TreeItem(path)
title = os.path.basename(path)
item = QtGui.QStandardItem()
item.setData(obj, role=QtCore.Qt.UserRole)
icon_path = FILE_ICON_OFF
if os.path.isdir(path):
icon_path = DIR_ICON_PATH
icon = QtGui.QIcon(icon_path)
item.setText(title)
item.setIcon(icon)
return item
def doubleClickedItem(self, idx):
if not idx.isValid():
return
obj = idx.data(QtCore.Qt.UserRole)
print obj.filepath, obj.isFavorite
# print idx.parent(), idx.parent().isValid()
# model = idx.model()
# print model.index(idx.row(), 0, parent=idx.parent()).data()
# Main
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
You have to implement the mousePressEvent and compare the mouse position with icon position and proceed accordingly.
You can use QStandardItemModel.itemFromIndex in order to access to the underlying item. The model can be obtained via QModelIndex.model. So the following would do:
item = idx.model().itemFromIndex(idx)
obj = idx.data(QtCore.Qt.UserRole).toPyObject()
obj.isFavorite = (obj.isFavorite + 1) % 2
if obj.isFavorite:
item.setIcon(QtGui.QIcon(FILE_ICON_ON))
else:
item.setIcon(QtGui.QIcon(FILE_ICON_OFF))
If required you can access obj.filepath in order to check whether the clicked item corresponds to a file.
I have a list which gets one element each time user opens a file. I need to create a button with the file's name (element from the list), each time this file is appended to a list, and put this button into a scroll-area.
The problem is that I always have only one button, that just changes its name:
filenames = []
def addfiles():
fileName = QtGui.QFileDialog.getOpenFileName()
fileDirectory = unicode(fileName)
global filenames
filenames.append(fileDirectory)
button = QtGui.QPushButton(os.path.basename(fileDirectory))
window.scrollArea.setWidget(button)
I know that the problem is that I add the same object (button) to the scroll-area, but I don't know how to fix it.
The Problem is not that you add the same button, but that you sort of replace the Widget in the scrollArea.
A better way would be to create a QHBoxLayout and than add the buttons to the layout.
filenames = []
lay = QtGui.QHboxLayout()
window.scrollArea.setLayout(lay)
def addfiles():
fileName= QtGui.QFileDialog.getOpenFileName()
fileDirectory = unicode(fileName)
global filenames
filenames.append(fileDirectory)
button = QtGui.QPushButton(os.path.basename(fileDirectory))
lay.addWidget(button)
In a sort of that way it should work. Here is a small working example:
from PyQt4 import QtGui
import sys
filenames = []
class TestGui(QtGui.QWidget):
""" A Fast test gui show how to create buttons in a ScrollArea"""
def __init__(self):
super(TestGui, self).__init__()
self.lay = QtGui.QHBoxLayout()
self.sA = QtGui.QScrollArea()
self.sA_lay = QtGui.QVBoxLayout()
self.sA.setLayout(self.sA_lay)
self.closeGui = QtGui.QPushButton("Close")
self.add_file_button = QtGui.QPushButton("Add File")
self.lay.addWidget(self.closeGui)
self.lay.addWidget(self.add_file_button)
self.lay.addWidget(self.sA)
self.setLayout(self.lay)
self.connect_()
self.show()
def connect_(self):
self.add_file_button.clicked.connect(self.__add_file_to_list)
self.closeGui.clicked.connect(self.close)
return
def __add_file_to_list(self):
fname = QtGui.QFileDialog.getOpenFileName()
global filenames
filenames.append(fname)
button = QtGui.QPushButton(fname)
self.sA_lay.addWidget(button)
return
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
tg = TestGui()
sys.exit(app.exec_())
The problem is that you're not adding a layout to the scrollLayout, you're setting the scrollArea's widget:
#!/usr/bin/env python
import os, sys
from PyQt4 import QtCore, QtGui
filenames = []
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.centralwidget = QtGui.QWidget(self)
self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
self.scrollArea = QtGui.QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QtGui.QWidget(self.scrollArea)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout.addWidget(self.scrollArea)
self.setCentralWidget(self.centralwidget)
# create a layout for your scrollarea
self.formLayout = QtGui.QFormLayout(self.scrollAreaWidgetContents)
self.addFiles()
def addFiles(self):
global filenames
filenames.append("~/files/newFile.txt")
button = QtGui.QPushButton(os.path.basename(filenames[-1]))
self.formLayout.addWidget(button)
I am new to pyqt.I am trying to invoke a child GUI when a button is clicked in the parent GUI. In this process, parent GUI has to wait for the child GUI to be closed by the user after selecting some inputs. But this is not happening, Parent GUI does execute the next lines after which the child GUI has been invoked. Below is the code where I am passing an argument to child GUI from parent GUI. The child GUI will return value based on OK/Cancel button click
Code:
import sys
from PyQt4 import QtGui,QtCore,Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Child(QtGui.QWidget):
def __init__(self,switches=None):
super(Child,self).__init__()
self.swwidget = QtGui.QWidget()
self.swlayout = QtGui.QGridLayout()
switches = ['abc1','def1']
switches.sort()
self.switches = switches
def switchesUI(self):
self.swwidget.setWindowModality(QtCore.Qt.ApplicationModal)
self.swl = len(self.switches)
self.sw = {}
self.addsw = []
print ("I am in switchesUI")
#Add the switches to layout dynamically
for i in range(self.swl):
self.sw[i] = QtGui.QCheckBox(self.switches[i])
self.swlayout.addWidget(self.sw[i],i,0)
self.swbuttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel);
self.swbuttonbox.setOrientation(QtCore.Qt.Horizontal)
self.swlayout.addWidget(self.swbuttonbox)
self.swwidget.setWindowTitle('Switches')
self.swwidget.setLayout(self.swlayout)
self.swwidget.show()
self.connect(self.swbuttonbox,QtCore.SIGNAL("accepted()"),self.swaccept)
self.connect(self.swbuttonbox,QtCore.SIGNAL("rejected()"),self.swreject)
def swaccept(self):
for i in range(self.swl):
if self.sw[i].isChecked():
self.addsw.append(self.switches[i])
self.swwidget.close()
return self.addsw
def swreject(self):
self.swwidget.close()
return None
class Parent(QtGui.QWidget):
def __init__(self):
super(Parent,self).__init__()
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.button)
self.assw = ['Test1','Test2']
self.CH = Child(self.assw)
self.connect(self.button,SIGNAL("clicked()"),self.popup)
print ("Child GUI closed")
def popup(self):
self.CH.switchesUI()
def main():
app = QtGui.QApplication(sys.argv)
form = Parent()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
After the button "Test" is clicked, a child GUI will pop-up. I don't want the statement "Child GUI Closed" to be printed till the child GUI is closed.
Can someone suggest me how to achieve this functionality ?
You have to handle closeEvent to perform operations when a window wants to close, also since your Child class inherits from QWidget which means it's a QWidget itself you dont need to create another one with self.swwidget
import sys
from PyQt4 import QtGui,QtCore,Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Child(QtGui.QWidget):
def __init__(self,switches=None):
super(Child,self).__init__()
# self.swwidget = QtGui.QWidget() # you don't need to do this you can add all the properties to self
self.swlayout = QtGui.QGridLayout()
switches = ['abc1','def1']
switches.sort()
self.switches = switches
def switchesUI(self):
self.setWindowModality(QtCore.Qt.ApplicationModal)
self.swl = len(self.switches)
self.sw = {}
self.addsw = []
print ("I am in switchesUI")
#Add the switches to layout dynamically
for i in range(self.swl):
self.sw[i] = QtGui.QCheckBox(self.switches[i])
self.swlayout.addWidget(self.sw[i],i,0)
self.swbuttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel);
self.swbuttonbox.setOrientation(QtCore.Qt.Horizontal)
self.swlayout.addWidget(self.swbuttonbox)
self.setWindowTitle('Switches')
self.setLayout(self.swlayout)
self.show()
self.connect(self.swbuttonbox,QtCore.SIGNAL("accepted()"),self.swaccept)
self.connect(self.swbuttonbox,QtCore.SIGNAL("rejected()"),self.swreject)
def swaccept(self):
for i in range(self.swl):
if self.sw[i].isChecked():
self.addsw.append(self.switches[i])
self.close()
return self.addsw
def swreject(self):
self.close()
return None
def closeEvent(self, event):
print ("Child GUI closed")
class Parent(QtGui.QWidget):
def __init__(self):
super(Parent,self).__init__()
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.button)
self.assw = ['Test1','Test2']
self.CH = Child(self.assw)
self.connect(self.button,SIGNAL("clicked()"),self.popup)
def popup(self):
self.CH.switchesUI()
def main():
app = QtGui.QApplication(sys.argv)
form = Parent()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
In the example below, I would like to fill my QListWidget with files opening a Qdialog. I don't understand how I can add the files selected in the list. Should I do a new class? How can I connect the two methods setupList and addFiles?
import sys
from PyQt4 import QtCore, QtGui
from datapath import *
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
self.sources =[]
self.setupActions()
self.setupList()
self.setupUi()
self.setupStatusBars()
def addFiles(self):
files = QtGui.QFileDialog.getOpenFileNames(
self,"Open File", dirpath, "txt Files (*.txt)")
for string in files:
self.sources.append(str(string))
return self.sources
def setupActions(self):
self.exitAct = QtGui.QAction(
QtGui.QIcon(':/images/exit.png'),
"E&xit", self, shortcut="Ctrl+Q",
statusTip="Exit the application", triggered=self.close
)
self.addFilesAct = QtGui.QAction(
QtGui.QIcon(':/images/open.png'),
"Add &Files", self, shortcut=QtGui.QKeySequence.Open,
statusTip="Open an existing file", triggered=self.addFiles
)
def setupList(self):
#FileList
self.FileList = QtGui.QListWidget(self)
self.FileList.addItems(self.sources)
def setupUi(self):
#Window size
horiz = 300
vert = 300
self.setGeometry(self.width()/2, self.height()/2,horiz,vert)
self.setWindowTitle("test")
#MenuBar
self.FileMenu = self.menuBar().addMenu("&File")
self.FileMenu.addAction(self.addFilesAct)
self.FileMenu.addSeparator();
self.FileMenu.addAction(self.exitAct)
#ToolBar
self.fileToolBar = self.addToolBar("Open")
self.fileToolBar.addAction(self.addFilesAct)
self.fileToolBar.setIconSize(QtCore.QSize(64,64))
#Build Layout
mainLayout = QtGui.QVBoxLayout()
mainLayout.addWidget(self.FileList)
widget = QtGui.QWidget()
widget.setLayout(mainLayout)
self.setCentralWidget(widget)
def setupStatusBars(self):
self.statusBar().showMessage("Ready")
def main():
app = QtGui.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
When self.sources is changed, the elements of the QListWidget will not change. So self.FileList.addItems(self.sources) in setupList() should be removed and instead put in addFiles() so that every time the files are selected in the dialog, the QListWidget's addItems method is called. Then return self.sources in addFiles() would be unnecessary.
In order to append files to the listwidget, the addFiles method should look like this:
def addFiles(self):
files = QtGui.QFileDialog.getOpenFileNames(
self, "Open File", dirpath, "txt Files (*.txt)")
for string in files:
self.FileList.addItem(string)
The source list looks like it might be redundant. If you need to get the full list of files, you can do something like this:
sources = []
for row in range(self.FileList.count()):
item = self.FileList.item(row)
# python3
sources.append(item.text())
# python2, convert to python strings
# sources.append(unicode(item.text()))
print(sources)