eric7/Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardDialog.py

branch
eric7
changeset 8312
800c432b34c8
parent 8218
7c09585bd960
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2013 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the QRegularExpression wizard dialog.
8 """
9
10 import os
11 import re
12 import sys
13 import json
14
15 from PyQt5.QtCore import QFileInfo, pyqtSlot, QProcess, QByteArray
16 from PyQt5.QtGui import QClipboard, QTextCursor
17 from PyQt5.QtWidgets import (
18 QWidget, QDialog, QInputDialog, QApplication, QDialogButtonBox,
19 QVBoxLayout, QTableWidgetItem
20 )
21
22 from E5Gui import E5MessageBox, E5FileDialog
23 from E5Gui.E5MainWindow import E5MainWindow
24
25 from .Ui_QRegularExpressionWizardDialog import (
26 Ui_QRegularExpressionWizardDialog
27 )
28
29 import UI.PixmapCache
30
31 import Utilities
32 import Preferences
33
34
35 class QRegularExpressionWizardWidget(QWidget,
36 Ui_QRegularExpressionWizardDialog):
37 """
38 Class implementing the QRegularExpression wizard dialog.
39 """
40 def __init__(self, parent=None, fromEric=True):
41 """
42 Constructor
43
44 @param parent parent widget (QWidget)
45 @param fromEric flag indicating a call from within eric
46 """
47 super().__init__(parent)
48 self.setupUi(self)
49
50 # initialize icons of the tool buttons
51 self.commentButton.setIcon(UI.PixmapCache.getIcon("comment"))
52 self.charButton.setIcon(UI.PixmapCache.getIcon("characters"))
53 self.anycharButton.setIcon(UI.PixmapCache.getIcon("anychar"))
54 self.repeatButton.setIcon(UI.PixmapCache.getIcon("repeat"))
55 self.nonGroupButton.setIcon(UI.PixmapCache.getIcon("nongroup"))
56 self.atomicGroupButton.setIcon(
57 UI.PixmapCache.getIcon("atomicgroup"))
58 self.groupButton.setIcon(UI.PixmapCache.getIcon("group"))
59 self.namedGroupButton.setIcon(UI.PixmapCache.getIcon("namedgroup"))
60 self.namedReferenceButton.setIcon(
61 UI.PixmapCache.getIcon("namedreference"))
62 self.altnButton.setIcon(UI.PixmapCache.getIcon("altn"))
63 self.beglineButton.setIcon(UI.PixmapCache.getIcon("begline"))
64 self.endlineButton.setIcon(UI.PixmapCache.getIcon("endline"))
65 self.wordboundButton.setIcon(
66 UI.PixmapCache.getIcon("wordboundary"))
67 self.nonwordboundButton.setIcon(
68 UI.PixmapCache.getIcon("nonwordboundary"))
69 self.poslookaheadButton.setIcon(
70 UI.PixmapCache.getIcon("poslookahead"))
71 self.neglookaheadButton.setIcon(
72 UI.PixmapCache.getIcon("neglookahead"))
73 self.poslookbehindButton.setIcon(
74 UI.PixmapCache.getIcon("poslookbehind"))
75 self.neglookbehindButton.setIcon(
76 UI.PixmapCache.getIcon("neglookbehind"))
77 self.undoButton.setIcon(UI.PixmapCache.getIcon("editUndo"))
78 self.redoButton.setIcon(UI.PixmapCache.getIcon("editRedo"))
79
80 self.namedGroups = re.compile(r"""\(?P<([^>]+)>""").findall
81
82 # start the PyQt5 server part
83 self.__pyqt5Available = False
84 self.__pyqt5Server = QProcess(self)
85 self.__pyqt5Server.start(
86 sys.executable, [os.path.join(
87 os.path.dirname(__file__), "QRegularExpressionWizardServer.py")
88 ])
89 if self.__pyqt5Server.waitForStarted(5000):
90 self.__pyqt5Server.setReadChannel(
91 QProcess.ProcessChannel.StandardOutput)
92 if self.__sendCommand("available"):
93 response = self.__receiveResponse()
94 if response and response["available"]:
95 self.__pyqt5Available = True
96
97 self.saveButton = self.buttonBox.addButton(
98 self.tr("Save"), QDialogButtonBox.ButtonRole.ActionRole)
99 self.saveButton.setToolTip(
100 self.tr("Save the regular expression to a file"))
101 self.loadButton = self.buttonBox.addButton(
102 self.tr("Load"), QDialogButtonBox.ButtonRole.ActionRole)
103 self.loadButton.setToolTip(
104 self.tr("Load a regular expression from a file"))
105 if self.__pyqt5Available:
106 self.validateButton = self.buttonBox.addButton(
107 self.tr("Validate"), QDialogButtonBox.ButtonRole.ActionRole)
108 self.validateButton.setToolTip(
109 self.tr("Validate the regular expression"))
110 self.executeButton = self.buttonBox.addButton(
111 self.tr("Execute"), QDialogButtonBox.ButtonRole.ActionRole)
112 self.executeButton.setToolTip(
113 self.tr("Execute the regular expression"))
114 self.nextButton = self.buttonBox.addButton(
115 self.tr("Next match"), QDialogButtonBox.ButtonRole.ActionRole)
116 self.nextButton.setToolTip(
117 self.tr("Show the next match of the regular expression"))
118 self.nextButton.setEnabled(False)
119 else:
120 self.validateButton = None
121 self.executeButton = None
122 self.nextButton = None
123
124 if fromEric:
125 self.buttonBox.setStandardButtons(
126 QDialogButtonBox.StandardButton.Cancel |
127 QDialogButtonBox.StandardButton.Ok)
128 self.copyButton = None
129 else:
130 self.copyButton = self.buttonBox.addButton(
131 self.tr("Copy"), QDialogButtonBox.ButtonRole.ActionRole)
132 self.copyButton.setToolTip(
133 self.tr("Copy the regular expression to the clipboard"))
134 self.buttonBox.setStandardButtons(
135 QDialogButtonBox.StandardButton.Close)
136 self.variableLabel.hide()
137 self.variableLineEdit.hide()
138 self.variableLine.hide()
139 self.regexpTextEdit.setFocus()
140
141 def __sendCommand(self, command, **kw):
142 """
143 Private method to send a command to the PyQt5 server.
144
145 @param command dictionary with command string and related
146 data (dict)
147 @keyparam kw parameters for the command
148 @return flag indicating a successful transmission (boolean)
149 """
150 result = False
151 if command:
152 commandDict = {"command": command}
153 commandDict.update(kw)
154 commandStr = json.dumps(commandDict) + "\n"
155 data = QByteArray(commandStr.encode("utf-8"))
156 self.__pyqt5Server.write(data)
157 result = self.__pyqt5Server.waitForBytesWritten(10000)
158 return result
159
160 def __receiveResponse(self):
161 """
162 Private method to receive a response from the PyQt5 server.
163
164 @return response dictionary (dict)
165 """
166 responseDict = {}
167 if self.__pyqt5Server.waitForReadyRead(10000):
168 data = bytes(self.__pyqt5Server.readAllStandardOutput())
169 responseStr = data.decode("utf-8")
170 responseDict = json.loads(responseStr)
171 if responseDict["error"]:
172 E5MessageBox.critical(
173 self,
174 self.tr("Communication Error"),
175 self.tr("""<p>The PyQt5 backend reported"""
176 """ an error.</p><p>{0}</p>""")
177 .format(responseDict["error"]))
178 responseDict = {}
179
180 return responseDict
181
182 def shutdown(self):
183 """
184 Public method to shut down the PyQt5 server part.
185 """
186 self.__sendCommand("exit")
187 self.__pyqt5Server.waitForFinished(5000)
188
189 def __insertString(self, s, steps=0):
190 """
191 Private method to insert a string into line edit and move cursor.
192
193 @param s string to be inserted into the regexp line edit
194 (string)
195 @param steps number of characters to move the cursor (integer).
196 Negative steps moves cursor back, positives forward.
197 """
198 self.regexpTextEdit.insertPlainText(s)
199 tc = self.regexpTextEdit.textCursor()
200 if steps != 0:
201 if steps < 0:
202 act = QTextCursor.MoveOperation.Left
203 steps = abs(steps)
204 else:
205 act = QTextCursor.MoveOperation.Right
206 for _ in range(steps):
207 tc.movePosition(act)
208 self.regexpTextEdit.setTextCursor(tc)
209
210 @pyqtSlot()
211 def on_commentButton_clicked(self):
212 """
213 Private slot to handle the comment toolbutton.
214 """
215 self.__insertString("(?#)", -1)
216
217 @pyqtSlot()
218 def on_charButton_clicked(self):
219 """
220 Private slot to handle the characters toolbutton.
221 """
222 from .QRegularExpressionWizardCharactersDialog import (
223 QRegularExpressionWizardCharactersDialog
224 )
225 dlg = QRegularExpressionWizardCharactersDialog(self)
226 if dlg.exec() == QDialog.DialogCode.Accepted:
227 self.__insertString(dlg.getCharacters())
228
229 @pyqtSlot()
230 def on_anycharButton_clicked(self):
231 """
232 Private slot to handle the any character toolbutton.
233 """
234 self.__insertString(".")
235
236 @pyqtSlot()
237 def on_repeatButton_clicked(self):
238 """
239 Private slot to handle the repeat toolbutton.
240 """
241 from .QRegularExpressionWizardRepeatDialog import (
242 QRegularExpressionWizardRepeatDialog
243 )
244 dlg = QRegularExpressionWizardRepeatDialog(self)
245 if dlg.exec() == QDialog.DialogCode.Accepted:
246 self.__insertString(dlg.getRepeat())
247
248 @pyqtSlot()
249 def on_nonGroupButton_clicked(self):
250 """
251 Private slot to handle the non group toolbutton.
252 """
253 self.__insertString("(?:)", -1)
254
255 @pyqtSlot()
256 def on_atomicGroupButton_clicked(self):
257 """
258 Private slot to handle the atomic non group toolbutton.
259 """
260 self.__insertString("(?>)", -1)
261
262 @pyqtSlot()
263 def on_groupButton_clicked(self):
264 """
265 Private slot to handle the group toolbutton.
266 """
267 self.__insertString("()", -1)
268
269 @pyqtSlot()
270 def on_namedGroupButton_clicked(self):
271 """
272 Private slot to handle the named group toolbutton.
273 """
274 self.__insertString("(?P<>)", -2)
275
276 @pyqtSlot()
277 def on_namedReferenceButton_clicked(self):
278 """
279 Private slot to handle the named reference toolbutton.
280 """
281 # determine cursor position as length into text
282 length = self.regexpTextEdit.textCursor().position()
283
284 # only present group names that occur before the current
285 # cursor position
286 regex = self.regexpTextEdit.toPlainText()[:length]
287 names = self.namedGroups(regex)
288 if not names:
289 E5MessageBox.information(
290 self,
291 self.tr("Named reference"),
292 self.tr("""No named groups have been defined yet."""))
293 return
294
295 groupName, ok = QInputDialog.getItem(
296 self,
297 self.tr("Named reference"),
298 self.tr("Select group name:"),
299 names,
300 0, True)
301 if ok and groupName:
302 self.__insertString("(?P={0})".format(groupName))
303
304 @pyqtSlot()
305 def on_altnButton_clicked(self):
306 """
307 Private slot to handle the alternatives toolbutton.
308 """
309 self.__insertString("(|)", -2)
310
311 @pyqtSlot()
312 def on_beglineButton_clicked(self):
313 """
314 Private slot to handle the begin line toolbutton.
315 """
316 self.__insertString("^")
317
318 @pyqtSlot()
319 def on_endlineButton_clicked(self):
320 """
321 Private slot to handle the end line toolbutton.
322 """
323 self.__insertString("$")
324
325 @pyqtSlot()
326 def on_wordboundButton_clicked(self):
327 """
328 Private slot to handle the word boundary toolbutton.
329 """
330 self.__insertString("\\b")
331
332 @pyqtSlot()
333 def on_nonwordboundButton_clicked(self):
334 """
335 Private slot to handle the non word boundary toolbutton.
336 """
337 self.__insertString("\\B")
338
339 @pyqtSlot()
340 def on_poslookaheadButton_clicked(self):
341 """
342 Private slot to handle the positive lookahead toolbutton.
343 """
344 self.__insertString("(?=)", -1)
345
346 @pyqtSlot()
347 def on_neglookaheadButton_clicked(self):
348 """
349 Private slot to handle the negative lookahead toolbutton.
350 """
351 self.__insertString("(?!)", -1)
352
353 @pyqtSlot()
354 def on_poslookbehindButton_clicked(self):
355 """
356 Private slot to handle the positive lookbehind toolbutton.
357 """
358 self.__insertString("(?<=)", -1)
359
360 @pyqtSlot()
361 def on_neglookbehindButton_clicked(self):
362 """
363 Private slot to handle the negative lookbehind toolbutton.
364 """
365 self.__insertString("(?<!)", -1)
366
367 @pyqtSlot()
368 def on_undoButton_clicked(self):
369 """
370 Private slot to handle the undo action.
371 """
372 self.regexpTextEdit.document().undo()
373
374 @pyqtSlot()
375 def on_redoButton_clicked(self):
376 """
377 Private slot to handle the redo action.
378 """
379 self.regexpTextEdit.document().redo()
380
381 def on_buttonBox_clicked(self, button):
382 """
383 Private slot called by a button of the button box clicked.
384
385 @param button button that was clicked (QAbstractButton)
386 """
387 if button == self.validateButton:
388 self.on_validateButton_clicked()
389 elif button == self.executeButton:
390 self.on_executeButton_clicked()
391 elif button == self.saveButton:
392 self.on_saveButton_clicked()
393 elif button == self.loadButton:
394 self.on_loadButton_clicked()
395 elif button == self.nextButton:
396 self.on_nextButton_clicked()
397 elif self.copyButton and button == self.copyButton:
398 self.on_copyButton_clicked()
399
400 @pyqtSlot()
401 def on_saveButton_clicked(self):
402 """
403 Private slot to save the QRegularExpression to a file.
404 """
405 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
406 self,
407 self.tr("Save regular expression"),
408 "",
409 self.tr("RegExp Files (*.rx);;All Files (*)"),
410 None,
411 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
412 if fname:
413 ext = QFileInfo(fname).suffix()
414 if not ext:
415 ex = selectedFilter.split("(*")[1].split(")")[0]
416 if ex:
417 fname += ex
418 if QFileInfo(fname).exists():
419 res = E5MessageBox.yesNo(
420 self,
421 self.tr("Save regular expression"),
422 self.tr("<p>The file <b>{0}</b> already exists."
423 " Overwrite it?</p>").format(fname),
424 icon=E5MessageBox.Warning)
425 if not res:
426 return
427
428 fname = Utilities.toNativeSeparators(fname)
429 try:
430 with open(fname, "w", encoding="utf-8") as f:
431 f.write(self.regexpTextEdit.toPlainText())
432 except OSError as err:
433 E5MessageBox.information(
434 self,
435 self.tr("Save regular expression"),
436 self.tr("""<p>The regular expression could not"""
437 """ be saved.</p><p>Reason: {0}</p>""")
438 .format(str(err)))
439
440 @pyqtSlot()
441 def on_loadButton_clicked(self):
442 """
443 Private slot to load a QRegularExpression from a file.
444 """
445 fname = E5FileDialog.getOpenFileName(
446 self,
447 self.tr("Load regular expression"),
448 "",
449 self.tr("RegExp Files (*.rx);;All Files (*)"))
450 if fname:
451 fname = Utilities.toNativeSeparators(fname)
452 try:
453 with open(fname, "r", encoding="utf-8") as f:
454 regexp = f.read()
455 self.regexpTextEdit.setPlainText(regexp)
456 except OSError as err:
457 E5MessageBox.information(
458 self,
459 self.tr("Save regular expression"),
460 self.tr("""<p>The regular expression could not"""
461 """ be saved.</p><p>Reason: {0}</p>""")
462 .format(str(err)))
463
464 @pyqtSlot()
465 def on_copyButton_clicked(self):
466 """
467 Private slot to copy the QRegularExpression string into the clipboard.
468
469 This slot is only available, if not called from within eric.
470 """
471 escaped = self.regexpTextEdit.toPlainText()
472 if escaped:
473 escaped = escaped.replace("\\", "\\\\")
474 cb = QApplication.clipboard()
475 cb.setText(escaped, QClipboard.Mode.Clipboard)
476 if cb.supportsSelection():
477 cb.setText(escaped, QClipboard.Mode.Selection)
478
479 @pyqtSlot()
480 def on_validateButton_clicked(self):
481 """
482 Private slot to validate the entered QRegularExpression.
483 """
484 if not self.__pyqt5Available:
485 # only available for PyQt5
486 return
487
488 regexp = self.regexpTextEdit.toPlainText()
489 if regexp:
490 options = []
491 if self.caseInsensitiveCheckBox.isChecked():
492 options.append("CaseInsensitiveOption")
493 if self.multilineCheckBox.isChecked():
494 options.append("MultilineOption")
495 if self.dotallCheckBox.isChecked():
496 options.append("DotMatchesEverythingOption")
497 if self.extendedCheckBox.isChecked():
498 options.append("ExtendedPatternSyntaxOption")
499 if self.greedinessCheckBox.isChecked():
500 options.append("InvertedGreedinessOption")
501 if self.unicodeCheckBox.isChecked():
502 options.append("UseUnicodePropertiesOption")
503 if self.captureCheckBox.isChecked():
504 options.append("DontCaptureOption")
505
506 if self.__sendCommand("validate", options=options, regexp=regexp):
507 response = self.__receiveResponse()
508 if response and "valid" in response:
509 if response["valid"]:
510 E5MessageBox.information(
511 self,
512 self.tr("Validation"),
513 self.tr(
514 """The regular expression is valid."""))
515 else:
516 E5MessageBox.critical(
517 self,
518 self.tr("Error"),
519 self.tr("""Invalid regular expression: {0}""")
520 .format(response["errorMessage"]))
521 # move cursor to error offset
522 offset = response["errorOffset"]
523 tc = self.regexpTextEdit.textCursor()
524 tc.setPosition(offset)
525 self.regexpTextEdit.setTextCursor(tc)
526 self.regexpTextEdit.setFocus()
527 return
528 else:
529 E5MessageBox.critical(
530 self,
531 self.tr("Communication Error"),
532 self.tr("""Invalid response received from"""
533 """ PyQt5 backend."""))
534 else:
535 E5MessageBox.critical(
536 self,
537 self.tr("Communication Error"),
538 self.tr("""Communication with PyQt5 backend"""
539 """ failed."""))
540 else:
541 E5MessageBox.critical(
542 self,
543 self.tr("Error"),
544 self.tr("""A regular expression must be given."""))
545
546 @pyqtSlot()
547 def on_executeButton_clicked(self, startpos=0):
548 """
549 Private slot to execute the entered QRegularExpression on the test
550 text.
551
552 This slot will execute the entered QRegularExpression on the entered
553 test data and will display the result in the table part of the dialog.
554
555 @param startpos starting position for the QRegularExpression matching
556 """
557 if not self.__pyqt5Available:
558 # only available for PyQt5
559 return
560
561 regexp = self.regexpTextEdit.toPlainText()
562 text = self.textTextEdit.toPlainText()
563 if regexp and text:
564 options = []
565 if self.caseInsensitiveCheckBox.isChecked():
566 options.append("CaseInsensitiveOption")
567 if self.multilineCheckBox.isChecked():
568 options.append("MultilineOption")
569 if self.dotallCheckBox.isChecked():
570 options.append("DotMatchesEverythingOption")
571 if self.extendedCheckBox.isChecked():
572 options.append("ExtendedPatternSyntaxOption")
573 if self.greedinessCheckBox.isChecked():
574 options.append("InvertedGreedinessOption")
575 if self.unicodeCheckBox.isChecked():
576 options.append("UseUnicodePropertiesOption")
577 if self.captureCheckBox.isChecked():
578 options.append("DontCaptureOption")
579
580 if self.__sendCommand("execute", options=options, regexp=regexp,
581 text=text, startpos=startpos):
582 response = self.__receiveResponse()
583 if response and ("valid" in response or "matched" in response):
584 if "valid" in response:
585 E5MessageBox.critical(
586 self,
587 self.tr("Error"),
588 self.tr("""Invalid regular expression: {0}""")
589 .format(response["errorMessage"]))
590 # move cursor to error offset
591 offset = response["errorOffset"]
592 tc = self.regexpTextEdit.textCursor()
593 tc.setPosition(offset)
594 self.regexpTextEdit.setTextCursor(tc)
595 self.regexpTextEdit.setFocus()
596 return
597 else:
598 row = 0
599 OFFSET = 5
600
601 self.resultTable.setColumnCount(0)
602 self.resultTable.setColumnCount(3)
603 self.resultTable.setRowCount(0)
604 self.resultTable.setRowCount(OFFSET)
605 self.resultTable.setItem(
606 row, 0, QTableWidgetItem(self.tr("Regexp")))
607 self.resultTable.setItem(
608 row, 1, QTableWidgetItem(regexp))
609 if response["matched"]:
610 captures = response["captures"]
611 # index 0 is the complete match
612 offset = captures[0][1]
613 self.lastMatchEnd = captures[0][2]
614 self.nextButton.setEnabled(True)
615 row += 1
616 self.resultTable.setItem(
617 row, 0,
618 QTableWidgetItem(self.tr("Offset")))
619 self.resultTable.setItem(
620 row, 1,
621 QTableWidgetItem("{0:d}".format(offset)))
622
623 row += 1
624 self.resultTable.setItem(
625 row, 0,
626 QTableWidgetItem(self.tr("Captures")))
627 self.resultTable.setItem(
628 row, 1,
629 QTableWidgetItem(
630 "{0:d}".format(len(captures) - 1)))
631 row += 1
632 self.resultTable.setItem(
633 row, 1,
634 QTableWidgetItem(self.tr("Text")))
635 self.resultTable.setItem(
636 row, 2,
637 QTableWidgetItem(self.tr("Characters")))
638
639 row += 1
640 self.resultTable.setItem(
641 row, 0,
642 QTableWidgetItem(self.tr("Match")))
643 self.resultTable.setItem(
644 row, 1,
645 QTableWidgetItem(captures[0][0]))
646 self.resultTable.setItem(
647 row, 2,
648 QTableWidgetItem(
649 "{0:d}".format(captures[0][3])))
650
651 for i in range(1, len(captures)):
652 if captures[i][0]:
653 row += 1
654 self.resultTable.insertRow(row)
655 self.resultTable.setItem(
656 row, 0,
657 QTableWidgetItem(
658 self.tr("Capture #{0}")
659 .format(i)))
660 self.resultTable.setItem(
661 row, 1,
662 QTableWidgetItem(captures[i][0]))
663 self.resultTable.setItem(
664 row, 2,
665 QTableWidgetItem(
666 "{0:d}".format(captures[i][3])))
667
668 # highlight the matched text
669 tc = self.textTextEdit.textCursor()
670 tc.setPosition(offset)
671 tc.setPosition(
672 self.lastMatchEnd,
673 QTextCursor.MoveMode.KeepAnchor)
674 self.textTextEdit.setTextCursor(tc)
675 else:
676 self.nextButton.setEnabled(False)
677 self.resultTable.setRowCount(2)
678 row += 1
679 if startpos > 0:
680 self.resultTable.setItem(
681 row, 0,
682 QTableWidgetItem(
683 self.tr("No more matches")))
684 else:
685 self.resultTable.setItem(
686 row, 0,
687 QTableWidgetItem(
688 self.tr("No matches")))
689
690 # remove the highlight
691 tc = self.textTextEdit.textCursor()
692 tc.setPosition(0)
693 self.textTextEdit.setTextCursor(tc)
694
695 self.resultTable.resizeColumnsToContents()
696 self.resultTable.resizeRowsToContents()
697 self.resultTable.verticalHeader().hide()
698 self.resultTable.horizontalHeader().hide()
699 else:
700 E5MessageBox.critical(
701 self,
702 self.tr("Communication Error"),
703 self.tr("""Invalid response received from"""
704 """ PyQt5 backend."""))
705 else:
706 E5MessageBox.critical(
707 self,
708 self.tr("Communication Error"),
709 self.tr("""Communication with PyQt5"""
710 """ backend failed."""))
711 else:
712 E5MessageBox.critical(
713 self,
714 self.tr("Error"),
715 self.tr("""A regular expression and a text must"""
716 """ be given."""))
717
718 @pyqtSlot()
719 def on_nextButton_clicked(self):
720 """
721 Private slot to find the next match.
722 """
723 self.on_executeButton_clicked(self.lastMatchEnd)
724
725 @pyqtSlot()
726 def on_regexpTextEdit_textChanged(self):
727 """
728 Private slot called when the regexp changes.
729 """
730 if self.nextButton:
731 self.nextButton.setEnabled(False)
732
733 def getCode(self, indLevel, indString):
734 """
735 Public method to get the source code.
736
737 @param indLevel indentation level (int)
738 @param indString string used for indentation (space or tab) (string)
739 @return generated code (string)
740 """
741 # calculate the indentation string
742 i1string = (indLevel + 1) * indString
743 estring = os.linesep + indLevel * indString
744
745 # now generate the code
746 reVar = self.variableLineEdit.text()
747 if not reVar:
748 reVar = "regexp"
749
750 regexp = self.regexpTextEdit.toPlainText()
751
752 options = []
753 if self.caseInsensitiveCheckBox.isChecked():
754 options.append(
755 "QRegularExpression.PatternOption.CaseInsensitiveOption")
756 if self.multilineCheckBox.isChecked():
757 options.append(
758 "QRegularExpression.PatternOption.MultilineOption")
759 if self.dotallCheckBox.isChecked():
760 options.append(
761 "QRegularExpression.PatternOption.DotMatchesEverythingOption")
762 if self.extendedCheckBox.isChecked():
763 options.append(
764 "QRegularExpression.PatternOption.ExtendedPatternSyntaxOption")
765 if self.greedinessCheckBox.isChecked():
766 options.append(
767 "QRegularExpression.PatternOption.InvertedGreedinessOption")
768 if self.unicodeCheckBox.isChecked():
769 options.append(
770 "QRegularExpression.PatternOption.UseUnicodePropertiesOption")
771 if self.captureCheckBox.isChecked():
772 options.append(
773 "QRegularExpression.PatternOption.DontCaptureOption")
774 options = " |{0}{1}".format(os.linesep, i1string).join(options)
775
776 code = '{0} = QRegularExpression('.format(reVar)
777 if options:
778 code += '{0}{1}r"""{2}""",'.format(
779 os.linesep, i1string, regexp.replace('"', '\\"'))
780 code += '{0}{1}{2}'.format(os.linesep, i1string, options)
781 else:
782 code += 'r"""{0}"""'.format(regexp.replace('"', '\\"'))
783 code += '){0}'.format(estring)
784 return code
785
786
787 class QRegularExpressionWizardDialog(QDialog):
788 """
789 Class for the dialog variant.
790 """
791 def __init__(self, parent=None, fromEric=True):
792 """
793 Constructor
794
795 @param parent parent widget (QWidget)
796 @param fromEric flag indicating a call from within eric
797 """
798 super().__init__(parent)
799 self.setModal(fromEric)
800 self.setSizeGripEnabled(True)
801
802 self.__layout = QVBoxLayout(self)
803 self.__layout.setContentsMargins(0, 0, 0, 0)
804 self.setLayout(self.__layout)
805
806 self.cw = QRegularExpressionWizardWidget(self, fromEric)
807 size = self.cw.size()
808 self.__layout.addWidget(self.cw)
809 self.resize(size)
810 self.setWindowTitle(self.cw.windowTitle())
811
812 self.cw.buttonBox.accepted.connect(self.accept)
813 self.cw.buttonBox.rejected.connect(self.reject)
814
815 def getCode(self, indLevel, indString):
816 """
817 Public method to get the source code.
818
819 @param indLevel indentation level (int)
820 @param indString string used for indentation (space or tab) (string)
821 @return generated code (string)
822 """
823 return self.cw.getCode(indLevel, indString)
824
825 def accept(self):
826 """
827 Public slot to hide the dialog and set the result code to Accepted.
828 """
829 self.cw.shutdown()
830 super().accept()
831
832 def reject(self):
833 """
834 Public slot to hide the dialog and set the result code to Rejected.
835 """
836 self.cw.shutdown()
837 super().reject()
838
839
840 class QRegularExpressionWizardWindow(E5MainWindow):
841 """
842 Main window class for the standalone dialog.
843 """
844 def __init__(self, parent=None):
845 """
846 Constructor
847
848 @param parent reference to the parent widget (QWidget)
849 """
850 super().__init__(parent)
851 self.cw = QRegularExpressionWizardWidget(self, fromEric=False)
852 size = self.cw.size()
853 self.setCentralWidget(self.cw)
854 self.resize(size)
855 self.setWindowTitle(self.cw.windowTitle())
856
857 self.setStyle(
858 Preferences.getUI("Style"), Preferences.getUI("StyleSheet"))
859
860 self.cw.buttonBox.accepted.connect(self.close)
861 self.cw.buttonBox.rejected.connect(self.close)
862
863 def closeEvent(self, evt):
864 """
865 Protected method handling the close event.
866
867 @param evt close event (QCloseEvent)
868 """
869 self.cw.shutdown()
870 super().closeEvent(evt)

eric ide

mercurial