1

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}
PolyGeo
65.5k29 gold badges115 silver badges350 bronze badges
asked Nov 21, 2024 at 9:09

1 Answer 1

2

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:

enter image description here

You can read more here:

https://www.faunalia.eu/en/blog/2019-07-02-custom-processing-widget

answered Nov 21, 2024 at 11:22
5
  • 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. Commented 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. Commented Nov 22, 2024 at 2:10
  • 1
    I 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. Commented Nov 22, 2024 at 3:51
  • 1
    I need to apologize; I tested it on a clean machine and it works perfectly. Thanks for clarifying and explaining the way to achieve this. Commented Nov 22, 2024 at 4:06
  • 1
    @GforGIS, no worries, all good. Glad it's working now! :-) Commented Nov 22, 2024 at 4:18

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.