I am learning how to create my own processing toolbox, I just want to be able to select a text file and display the content in the multiline text box. I can put "some text" in it but not the one loaded from my text file.
I am not sure what I am doing wrong.
Could it be that this type of dialog does not support what I am trying to do?
from qgis.core import (
QgsProcessingAlgorithm,
QgsProcessingParameterFile,
QgsProcessingParameterString,
QgsProcessingOutputString
)
from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout, QLabel, QPlainTextEdit, QPushButton, QFileDialog, QLineEdit
import os
class MultilineTextFileAlgorithm(QgsProcessingAlgorithm):
INPUT_FILE = 'INPUT_FILE'
INPUT_TEXT = 'INPUT_TEXT'
OUTPUT_TEXT = 'OUTPUT_TEXT'
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return MultilineTextFileAlgorithm()
def name(self):
return 'multilinetextfile'
def displayName(self):
return self.tr('Multiline Text with File Content')
def group(self):
return self.tr('Custom Scripts')
def groupId(self):
return 'customscripts'
def initAlgorithm(self, config=None):
# Add a file input parameter
self.addParameter(
QgsProcessingParameterFile(
self.INPUT_FILE,
self.tr('Select a text file'),
behavior=QgsProcessingParameterFile.File,
extension='txt'
)
)
# Add a multi-line text input parameter with default text
self.addParameter(
QgsProcessingParameterString(
self.INPUT_TEXT,
self.tr('File content (editable)'),
defaultValue='some text', # Preloaded with "some text"
multiLine=True
)
)
# Add an output string for demonstration
self.addOutput(
QgsProcessingOutputString(
self.OUTPUT_TEXT,
self.tr('Processed Text Summary')
)
)
def createCustomWidget(self, parent, alg):
"""Override the dialog to dynamically update text content."""
dialog = QDialog(parent)
dialog.setWindowTitle('Multiline Text with File Content')
layout = QVBoxLayout()
# File selection
self.file_label = QLabel("Select a Text File:")
self.file_input = QLineEdit()
self.file_browse_button = QPushButton("Browse")
layout.addWidget(self.file_label)
layout.addWidget(self.file_input)
layout.addWidget(self.file_browse_button)
# Multiline text box
self.text_box = QPlainTextEdit()
self.text_box.setPlainText("some text") # Default text
layout.addWidget(self.text_box)
# Connect file button to update content
self.file_browse_button.clicked.connect(self.load_file)
dialog.setLayout(layout)
return dialog
def load_file(self):
"""Dynamically load content of the selected file into the text box."""
file_path, _ = QFileDialog.getOpenFileName(
None, "Select Text File", "", "Text Files (*.txt)"
)
if file_path:
self.file_input.setText(file_path)
try:
with open(file_path, 'r') as file:
content = file.read()
self.text_box.setPlainText(content)
except Exception as e:
self.text_box.setPlainText(f"Error reading file: {e}")
def processAlgorithm(self, parameters, context, feedback):
# Get the input text (editable by the user)
input_text = self.text_box.toPlainText()
# Count lines and words
line_count = len(input_text.splitlines())
word_count = len(input_text.split())
# Create a summary
processed_text = f"The text contains {line_count} lines and {word_count} words."
# Return the summary
return {self.OUTPUT_TEXT: processed_text}
1 Answer 1
To use a custom widget in a processing algorithm you need to create a custom widget class (use QWidget
not QDialog
) and a widget wrapper class. The WidgetWrapper
class needs to have a createWidget()
method which returns an instance of the custom widget class, and a value()
method which returns a value of the same type as the parameter for which it is a wrapper (in this case a string). Then set the widget wrapper in the setMetadata()
method on a standard processing parameter in this case QgsProcessingParameterString
.
Here is a working example:
from processing.gui.wrappers import WidgetWrapper
from qgis.core import (
QgsProcessingAlgorithm,
QgsProcessingParameterString,
QgsProcessingOutputString
)
from qgis.gui import QgsFileWidget
from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtWidgets import QWidget, QVBoxLayout, QLabel, QPlainTextEdit
import os
class MultilineTextFileAlgorithm(QgsProcessingAlgorithm):
INPUT_TEXT = 'INPUT_TEXT'
OUTPUT_TEXT = 'OUTPUT_TEXT'
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return MultilineTextFileAlgorithm()
def name(self):
return 'multilinetextfile'
def displayName(self):
return self.tr('Multiline Text with File Content')
def group(self):
return self.tr('Custom Scripts')
def groupId(self):
return 'customscripts'
def initAlgorithm(self, config=None):
input_text = QgsProcessingParameterString(self.INPUT_TEXT, 'Input text from a text file')
input_text.setMetadata({'widget_wrapper': {'class': CustomParametersWidget}})
self.addParameter(input_text)
# Add an output string for demonstration
self.addOutput(
QgsProcessingOutputString(
self.OUTPUT_TEXT,
self.tr('Processed Text Summary')
)
)
def processAlgorithm(self, parameters, context, feedback):
# Get the input text (editable by the user)
input_text = self.parameterAsString(parameters, self.INPUT_TEXT, context)
# Count lines and words
line_count = len(input_text.splitlines())
word_count = len(input_text.split())
# Create a summary
processed_text = f"The text contains {line_count} lines and {word_count} words."
# Return the summary
return {self.OUTPUT_TEXT: processed_text}
# Widget Wrapper class
class CustomParametersWidget(WidgetWrapper):
def createWidget(self):
self.cpw = CustomWidget()
return self.cpw
def value(self):
return self.cpw.get_content()
# Custom Widget class
class CustomWidget(QWidget):
def __init__(self):
super(CustomWidget, self).__init__()
layout = QVBoxLayout()
# File selection
self.file_label = QLabel("Select a Text File:")
self.file_input = QgsFileWidget()
self.file_input.setFilter('*.txt')
layout.addWidget(self.file_label)
layout.addWidget(self.file_input)
# Multiline text box
self.text_box = QPlainTextEdit()
self.text_box.setPlainText("some text") # Default text
layout.addWidget(self.text_box)
self.setLayout(layout)
self.file_input.fileChanged.connect(self.load_file)
def load_file(self, file_path):
"""Dynamically load content of the selected file into the text box."""
if file_path:
try:
with open(file_path, 'r') as file:
content = file.read()
self.text_box.setPlainText(content)
except Exception as e:
self.text_box.setPlainText(f"Error reading file: {e}")
def get_content(self):
return self.text_box.toPlainText()
Result:
You can read more here:
https://www.faunalia.eu/en/blog/2019-07-02-custom-processing-widget
-
Hi Ben. thanks a lot. I tried but it does not work on my machine; I am using Windows OS I tested on version QGIS 3.34 and 3.36. I will try to check what is the issue first since it works well on your machine.GforGIS– GforGIS2024年11月22日 01:59:11 +00:00Commented Nov 22, 2024 at 1:59
-
@GforGIS, in what way does it not work? I have tested in 3.34 on Ubuntu & 3.28 on Windows and it works fine for me on both versions/OSs. How are you adding the script to the toolbox. You do know that you need to click the Python icon in the toolbar at the top of the Processing Toolbox, select Add Script to Toolbox... and navigate to your script
.py
file. Right? The tool will then be in Custom Scripts group under the Scripts provider.Ben W– Ben W2024年11月22日 02:10:50 +00:00Commented Nov 22, 2024 at 2:10 -
1I meant when I double clicking on the script in the Custom Scripts group, the dialog load but when clicking on the button to select the text file, it works fine, but it would not display the text in the text box.GforGIS– GforGIS2024年11月22日 03:51:10 +00:00Commented Nov 22, 2024 at 3:51
-
1I need to apologize; I tested it on a clean machine and it works perfectly. Thanks for clarifying and explaining the way to achieve this.GforGIS– GforGIS2024年11月22日 04:06:17 +00:00Commented Nov 22, 2024 at 4:06
-
1@GforGIS, no worries, all good. Glad it's working now! :-)Ben W– Ben W2024年11月22日 04:18:41 +00:00Commented Nov 22, 2024 at 4:18