Plugins/WizardPlugins/QRegularExpressionWizard/QRegularExpressionWizardDialog.py

changeset 2747
68b920f307ff
parent 2743
d092c33ecbcd
child 2748
3731148a7cdf
equal deleted inserted replaced
2746:96dc4bcb6dc0 2747:68b920f307ff
7 Module implementing the QRegularExpression wizard dialog. 7 Module implementing the QRegularExpression wizard dialog.
8 """ 8 """
9 9
10 import os 10 import os
11 import re 11 import re
12 12 import sys
13 from PyQt4.QtCore import QFileInfo, pyqtSlot, qVersion 13 import json
14 try: 14
15 from PyQt4.QtCore import QRegularExpression 15 from PyQt4.QtCore import QFileInfo, pyqtSlot, qVersion, QProcess, QByteArray
16 AVAILABLE = True
17 except ImportError:
18 AVAILABLE = False
19 from PyQt4.QtGui import QWidget, QDialog, QInputDialog, QApplication, QClipboard, \ 16 from PyQt4.QtGui import QWidget, QDialog, QInputDialog, QApplication, QClipboard, \
20 QTextCursor, QDialogButtonBox, QVBoxLayout, QTableWidgetItem 17 QTextCursor, QDialogButtonBox, QVBoxLayout, QTableWidgetItem
21 18
22 from E5Gui import E5MessageBox, E5FileDialog 19 from E5Gui import E5MessageBox, E5FileDialog
23 from E5Gui.E5MainWindow import E5MainWindow 20 from E5Gui.E5MainWindow import E5MainWindow
66 self.undoButton.setIcon(UI.PixmapCache.getIcon("editUndo.png")) 63 self.undoButton.setIcon(UI.PixmapCache.getIcon("editUndo.png"))
67 self.redoButton.setIcon(UI.PixmapCache.getIcon("editRedo.png")) 64 self.redoButton.setIcon(UI.PixmapCache.getIcon("editRedo.png"))
68 65
69 self.namedGroups = re.compile(r"""\(?P<([^>]+)>""").findall 66 self.namedGroups = re.compile(r"""\(?P<([^>]+)>""").findall
70 67
68 # start the PyQt5 server part
69 self.__pyqt5Available = False
70 self.__pyqt5Server = QProcess(self)
71 self.__pyqt5Server.start(sys.executable, [
72 os.path.join(os.path.dirname(__file__), "QRegularExpressionWizardServer.py")])
73 if self.__pyqt5Server.waitForStarted(10000):
74 self.__pyqt5Server.setReadChannel(QProcess.StandardOutput)
75 if self.__sendCommand("available"):
76 response = self.__receiveResponse()
77 if response and response["available"]:
78 self.__pyqt5Available = True
79
71 self.saveButton = \ 80 self.saveButton = \
72 self.buttonBox.addButton(self.trUtf8("Save"), QDialogButtonBox.ActionRole) 81 self.buttonBox.addButton(self.trUtf8("Save"), QDialogButtonBox.ActionRole)
73 self.saveButton.setToolTip(self.trUtf8("Save the regular expression to a file")) 82 self.saveButton.setToolTip(self.trUtf8("Save the regular expression to a file"))
74 self.loadButton = \ 83 self.loadButton = \
75 self.buttonBox.addButton(self.trUtf8("Load"), QDialogButtonBox.ActionRole) 84 self.buttonBox.addButton(self.trUtf8("Load"), QDialogButtonBox.ActionRole)
76 self.loadButton.setToolTip(self.trUtf8("Load a regular expression from a file")) 85 self.loadButton.setToolTip(self.trUtf8("Load a regular expression from a file"))
77 if qVersion() >= "5.0.0" and AVAILABLE: 86 if qVersion() >= "5.0.0" and self.__pyqt5Available:
78 self.validateButton = self.buttonBox.addButton( 87 self.validateButton = self.buttonBox.addButton(
79 self.trUtf8("Validate"), QDialogButtonBox.ActionRole) 88 self.trUtf8("Validate"), QDialogButtonBox.ActionRole)
80 self.validateButton.setToolTip(self.trUtf8("Validate the regular expression")) 89 self.validateButton.setToolTip(self.trUtf8("Validate the regular expression"))
81 self.executeButton = self.buttonBox.addButton( 90 self.executeButton = self.buttonBox.addButton(
82 self.trUtf8("Execute"), QDialogButtonBox.ActionRole) 91 self.trUtf8("Execute"), QDialogButtonBox.ActionRole)
104 self.variableLabel.hide() 113 self.variableLabel.hide()
105 self.variableLineEdit.hide() 114 self.variableLineEdit.hide()
106 self.variableLine.hide() 115 self.variableLine.hide()
107 self.importCheckBox.hide() 116 self.importCheckBox.hide()
108 self.regexpTextEdit.setFocus() 117 self.regexpTextEdit.setFocus()
109 118
119 def __sendCommand(self, command, **kw):
120 """
121 Private method to send a command to the PyQt5 server.
122
123 @param commandDict dictionary with command string and related data (dict)
124 @return flag indicating a successful transmission (boolean)
125 """
126 result = False
127 if command:
128 commandDict = {"command": command}
129 commandDict.update(kw)
130 commandStr = json.dumps(commandDict) + "\n"
131 data = QByteArray(commandStr.encode("utf-8"))
132 self.__pyqt5Server.write(data)
133 result = self.__pyqt5Server.waitForBytesWritten(10000)
134 return result
135
136 def __receiveResponse(self):
137 """
138 Private method to receive a response from the PyQt5 server.
139
140 @return response dictionary (dict)
141 """
142 responseDict = {}
143 if self.__pyqt5Server.waitForReadyRead(10000):
144 data = bytes(self.__pyqt5Server.readAllStandardOutput())
145 responseStr = data.decode("utf-8")
146 responseDict = json.loads(responseStr)
147 if responseDict["error"]:
148 E5MessageBox.critical(self,
149 self.trUtf8("Communication Error"),
150 self.trUtf8("""<p>The PyQt5 backend reported an error.</p>"""
151 """<p>{0}</p>""").format(responseDict["error"]))
152 responseDict = {}
153
154 return responseDict
155
156 def shutdown(self):
157 """
158 Public method to shut down the PyQt5 server part.
159 """
160 self.__sendCommand("exit")
161 self.__pyqt5Server.waitForFinished(5000)
162
110 def __insertString(self, s, steps=0): 163 def __insertString(self, s, steps=0):
111 """ 164 """
112 Private method to insert a string into line edit and move cursor. 165 Private method to insert a string into line edit and move cursor.
113 166
114 @param s string to be inserted into the regexp line edit 167 @param s string to be inserted into the regexp line edit
391 @pyqtSlot() 444 @pyqtSlot()
392 def on_validateButton_clicked(self): 445 def on_validateButton_clicked(self):
393 """ 446 """
394 Private slot to validate the entered QRegularExpression. 447 Private slot to validate the entered QRegularExpression.
395 """ 448 """
396 if qVersion() < "5.0.0" or not AVAILABLE: 449 if qVersion() < "5.0.0" or not self.__pyqt5Available:
397 # only available for Qt5 450 # only available for Qt5
398 return 451 return
399 452
400 regex = self.regexpTextEdit.toPlainText() 453 regexp = self.regexpTextEdit.toPlainText()
401 if regex: 454 if regexp:
402 options = QRegularExpression.NoPatternOption 455 options = []
403 if self.caseInsensitiveCheckBox.isChecked(): 456 if self.caseInsensitiveCheckBox.isChecked():
404 options |= QRegularExpression.CaseInsensitiveOption 457 options.append("CaseInsensitiveOption")
405 if self.multilineCheckBox.isChecked(): 458 if self.multilineCheckBox.isChecked():
406 options |= QRegularExpression.MultilineOption 459 options.append("MultilineOption")
407 if self.dotallCheckBox.isChecked(): 460 if self.dotallCheckBox.isChecked():
408 options |= QRegularExpression.DotMatchesEverythingOption 461 options.append("DotMatchesEverythingOption")
409 if self.extendedCheckBox.isChecked(): 462 if self.extendedCheckBox.isChecked():
410 options |= QRegularExpression.ExtendedPatternSyntaxOption 463 options.append("ExtendedPatternSyntaxOption")
411 if self.greedinessCheckBox.isChecked(): 464 if self.greedinessCheckBox.isChecked():
412 options |= QRegularExpression.InvertedGreedinessOption 465 options.append("InvertedGreedinessOption")
413 if self.unicodeCheckBox.isChecked(): 466 if self.unicodeCheckBox.isChecked():
414 options |= QRegularExpression.UseUnicodePropertiesOption 467 options.append("UseUnicodePropertiesOption")
415 if self.captureCheckBox.isChecked(): 468 if self.captureCheckBox.isChecked():
416 options |= QRegularExpression.DontCaptureOption 469 options.append("DontCaptureOption")
417 470
418 re = QRegularExpression(regex, options) 471 if self.__sendCommand("validate", options=options, regexp=regexp):
419 if re.isValid(): 472 response = self.__receiveResponse()
420 E5MessageBox.information(self, 473 if response and "valid" in response:
421 self.trUtf8("Validation"), 474 if response["valid"]:
422 self.trUtf8("""The regular expression is valid.""")) 475 E5MessageBox.information(self,
476 self.trUtf8("Validation"),
477 self.trUtf8("""The regular expression is valid."""))
478 else:
479 E5MessageBox.critical(self,
480 self.trUtf8("Error"),
481 self.trUtf8("""Invalid regular expression: {0}""")
482 .format(response["errorMessage"]))
483 # move cursor to error offset
484 offset = response["errorOffset"]
485 tc = self.regexpTextEdit.textCursor()
486 tc.setPosition(offset)
487 self.regexpTextEdit.setTextCursor(tc)
488 self.regexpTextEdit.setFocus()
489 return
490 else:
491 E5MessageBox.critical(self,
492 self.trUtf8("Communication Error"),
493 self.trUtf8("""Invalid response received from PyQt5 backend."""))
423 else: 494 else:
424 E5MessageBox.critical(self, 495 E5MessageBox.critical(self,
425 self.trUtf8("Error"), 496 self.trUtf8("Communication Error"),
426 self.trUtf8("""Invalid regular expression: {0}""") 497 self.trUtf8("""Communication with PyQt5 backend failed."""))
427 .format(re.errorString()))
428 # move cursor to error offset
429 offset = re.errorPatternOffset()
430 tc = self.regexpTextEdit.textCursor()
431 tc.setPosition(offset)
432 self.regexpTextEdit.setTextCursor(tc)
433 return
434 else: 498 else:
435 E5MessageBox.critical(self, 499 E5MessageBox.critical(self,
436 self.trUtf8("Error"), 500 self.trUtf8("Error"),
437 self.trUtf8("""A regular expression must be given.""")) 501 self.trUtf8("""A regular expression must be given."""))
438 502
444 This slot will execute the entered QRegularExpression on the entered test 508 This slot will execute the entered QRegularExpression on the entered test
445 data and will display the result in the table part of the dialog. 509 data and will display the result in the table part of the dialog.
446 510
447 @param startpos starting position for the QRegularExpression matching 511 @param startpos starting position for the QRegularExpression matching
448 """ 512 """
449 if qVersion() < "5.0.0" or not AVAILABLE: 513 if qVersion() < "5.0.0" or not self.__pyqt5Available:
450 # only available for Qt5 514 # only available for Qt5
451 return 515 return
452 516
453 regex = self.regexpTextEdit.toPlainText() 517 regex = self.regexpTextEdit.toPlainText()
454 text = self.textTextEdit.toPlainText() 518 text = self.textTextEdit.toPlainText()
662 @param indLevel indentation level (int) 726 @param indLevel indentation level (int)
663 @param indString string used for indentation (space or tab) (string) 727 @param indString string used for indentation (space or tab) (string)
664 @return generated code (string) 728 @return generated code (string)
665 """ 729 """
666 return self.cw.getCode(indLevel, indString) 730 return self.cw.getCode(indLevel, indString)
731
732 def accept(self):
733 """
734 Public slot to hide the dialog and set the result code to Accepted.
735 """
736 self.cw.shutdown()
737 super().accept()
738
739 def reject(self):
740 """
741 Public slot to hide the dialog and set the result code to Rejected.
742 """
743 self.cw.shutdown()
744 super().reject()
667 745
668 746
669 class QRegularExpressionWizardWindow(E5MainWindow): 747 class QRegularExpressionWizardWindow(E5MainWindow):
670 """ 748 """
671 Main window class for the standalone dialog. 749 Main window class for the standalone dialog.
685 763
686 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) 764 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet"))
687 765
688 self.cw.buttonBox.accepted[()].connect(self.close) 766 self.cw.buttonBox.accepted[()].connect(self.close)
689 self.cw.buttonBox.rejected[()].connect(self.close) 767 self.cw.buttonBox.rejected[()].connect(self.close)
768
769 def closeEvent(self, evt):
770 """
771 Protected method handling the close event.
772
773 @param evt close event (QCloseEvent)
774 """
775 self.cw.shutdown()
776 super().closeEvent(evt)

eric ide

mercurial