eric6/Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardDialog.py

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

eric ide

mercurial