Refresh content of QTableView with items comming from loop in PyQt5 - python

I am working on a small GUI for managing virtual environments. In the main window, I want to display existing virtual environments from a default directory in a table view. This works so far.
Now I've noticed that if I choose a different default directory, I have to close the GUI and open it again to see the content. Unfortunately, I did not consider that in my plans (I'm still a bit inexperienced in Python).
I would like to add a button with which you can update the contents of the table view. At the same time, the existing button okButton in settings.py (which confirms the input of the selected standard directory) should also update the table view.
I tried to use pyqtsignal() and pyqtslot(), but I don't understand how to apply that to my code. The data (for example: version, path ...) for the table view comes from a loop located in organize.py. The items are collected in a list and then displayed in the table.
How can I refresh the view with a button click? Do I have to revise my code structure?
Here are the minimal reproducable parts of the code:
You can also take look at the repository here if you want. There is no commercial background.
main_ui.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import organize
import settings
class Ui_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setupUi()
def setupUi(self):
self.selectDefaultDir = settings.SetDefaultDirectory()
self.setWindowTitle("MainWindow")
self.setGeometry(430, 335, 750, 330)
centralwidget = QWidget(self)
self.v_Layout_1 = QVBoxLayout()
self.v_Layout_2 = QVBoxLayout(centralwidget)
selectButton = QPushButton(
"Set default dir", clicked=self.selectButton_clicked
)
# venv table
venvTable = QTableView(centralwidget)
venvTable.verticalHeader().setVisible(False)
venvTable.setSelectionBehavior(QAbstractItemView.SelectRows)
venvTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
venvTable.setAlternatingRowColors(True)
# adjust vertical headers
v_HeaderTV2 = venvTable.verticalHeader()
v_HeaderTV2.setVisible(False)
v_HeaderTV2.setDefaultSectionSize(27.5)
# adjust (horizontal) headers
h_HeaderTV2 = venvTable.horizontalHeader()
h_HeaderTV2.setDefaultAlignment(Qt.AlignLeft)
h_HeaderTV2.setDefaultSectionSize(180)
h_HeaderTV2.setStretchLastSection(True)
# set table view model
self.modelTV2 = QStandardItemModel(centralwidget)
self.modelTV2.setColumnCount(3)
self.modelTV2.setHorizontalHeaderLabels(
["Venv Name", "Version", "Path"]
)
venvTable.setModel(self.modelTV2)
self.v_Layout_1.addWidget(venvTable)
self.v_Layout_1.addWidget(selectButton)
self.v_Layout_2.addLayout(self.v_Layout_1)
self.setCentralWidget(centralwidget)
def popVenvTable(self):
"""
Populate the venv table view.
"""
for i in range(len(organize.venvDirs)):
self.modelTV2.insertRow(0)
self.modelTV2.setItem(0, 0, QStandardItem(organize.venvDirs[i]))
self.modelTV2.setItem(0, 1, QStandardItem(organize.venvVers[i]))
self.modelTV2.setItem(0, 2, QStandardItem(organize.venvPath[i]))
def selectButton_clicked(self):
self.selectDefaultDir.exec_()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
ui = Ui_MainWindow()
ui.popVenvTable()
ui.show()
sys.exit(app.exec_())
organize.py
# -*- coding: utf-8 -*-
from subprocess import Popen, PIPE
import os
#]===========================================================================[#
#] GET VENVS FROM DEFAULT DIRECTORY [#=======================================[#
#]===========================================================================[#
venvDirs, venvVers, venvPath = [], [], []
def getVenvs():
"""
Get the sub directories (venv directories) from the default directory.
"""
# get the path (str) to the default dir from file
with open("def/default", 'r') as default:
defDir = default.read()
default.close()
# get all folders inside the selected default dir
subDirs = os.listdir(defDir)
# loop over the subdirs of the selected default dir
for i, _dir in enumerate(subDirs):
# if there's a 'bin' folder within the subdir, and if it contains a
# file named 'python', then try to get the version
if ("bin" in os.listdir('/'.join([defDir, _dir]))
and "python" in os.listdir('/'.join([defDir, _dir, "bin"]))):
try:
getVers = Popen(
['/'.join([defDir, _dir, "bin", "python"]), "-V"],
stdout=PIPE, universal_newlines=True
)
venvVersion = getVers.communicate()[0].strip()
except Exception as err:
# in case there's a file named 'python' but
# isn't a python executable
print(
err.args[1]+':',
"[list index:", str(i)+']',
'/'.join([defDir, _dir, "bin"])
)
continue
venvDirs.append(_dir)
venvVers.append(venvVersion)
venvPath.append(defDir)
getVenvs()
if __name__ == "__main__":
for i in range(len(venvDirs)):
print(venvDirs[i])
print(venvVers[i])
print(venvPath[i])
settings.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class SetDefaultDirectory(QDialog):
"""
Set the default directory, where to look for virtual environments.
"""
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
#]===================================================================[#
#] WINDOW SETTINGS [#================================================[#
#]===================================================================[#
self.setWindowTitle("Set Default Directory")
self.setGeometry(600, 365, 500, 100)
self.setFixedSize(500, 100)
v_Layout = QVBoxLayout(self)
h_Layout = QHBoxLayout()
gridLayout = QGridLayout()
defaultDirLabel = QLabel("Default Venv Directory:")
self.defaultDirLineEdit = QLineEdit()
defaultDirLabel.setBuddy(self.defaultDirLineEdit)
folder_icon = QIcon.fromTheme("folder")
selectDirToolButton = QToolButton(
toolTip="Browse",
clicked=self.selectDirTButton_clicked
)
selectDirToolButton.setFixedSize(26, 27)
selectDirToolButton.setIcon(folder_icon)
horizontalLine = QFrame()
horizontalLine.setFrameShape(QFrame.HLine)
horizontalLine.setFrameShadow(QFrame.Sunken)
cancelButton = QPushButton(
"Cancel", clicked=self.close
)
okButton = QPushButton(
"OK", clicked=self.okButton_clicked
)
spacerItem = QSpacerItem(
40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum
)
gridLayout.addWidget(defaultDirLabel, 0, 0, 1, 1)
gridLayout.addWidget(self.defaultDirLineEdit, 0, 1, 1, 1)
gridLayout.addWidget(selectDirToolButton, 0, 2, 1, 1)
h_Layout.addItem(spacerItem)
h_Layout.addWidget(okButton, 0, Qt.AlignBottom)
h_Layout.addWidget(cancelButton, 0, Qt.AlignBottom)
v_Layout.addLayout(gridLayout)
v_Layout.addWidget(horizontalLine)
v_Layout.addLayout(h_Layout)
def selectDirTButton_clicked(self):
"""
Select directory which should be set as default.
"""
fileDiag = QFileDialog()
directory = fileDiag.getExistingDirectory()
self.defaultDirLineEdit.setText(directory)
def okButton_clicked(self):
"""
Store the absolute path (as str) to the selected dir in 'def/default'.
"""
with open("def/default", 'w') as default:
default.write(self.defaultDirLineEdit.text())
default.close()
self.close()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
settingsUI = SetDefaultDirectory()
settingsUI.show()
sys.exit(app.exec_())

Your code has the following errors or problems:
The function that the venvs look for should not fill a list but return a list, so you can call it whenever you need it
Your method that returns the vens has errors like for example it does not verify if "bin" exists or not, also do not build the routes joining with "/", instead it uses os.path.join().
Do not use relative paths but build absolute paths.
Create a data structure that stores the information of the venvs
Considering the above, the solution is:
main_ui.py
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets
import organize
import settings
class Ui_MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setupUi()
def setupUi(self):
self.selectDefaultDir = settings.SetDefaultDirectory()
self.setWindowTitle("MainWindow")
self.setGeometry(430, 335, 750, 330)
centralwidget = QtWidgets.QWidget(self)
self.v_Layout_1 = QtWidgets.QVBoxLayout()
self.v_Layout_2 = QtWidgets.QVBoxLayout(centralwidget)
selectButton = QtWidgets.QPushButton(
"Set default dir", clicked=self.selectButton_clicked
)
# venv table
venvTable = QtWidgets.QTableView(
centralwidget,
selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
editTriggers=QtWidgets.QAbstractItemView.NoEditTriggers,
alternatingRowColors=True,
)
# adjust vertical headers
v_HeaderTV2 = venvTable.verticalHeader()
v_HeaderTV2.hide()
v_HeaderTV2.setDefaultSectionSize(27.5)
# adjust (horizontal) headers
h_HeaderTV2 = venvTable.horizontalHeader()
h_HeaderTV2.setDefaultAlignment(QtCore.Qt.AlignLeft)
h_HeaderTV2.setDefaultSectionSize(180)
h_HeaderTV2.setStretchLastSection(True)
# set table view model
self.modelTV2 = QtGui.QStandardItemModel(0, 3, centralwidget)
self.modelTV2.setHorizontalHeaderLabels(["Venv Name", "Version", "Path"])
venvTable.setModel(self.modelTV2)
self.v_Layout_1.addWidget(venvTable)
self.v_Layout_1.addWidget(selectButton)
self.v_Layout_2.addLayout(self.v_Layout_1)
self.setCentralWidget(centralwidget)
def popVenvTable(self):
"""
Populate the venv table view.
"""
self.modelTV2.setRowCount(0)
for info in organize.get_venvs_default():
self.modelTV2.insertRow(0)
for i, text in enumerate((info.name, info.version, info.directory)):
self.modelTV2.setItem(0, i, QtGui.QStandardItem(text))
print(info)
def selectButton_clicked(self):
if self.selectDefaultDir.exec_() == QtWidgets.QDialog.Accepted:
self.popVenvTable()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Ui_MainWindow()
ui.popVenvTable()
ui.show()
sys.exit(app.exec_())
organize.py
# -*- coding: utf-8 -*-
import os
from subprocess import Popen, PIPE
from dataclasses import dataclass
#dataclass
class VenvInfo:
name: str
directory: str
version: str
def get_venvs(path):
if not os.path.isdir(path):
return []
infos = []
for i, _dir in enumerate(os.listdir(path)):
bin_folder = os.path.join(path, _dir, "bin")
if not os.path.isdir(bin_folder):
continue
python_binary = os.path.join(bin_folder, "python")
if not os.path.isfile(python_binary):
continue
try:
res = Popen([python_binary, "-V"], stdout=PIPE, universal_newlines=True)
out, _ = res.communicate()
version = out.strip()
info = VenvInfo(_dir, path, version)
infos.append(info)
except Exception as err:
print(f"{err.args[1]} : [list index: {i} ] {python_binary}")
return infos
def get_venvs_default():
current_dir = os.path.dirname(os.path.realpath(__file__))
default_file = os.path.join(current_dir, "def", "default")
if os.path.isfile(default_file):
with open(default_file, "r") as f:
default_dir = f.read()
return get_venvs(default_dir)
return []
if __name__ == "__main__":
for venv in get_venvs_default():
print(venv.name, venv.version, venv.directory)
settings.py
# -*- coding: utf-8 -*-
import os
from PyQt5 import QtCore, QtGui, QtWidgets
class SetDefaultDirectory(QtWidgets.QDialog):
"""
Set the default directory, where to look for virtual environments.
"""
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# ]===================================================================[#
# ] WINDOW SETTINGS [#================================================[#
# ]===================================================================[#
self.setWindowTitle("Set Default Directory")
self.move(600, 365)
self.setFixedSize(500, 100)
v_Layout = QtWidgets.QVBoxLayout(self)
h_Layout = QtWidgets.QHBoxLayout()
gridLayout = QtWidgets.QGridLayout()
defaultDirLabel = QtWidgets.QLabel("Default Venv Directory:")
self.defaultDirLineEdit = QtWidgets.QLineEdit()
defaultDirLabel.setBuddy(self.defaultDirLineEdit)
folder_icon = QtGui.QIcon.fromTheme("folder")
selectDirToolButton = QtWidgets.QToolButton(
toolTip="Browse", clicked=self.selectDirTButton_clicked, icon=folder_icon
)
selectDirToolButton.setFixedSize(26, 27)
horizontalLine = QtWidgets.QFrame(
frameShape=QtWidgets.QFrame.HLine, frameShadow=QtWidgets.QFrame.Sunken
)
cancelButton = QtWidgets.QPushButton("Cancel", clicked=self.reject)
okButton = QtWidgets.QPushButton("OK", clicked=self.okButton_clicked)
gridLayout.addWidget(defaultDirLabel, 0, 0, 1, 1)
gridLayout.addWidget(self.defaultDirLineEdit, 0, 1, 1, 1)
gridLayout.addWidget(selectDirToolButton, 0, 2, 1, 1)
h_Layout.addStretch()
h_Layout.addWidget(okButton, 0, QtCore.Qt.AlignBottom)
h_Layout.addWidget(cancelButton, 0, QtCore.Qt.AlignBottom)
v_Layout.addLayout(gridLayout)
v_Layout.addWidget(horizontalLine)
v_Layout.addLayout(h_Layout)
def selectDirTButton_clicked(self):
"""
Select directory which should be set as default.
"""
directory = QtWidgets.QFileDialog.getExistingDirectory()
self.defaultDirLineEdit.setText(directory)
def okButton_clicked(self):
"""
Store the absolute path (as str) to the selected dir in 'def/default'.
"""
current_dir = os.path.dirname(os.path.realpath(__file__))
default_file = os.path.join(current_dir, "def", "default")
with open(default_file, "w") as default:
default.write(self.defaultDirLineEdit.text())
self.accept()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
settingsUI = SetDefaultDirectory()
settingsUI.show()
sys.exit(app.exec_())

Related

pyqt5 Embedded Terminal placed outside the new tab

I tried to enhanced this code The code embeds a terminal into a pyqt tab and sends command remotely with a button. I tried to make it so that a new tab with another embedded terminal instance is added when pressing a button. I successfully implemented this but the embedded terminal in the new tab isn't placed inside.
This is the code.
import time
from gi.repository import Wnck, Gdk
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
import uuid
gi.require_version('Wnck', '3.0')
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.sess_count = 0
self.embed('xterm')
def embed(self, command, *args):
self.name_session = uuid.uuid4().hex
print ("SESSION {0} ID: {1}".format(self.sess_count,self.name_session))
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
started, procId = QtCore.QProcess.startDetached(
"xterm", ["-e", "tmux", "new", "-s", self.name_session], "."
)
if not started:
QtWidgets.QMessageBox.critical(
self, 'Command "{}" not started!'.format(command), "Eh")
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# do a bit of sleep, else window is not really found
time.sleep(0.1)
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
print(attempts, w.get_pid(), procId, w.get_pid() == procId)
if w.get_pid() == procId:
#self.window = QtGui.QWindow.fromWinId(w.get_xid())
proc.setParent(self)
win32w = QtGui.QWindow.fromWinId(w.get_xid())
win32w.setFlags(QtCore.Qt.FramelessWindowHint)
widg = QtWidgets.QWidget.createWindowContainer(win32w)
self.addTab(widg, command)
#self.insertTab(self.sess_count, widg, command)
#widg.setFocusPolicy(QtCore.Qt.StrongFocus)
#self.currentIndex(self.sess_count)
self.resize(500, 400) # set initial size of window
# self.setFocus()
# self.update()
return
attempts += 1
QtWidgets.QMessageBox.critical(
self, 'Window not found', 'Process started but window not found')
def stop(self):
QtCore.QProcess.execute(
"tmux", ["kill-session", "-t", self.name_session])
def send_command(self, command):
QtCore.QProcess.execute(
"tmux", ["send-keys", "-t", self.name_session, command, "Enter"]
)
def add_terminal(self):
self.sess_count +=1
self.embed('xterm')
#self.addTab(EmbTerminal(),"")
#self.currentIndex(self.sess_count)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.ifconfig_btn = QtWidgets.QPushButton("ifconfig")
self.ping_btn = QtWidgets.QPushButton("ping")
self.add_term_btn = QtWidgets.QPushButton("add terminal")
self.terminal = Container()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.ifconfig_btn, 0, 0)
lay.addWidget(self.ping_btn, 0, 1)
lay.addWidget(self.add_term_btn, 1, 0, 1, 2)
lay.addWidget(self.terminal, 2, 0, 2, 2)
self.resize(640, 480)
self.ifconfig_btn.clicked.connect(self.launch_ifconfig)
self.ping_btn.clicked.connect(self.launch_ping)
self.add_term_btn.clicked.connect(self.terminal.add_terminal)
def launch_ifconfig(self):
self.terminal.send_command("ifconfig")
def launch_ping(self):
self.terminal.send_command("ping 8.8.8.8")
def closeEvent(self, event):
self.terminal.stop()
super().closeEvent(event)
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Also, I found a workaround to fix this, Video Link on the workaround, but as you can see, it involves interacting the GUI, not by code. I want to fix this via code.
When clicking the Add Terminal Button, the embedded terminal should be inside the new tab.

PyQt5 drag and drop into system file explorer (with delayed encoding)?

I want to let users create files by dragging items from PyQt to the system file explorer. Since some of the files will be very large, I also need to delay setting the data to when the user finishes the drop instead of immediately on start of drag.
This example seems to be what I need: https://doc.qt.io/archives/4.6/draganddrop-delayedencoding.html
I tried converting that to a simple PyQt5 version where dragging a QPushButton into a folder will create a plain text file, but it's not working for me... when I run it dropping does nothing and my cursor looks like this:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import typing
import time
class MimeData(QtCore.QMimeData):
dataRequested = QtCore.pyqtSignal(str)
def formats(self) -> typing.List[str]:
return QtCore.QMimeData.formats(self) + ["text/plain"]
def retrieveData(self, mime_type: str, preferred_type: QtCore.QVariant.Type):
self.dataRequested.emit(mime_type)
return QtCore.QMimeData.retrieveData(self, mime_type, preferred_type)
class SourceWidget(QtWidgets.QWidget):
mimeData: MimeData = None
def __init__(self, parent=None):
super().__init__(parent)
layout = QtWidgets.QVBoxLayout()
button = QtWidgets.QPushButton("Drag Me")
button.pressed.connect(self.start_drag)
layout.addWidget(button)
self.setLayout(layout)
#QtCore.pyqtSlot()
def create_data(self, mime_type):
if mime_type == "text/plain":
time.sleep(0.25) # Simulate large file
self.mimeData.setData("text/plain", b"my text file contents")
#QtCore.pyqtSlot()
def start_drag(self):
self.mimeData = MimeData()
self.mimeData.dataRequested.connect(self.create_data)
drag = QtGui.QDrag(self)
drag.setMimeData(self.mimeData)
drag.exec(QtCore.Qt.CopyAction)
if __name__ == "__main__":
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
w = SourceWidget()
w.show()
app.exec_()
Here's what I ended up with for dragging and dropping files from PyQt5 into file explorer, and only having the file write once the mouse is released to finalize the drop.
import time
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
import tempfile
import os
# Use win32api on Windows because the pynput and mouse packages cause lag
# https://github.com/moses-palmer/pynput/issues/390
if os.name == 'nt':
import win32api
def mouse_pressed():
return win32api.GetKeyState(0x01) not in [0, 1]
else:
import mouse
def mouse_pressed():
return mouse.is_pressed()
class DelayedMimeData(QtCore.QMimeData):
def __init__(self):
super().__init__()
self.callbacks = []
def add_callback(self, callback):
self.callbacks.append(callback)
def retrieveData(self, mime_type: str, preferred_type: QtCore.QVariant.Type):
mp = mouse_pressed()
if not mp:
for callback in self.callbacks.copy():
self.callbacks.remove(callback)
callback()
return QtCore.QMimeData.retrieveData(self, mime_type, preferred_type)
class Navigator(QtWidgets.QTreeWidget):
def __init__(self):
super().__init__()
self.setHeaderLabels(["Name"])
QtWidgets.QTreeWidgetItem(self, ['Test1'])
QtWidgets.QTreeWidgetItem(self, ['Test2'])
QtWidgets.QTreeWidgetItem(self, ['Test3'])
self.setAcceptDrops(True)
self.setDragEnabled(True)
self.setDragDropMode(self.DragDrop)
self.setDefaultDropAction(Qt.MoveAction)
self.setSelectionMode(self.ExtendedSelection)
self.setSelectionBehavior(self.SelectRows)
self.setContextMenuPolicy(Qt.CustomContextMenu)
def startDrag(self, actions):
drag = QtGui.QDrag(self)
names = [item.text(0) for item in self.selectedItems()]
mime = DelayedMimeData()
path_list = []
for name in names:
path = os.path.join(tempfile.gettempdir(), 'DragTest', name + '.txt')
os.makedirs(os.path.dirname(path), exist_ok=True)
print(path)
def write_to_file(path=path, name=name, widget=self):
with open(path, 'w+') as f:
print("Writing large file(s)...")
time.sleep(2) # Sleep to simulate long file write
f.write(f"Contents of {name}")
mime.add_callback(write_to_file)
path_list.append(QtCore.QUrl.fromLocalFile(path))
mime.setUrls(path_list)
mime.setData('application/x-qabstractitemmodeldatalist',
self.mimeData(self.selectedItems()).data('application/x-qabstractitemmodeldatalist'))
drag.setMimeData(mime)
drag.exec_(Qt.MoveAction)
super().startDrag(actions)
app = QtWidgets.QApplication([])
nav = Navigator()
nav.show()
app.exec_()

How to save and restore current state and values using PyQt4

I am trying to figure out a way to save the current state and all values in the gui such as the text in QLineEdit and QEditText widgets.
I found this code which I have been trying to use and it seems that I can get it to save everything okay when I exit the GUI but when I open it, all it seems to restore is the window dimensions if I had moved them previously.
I can see in the ini file that everything gets saved including any text in the 2 widgets but I cant get the text to restore when I open the GUI. Does anyone know how I can get the text values to restore as well?
Here is what I am currently working with.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def restore(settings):
finfo = QFileInfo(settings.fileName())
if finfo.exists() and finfo.isFile():
for w in qApp.allWidgets():
mo = w.metaObject()
if w.objectName() != "":
for i in range(mo.propertyCount()):
name = mo.property(i).name()
val = settings.value("{}/{}".format(w.objectName(), name), w.property(name))
w.setProperty(name, val)
def save(settings):
for w in qApp.allWidgets():
mo = w.metaObject()
if w.objectName() != "":
for i in range(mo.propertyCount()):
name = mo.property(i).name()
settings.setValue("{}/{}".format(w.objectName(), name), w.property(name))
class MainWindow(QWidget):
settings = QSettings("gui.ini", QSettings.IniFormat)
def __init__(self):
super(MainWindow, self).__init__()
self.setObjectName("MainWindow")
restore(self.settings)
self.layout = QGridLayout()
self.text_Box = QTextEdit(self)
self.text_Box.setObjectName("text_Box")
self.layout.addWidget(self.text_Box, 2, 0, 1, 1)
self.quit_Button = QPushButton(self)
self.quit_Button.setMaximumSize(30, 30)
self.quit_Button.setObjectName("quit_Button")
self.layout.addWidget(self.quit_Button, 3, 0, 1, 1)
self.line_Edit = QLineEdit(self)
self.line_Edit.setObjectName("line_Edit")
self.layout.addWidget(self.line_Edit, 1, 0, 1, 1)
self.quit_Button.clicked.connect(self.exitGUI)
self.setLayout(self.layout)
def closeEvent(self, event):
save(self.settings)
QWidget.closeEvent(self, event)
def exitGUI(self):
self.close()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
sys.exit(app.exec_())

Python: PyQt QTreeview example - selection

I'm using Python 2.7 and Qt designer and I'm new to MVC:
I have a View completed within Qt to give me a directory tree list, and the controller in place to run things. My question is:
Given a Qtree view, how may I obtain a directory once a dir is selected?
Code snap shot is below, I suspect it's SIGNAL(..) though I'm unsure:
class Main(QtGui.QMainWindow):
plot = pyqtSignal()
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# create model
model = QtGui.QFileSystemModel()
model.setRootPath( QtCore.QDir.currentPath() )
# set the model
self.ui.treeView.setModel(model)
**QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked()'), self.test)**
def test(self):
print "hello!"
The signal you're looking for is selectionChanged emmited by the selectionModel owned by your tree. This signal is emmited with the selected item as first argument and the deselected as second, both are instances of QItemSelection.
So you might want to change the line:
QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked()'), self.test)
to
QtCore.QObject.connect(self.ui.treeView.selectionModel(), QtCore.SIGNAL('selectionChanged()'), self.test)
Also I recommend you to use the new style for signals and slots. Redefine your test function as:
#QtCore.pyqtSlot("QItemSelection, QItemSelection")
def test(self, selected, deselected):
print("hello!")
print(selected)
print(deselected)
Here you have a working example:
from PyQt4 import QtGui
from PyQt4 import QtCore
class Main(QtGui.QTreeView):
def __init__(self):
QtGui.QTreeView.__init__(self)
model = QtGui.QFileSystemModel()
model.setRootPath( QtCore.QDir.currentPath() )
self.setModel(model)
QtCore.QObject.connect(self.selectionModel(), QtCore.SIGNAL('selectionChanged(QItemSelection, QItemSelection)'), self.test)
#QtCore.pyqtSlot("QItemSelection, QItemSelection")
def test(self, selected, deselected):
print("hello!")
print(selected)
print(deselected)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
PyQt5
In PyQt5 is a little bit different (thanks to Carel and saldenisov for comments and aswer.)
... connect moved from being an object method to a method acting upon the attribute when PyQt went from 4 to 5
So instead the known:
QtCore.QObject.connect(self.ui.treeView, QtCore.SIGNAL('clicked()'), self.test)
now you write:
class Main(QTreeView):
def __init__(self):
# ...
self.setModel(model)
self.doubleClicked.connect(self.test) # Note that the the signal is now a attribute of the widget.
Here is a the example (by saldenisov) using PyQt5.
from PyQt5.QtWidgets import QTreeView,QFileSystemModel,QApplication
class Main(QTreeView):
def __init__(self):
QTreeView.__init__(self)
model = QFileSystemModel()
model.setRootPath('C:\\')
self.setModel(model)
self.doubleClicked.connect(self.test)
def test(self, signal):
file_path=self.model().filePath(signal)
print(file_path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
In PyQt5 it can be done in this way:
from PyQt5.QtWidgets import QTreeView,QFileSystemModel,QApplication
class Main(QTreeView):
def __init__(self):
QTreeView.__init__(self)
model = QFileSystemModel()
model.setRootPath('C:\\')
self.setModel(model)
self.doubleClicked.connect(self.test)
def test(self, signal):
file_path=self.model().filePath(signal)
print(file_path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
If I understand the question correctly you would like the directory or file name selected.
This is what I do:
from PyQt4 import QtGui
from PyQt4 import QtCore
# ---------------------------------------------------------------------
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(600,400)
self.setWindowTitle("Treeview Example")
self.treeview = QtGui.QTreeView(self)
self.treeview.model = QtGui.QFileSystemModel()
self.treeview.model.setRootPath( QtCore.QDir.currentPath() )
self.treeview.setModel(self.treeview.model)
self.treeview.setColumnWidth(0, 200)
self.setCentralWidget(self.treeview)
self.treeview.clicked.connect(self.on_treeview_clicked)
# ---------------------------------------------------------------------
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeview_clicked(self, index):
indexItem = self.treeview.model.index(index.row(), 0, index.parent())
# path or filename selected
fileName = self.treeview.model.fileName(indexItem)
# full path/filename selected
filePath = self.treeview.model.filePath(indexItem)
print(fileName)
print(filePath)
# ---------------------------------------------------------------------
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I tried this alternative to get the file name...
Instead of:
indexItem = self.treeview.model.index(index.row(), 0, index.parent())
# path or filename selected
fileName = self.treeview.model.fileName(indexItem)
I tried:
# path or filename selected
fileName = index.internalPointer().fileName
Which also seems to work...

PyQt and QtreeView : How get the path from the selected file

I'am a beginner with PyQt. All is in the title :I don't understand how can I get the path (and the name) from the selected file?
I wish later update a QListView with this path.
Here my script :
# -*- coding: utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class MyWidget(QWidget):
# SIGNAUX
tree_model_indexSig = pyqtSignal(QModelIndex)
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
# connect signaux to slots
self.tree_model_indexSig.connect(self.indexSlot)
self.model = QFileSystemModel()
self.model.setRootPath(QDir.rootPath())
''' GUI '''
# instantiation du treeview et du listview
self.treeView = QTreeView(self)
self.treeView.setGeometry(QRect(10, 20, 601, 231))
self.treeView.setObjectName("treeView")
''' END GUI '''
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.model.index(QDir.rootPath()))
# clicked.CONNECT
self.treeView.clicked.connect(self.treeClicked)
self.treeView.show()
def treeClicked(self, checked=False):
# EMIT
self.tree_model_indexSig.emit(self.model.index(QDir.rootPath()))
# Definition des slots
def indexSlot(self, *args):
# 1 argument --> ModelIndex
path = QDirModel(args[0]).filePath(args[0].currentIndex())
print path
'''
How get the path from the selected file ??
'''
if __name__=='__main__':
app = QApplication(sys.argv)
widget = MyWidget()
widget.show()
app.exec_()
Thanks for help!
Something like this should work for you:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtCore, QtGui
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.pathRoot = QtCore.QDir.rootPath()
self.model = QtGui.QFileSystemModel(self)
self.model.setRootPath(self.pathRoot)
self.indexRoot = self.model.index(self.model.rootPath())
self.treeView = QtGui.QTreeView(self)
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.indexRoot)
self.treeView.clicked.connect(self.on_treeView_clicked)
self.labelFileName = QtGui.QLabel(self)
self.labelFileName.setText("File Name:")
self.lineEditFileName = QtGui.QLineEdit(self)
self.labelFilePath = QtGui.QLabel(self)
self.labelFilePath.setText("File Path:")
self.lineEditFilePath = QtGui.QLineEdit(self)
self.gridLayout = QtGui.QGridLayout()
self.gridLayout.addWidget(self.labelFileName, 0, 0)
self.gridLayout.addWidget(self.lineEditFileName, 0, 1)
self.gridLayout.addWidget(self.labelFilePath, 1, 0)
self.gridLayout.addWidget(self.lineEditFilePath, 1, 1)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addLayout(self.gridLayout)
self.layout.addWidget(self.treeView)
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeView_clicked(self, index):
indexItem = self.model.index(index.row(), 0, index.parent())
fileName = self.model.fileName(indexItem)
filePath = self.model.filePath(indexItem)
self.lineEditFileName.setText(fileName)
self.lineEditFilePath.setText(filePath)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.resize(666, 333)
main.move(app.desktop().screen().rect().center() - main.rect().center())
main.show()
sys.exit(app.exec_())

Categories

Resources