PipInterface/PipPackagesWidget.py

branch
pypi
changeset 6798
3985c1a67fa2
parent 6795
6e2ed2aac325
child 6801
df623ed5aaa6
equal deleted inserted replaced
6795:6e2ed2aac325 6798:3985c1a67fa2
27 import UI.PixmapCache 27 import UI.PixmapCache
28 28
29 from .Pip import Pip 29 from .Pip import Pip
30 30
31 31
32 # TODO: use icons for action buttons
33 # TODO: show package details for installed packages
32 class PipPackagesWidget(QWidget, Ui_PipPackagesWidget): 34 class PipPackagesWidget(QWidget, Ui_PipPackagesWidget):
33 """ 35 """
34 Class implementing the pip packages management widget. 36 Class implementing the pip packages management widget.
35 """ 37 """
36 ShowProcessGeneralMode = 0 38 ShowProcessGeneralMode = 0
109 self.__updateActionButtons() 111 self.__updateActionButtons()
110 112
111 self.statusLabel.hide() 113 self.statusLabel.hide()
112 self.searchWidget.hide() 114 self.searchWidget.hide()
113 115
114 self.__detailsData = {} 116 self.__queryName = []
115 self.__query = [] 117 self.__querySummary = []
116 118
117 self.__packageDetailsDialog = None 119 self.__packageDetailsDialog = None
118 120
119 def __populateEnvironments(self): 121 def __populateEnvironments(self):
120 """ 122 """
469 def __updateSearchButton(self): 471 def __updateSearchButton(self):
470 """ 472 """
471 Private method to update the state of the search button. 473 Private method to update the state of the search button.
472 """ 474 """
473 self.searchButton.setEnabled( 475 self.searchButton.setEnabled(
474 bool(self.searchEdit.text()) and 476 (bool(self.searchEditName.text()) or
477 bool(self.searchEditSummary.text())) and
475 self.__isPipAvailable() 478 self.__isPipAvailable()
476 ) 479 )
477 480
478 @pyqtSlot(bool) 481 @pyqtSlot(bool)
479 def on_searchToggleButton_toggled(self, checked): 482 def on_searchToggleButton_toggled(self, checked):
484 @type bool 487 @type bool
485 """ 488 """
486 self.searchWidget.setVisible(checked) 489 self.searchWidget.setVisible(checked)
487 490
488 if checked: 491 if checked:
489 self.searchEdit.setFocus(Qt.OtherFocusReason) 492 self.searchEditName.setFocus(Qt.OtherFocusReason)
490 self.searchEdit.selectAll() 493 self.searchEditName.selectAll()
491 494
492 self.__updateSearchActionButtons() 495 self.__updateSearchActionButtons()
493 self.__updateSearchButton() 496 self.__updateSearchButton()
494 497
495 @pyqtSlot(str) 498 @pyqtSlot(str)
496 def on_searchEdit_textChanged(self, txt): 499 def on_searchEditName_textChanged(self, txt):
497 """ 500 """
498 Private slot handling a change of the search term. 501 Private slot handling a change of the search term.
499 502
500 @param txt search term 503 @param txt search term
501 @type str 504 @type str
502 """ 505 """
503 self.__updateSearchButton() 506 self.__updateSearchButton()
504 507
505 @pyqtSlot() 508 @pyqtSlot()
506 def on_searchEdit_returnPressed(self): 509 def on_searchEditName_returnPressed(self):
510 """
511 Private slot initiating a search via a press of the Return key.
512 """
513 self.__search()
514
515 @pyqtSlot(str)
516 def on_searchEditSummary_textChanged(self, txt):
517 """
518 Private slot handling a change of the search term.
519
520 @param txt search term
521 @type str
522 """
523 self.__updateSearchButton()
524
525 @pyqtSlot()
526 def on_searchEditSummary_returnPressed(self):
507 """ 527 """
508 Private slot initiating a search via a press of the Return key. 528 Private slot initiating a search via a press of the Return key.
509 """ 529 """
510 self.__search() 530 self.__search()
511 531
532 552
533 self.searchButton.setEnabled(False) 553 self.searchButton.setEnabled(False)
534 QApplication.setOverrideCursor(Qt.WaitCursor) 554 QApplication.setOverrideCursor(Qt.WaitCursor)
535 QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) 555 QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
536 556
537 self.__query = [term for term in self.searchEdit.text().strip().split() 557 self.__queryName = [
538 if term not in self.SearchStopwords] 558 term for term in self.searchEditName.text().strip().split()
559 if term not in self.SearchStopwords
560 ]
561 self.__querySummary = [
562 term for term in self.searchEditSummary.text().strip().split()
563 if term not in self.SearchStopwords
564 ]
539 self.__client.call( 565 self.__client.call(
540 "search", 566 "search",
541 ({"name": self.__query, "summary": self.__query}, "or"), 567 ({"name": self.__queryName,
568 "summary": self.__querySummary},
569 self.searchTermCombineComboBox.currentText()),
542 self.__processSearchResult, 570 self.__processSearchResult,
543 self.__searchError 571 self.__searchError
544 ) 572 )
545 573
546 def __processSearchResult(self, data): 574 def __processSearchResult(self, data):
614 QApplication.restoreOverrideCursor() 642 QApplication.restoreOverrideCursor()
615 643
616 self.__updateSearchActionButtons() 644 self.__updateSearchActionButtons()
617 self.__updateSearchButton() 645 self.__updateSearchButton()
618 646
619 self.searchEdit.setFocus(Qt.OtherFocusReason) 647 self.searchEditName.setFocus(Qt.OtherFocusReason)
620 648
621 def __searchError(self, errorCode, errorString): 649 def __searchError(self, errorCode, errorString):
622 """ 650 """
623 Private method handling a search error. 651 Private method handling a search error.
624 652
680 @type str 708 @type str
681 @return score value 709 @return score value
682 @rtype int 710 @rtype int
683 """ 711 """
684 score = 0 712 score = 0
685 for queryTerm in self.__query: 713 for queryTerm in self.__queryName:
686 if queryTerm.lower() in name.lower(): 714 if queryTerm.lower() in name.lower():
687 score += 4 715 score += 4
688 if queryTerm.lower() == name.lower(): 716 if queryTerm.lower() == name.lower():
689 score += 4 717 score += 4
690 718
719 for queryTerm in self.__querySummary:
691 if queryTerm.lower() in summary.lower(): 720 if queryTerm.lower() in summary.lower():
692 if QRegExp(r'\b{0}\b'.format(QRegExp.escape(queryTerm)), 721 if QRegExp(r'\b{0}\b'.format(QRegExp.escape(queryTerm)),
693 Qt.CaseInsensitive).indexIn(summary) != -1: 722 Qt.CaseInsensitive).indexIn(summary) != -1:
694 # word match gets even higher score 723 # word match gets even higher score
695 score += 2 724 score += 2
755 @type QTreeWidgetItem 784 @type QTreeWidgetItem
756 """ 785 """
757 self.showDetailsButton.setEnabled(False) 786 self.showDetailsButton.setEnabled(False)
758 QApplication.setOverrideCursor(Qt.WaitCursor) 787 QApplication.setOverrideCursor(Qt.WaitCursor)
759 QApplication.processEvents(QEventLoop.ExcludeUserInputEvents) 788 QApplication.processEvents(QEventLoop.ExcludeUserInputEvents)
760
761 self.__detailsData = {}
762 789
763 if not item: 790 if not item:
764 item = self.searchResultList.selectedItems()[0] 791 item = self.searchResultList.selectedItems()[0]
765 792
766 packageVersions = item.data(0, self.SearchVersionRole) 793 packageVersions = item.data(0, self.SearchVersionRole)
775 self.tr("Select the package version:"), 802 self.tr("Select the package version:"),
776 packageVersions, 803 packageVersions,
777 0, False) 804 0, False)
778 if not ok: 805 if not ok:
779 return 806 return
780
781 packageName = item.text(0) 807 packageName = item.text(0)
782 self.__client.call( 808 packageData = self.__pip.getPackageDetails(packageName, packageVersion)
783 "release_data", 809
784 (packageName, packageVersion), 810 QApplication.restoreOverrideCursor()
785 lambda d: self.__getPackageDownloadsData(packageName, 811 if packageData:
786 packageVersion, 812 from .PipPackageDetailsDialog import PipPackageDetailsDialog
787 d), 813
788 lambda c, s: self.__detailsError(packageName, c, s) 814 self.showDetailsButton.setEnabled(True)
789 ) 815
790 816 if self.__packageDetailsDialog is not None:
791 def __getPackageDownloadsData(self, packageName, packageVersion, data): 817 self.__packageDetailsDialog.close()
792 """ 818
793 Private method to store the details data and get downloads 819 self.__packageDetailsDialog = \
794 information. 820 PipPackageDetailsDialog(packageData, self)
795 821 self.__packageDetailsDialog.show()
796 @param packageName name of the package
797 @type str
798 @param packageVersion version info
799 @type str
800 @param data result data with package details in the first
801 element
802 @type tuple
803 """
804 if data and data[0]:
805 self.__detailsData = data[0]
806 self.__client.call(
807 "release_urls",
808 (packageName, packageVersion),
809 self.__displayPackageDetails,
810 lambda c, s: self.__detailsError(packageName, c, s)
811 )
812 else: 822 else:
813 QApplication.restoreOverrideCursor()
814 E5MessageBox.warning( 823 E5MessageBox.warning(
815 self, 824 self,
816 self.tr("Search PyPI"), 825 self.tr("Search PyPI"),
817 self.tr("""<p>No package details info for <b>{0}</b>""" 826 self.tr("""<p>No package details info for <b>{0}</b>"""
818 """ available.</p>""").format(packageName)) 827 """ available.</p>""").format(packageName))
819
820 def __displayPackageDetails(self, data):
821 """
822 Private method to display the returned package details.
823
824 @param data result data with downloads information in the first element
825 @type tuple
826 """
827 from .PipPackageDetailsDialog import PipPackageDetailsDialog
828
829 QApplication.restoreOverrideCursor()
830 self.showDetailsButton.setEnabled(True)
831
832 if self.__packageDetailsDialog is not None:
833 self.__packageDetailsDialog.close()
834
835 self.__packageDetailsDialog = \
836 PipPackageDetailsDialog(self.__detailsData, data[0], self)
837 self.__packageDetailsDialog.show()
838
839 def __detailsError(self, packageName, errorCode, errorString):
840 """
841 Private method handling a details error.
842
843 @param packageName name of the package
844 @type str
845 @param errorCode code of the error
846 @type int
847 @param errorString error message
848 @type str
849 """
850 QApplication.restoreOverrideCursor()
851 self.showDetailsButton.setEnabled(True)
852 E5MessageBox.warning(
853 self,
854 self.tr("Search PyPI"),
855 self.tr("""<p>Package details info for <b>{0}</b> could not be"""
856 """ retrieved.</p><p>Reason: {1}</p>""")
857 .format(packageName, errorString)
858 )
859 828
860 ####################################################################### 829 #######################################################################
861 ## Menu related methods below 830 ## Menu related methods below
862 ####################################################################### 831 #######################################################################
863 832
991 userSite=user) 960 userSite=user)
992 961
993 @pyqtSlot() 962 @pyqtSlot()
994 def __installRequirements(self): 963 def __installRequirements(self):
995 """ 964 """
996 965 Private slot to install packages as given in a requirements file.
997 """ 966 """
998 # TODO: call pip.installRequirements() 967 venvName = self.environmentsComboBox.currentText()
968 if venvName:
969 self.__pip.installRequirements(venvName)
999 970
1000 @pyqtSlot() 971 @pyqtSlot()
1001 def __uninstallRequirements(self): 972 def __uninstallRequirements(self):
1002 """ 973 """
1003 974 Private slot to uninstall packages as given in a requirements file.
1004 """ 975 """
1005 # TODO: call pip.uninstallRequirements() 976 venvName = self.environmentsComboBox.currentText()
977 if venvName:
978 self.__pip.uninstallRequirements(venvName)
1006 979
1007 @pyqtSlot() 980 @pyqtSlot()
1008 def __generateRequirements(self): 981 def __generateRequirements(self):
1009 """ 982 """
1010 Private slot to generate the contents for a requirements file. 983 Private slot to generate the contents for a requirements file.
1011 """ 984 """
1012 # TODO: modify to get selected environment 985 venvName = self.environmentsComboBox.currentText()
1013 from .PipFreezeDialog import PipFreezeDialog 986 if venvName:
1014 self.__freezeDialog = PipFreezeDialog(self) 987 from .PipFreezeDialog import PipFreezeDialog
1015 self.__freezeDialog.show() 988 self.__freezeDialog = PipFreezeDialog(self.__pip, self)
1016 self.__freezeDialog.start() 989 self.__freezeDialog.show()
990 self.__freezeDialog.start(venvName)
1017 991
1018 @pyqtSlot() 992 @pyqtSlot()
1019 def __editUserConfiguration(self): 993 def __editUserConfiguration(self):
1020 """ 994 """
1021 Private slot to edit the user configuration. 995 Private slot to edit the user configuration.
1023 self.__editConfiguration() 997 self.__editConfiguration()
1024 998
1025 @pyqtSlot() 999 @pyqtSlot()
1026 def __editVirtualenvConfiguration(self): 1000 def __editVirtualenvConfiguration(self):
1027 """ 1001 """
1028 Private slot to edit the current virtualenv configuration. 1002 Private slot to edit the configuration of the selected environment.
1029 """ 1003 """
1030 # TODO: modify to get selected environment 1004 venvName = self.environmentsComboBox.currentText()
1031 self.__editConfiguration(virtualenv=True) 1005 if venvName:
1032 1006 self.__editConfiguration(venvName=venvName)
1033 def __editConfiguration(self, virtualenv=False): 1007
1008 def __editConfiguration(self, venvName=""):
1034 """ 1009 """
1035 Private method to edit a configuration. 1010 Private method to edit a configuration.
1036 1011
1037 @param virtualenv flag indicating to edit the current virtualenv 1012 @param venvName name of the environment to act upon
1038 configuration file 1013 @type str
1039 @type bool 1014 """
1040 """
1041 # TODO: modify to use venvName
1042 from QScintilla.MiniEditor import MiniEditor 1015 from QScintilla.MiniEditor import MiniEditor
1043 if virtualenv: 1016 if venvName:
1044 cfgFile = self.__getVirtualenvConfig() 1017 cfgFile = self.__pip.getVirtualenvConfig(venvName)
1045 if not cfgFile: 1018 if not cfgFile:
1046 return 1019 return
1047 else: 1020 else:
1048 cfgFile = self.__getUserConfig() 1021 cfgFile = self.__pip.getUserConfig()
1049 cfgDir = os.path.dirname(cfgFile) 1022 cfgDir = os.path.dirname(cfgFile)
1050 if not cfgDir: 1023 if not cfgDir:
1051 E5MessageBox.critical( 1024 E5MessageBox.critical(
1052 None, 1025 None,
1053 self.tr("Edit Configuration"), 1026 self.tr("Edit Configuration"),
1054 self.tr("""No valid configuration path determined.""" 1027 self.tr("""No valid configuration path determined."""
1055 """ Is a virtual environment selected? Aborting""")) 1028 """ Aborting"""))
1056 return 1029 return
1057 1030
1058 try: 1031 try:
1059 if not os.path.isdir(cfgDir): 1032 if not os.path.isdir(cfgDir):
1060 os.makedirs(cfgDir) 1033 os.makedirs(cfgDir)
1061 except OSError: 1034 except OSError:
1062 E5MessageBox.critical( 1035 E5MessageBox.critical(
1063 None, 1036 None,
1064 self.tr("Edit Configuration"), 1037 self.tr("Edit Configuration"),
1065 self.tr("""No valid configuration path determined.""" 1038 self.tr("""No valid configuration path determined."""
1066 """ Is a virtual environment selected? Aborting""")) 1039 """ Aborting"""))
1067 return 1040 return
1068 1041
1069 if not os.path.exists(cfgFile): 1042 if not os.path.exists(cfgFile):
1070 try: 1043 try:
1071 f = open(cfgFile, "w") 1044 f = open(cfgFile, "w")
1079 if not os.access(cfgFile, os.W_OK): 1052 if not os.access(cfgFile, os.W_OK):
1080 E5MessageBox.critical( 1053 E5MessageBox.critical(
1081 None, 1054 None,
1082 self.tr("Edit Configuration"), 1055 self.tr("Edit Configuration"),
1083 self.tr("""No valid configuartion path determined.""" 1056 self.tr("""No valid configuartion path determined."""
1084 """ Is a virtual environment selected? Aborting""")) 1057 """ Aborting"""))
1085 return 1058 return
1086 1059
1087 self.__editor = MiniEditor(cfgFile, "Properties") 1060 self.__editor = MiniEditor(cfgFile, "Properties")
1088 self.__editor.show() 1061 self.__editor.show()
1089 1062

eric ide

mercurial